This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2022-10-27
Channels
- # announcements (10)
- # beginners (95)
- # biff (2)
- # calva (33)
- # cherry (1)
- # clj-kondo (16)
- # clojure (96)
- # clojure-australia (1)
- # clojure-china (1)
- # clojure-europe (42)
- # clojure-filipino (1)
- # clojure-france (2)
- # clojure-hk (1)
- # clojure-indonesia (1)
- # clojure-japan (1)
- # clojure-korea (1)
- # clojure-my (1)
- # clojure-nl (1)
- # clojure-norway (24)
- # clojure-sg (11)
- # clojure-taiwan (1)
- # clojure-uk (1)
- # clojurescript (21)
- # cursive (22)
- # data-science (3)
- # events (7)
- # fulcro (3)
- # graalvm (4)
- # gratitude (6)
- # helix (13)
- # honeysql (7)
- # hoplon (1)
- # introduce-yourself (1)
- # jobs (2)
- # jobs-discuss (16)
- # lsp (15)
- # malli (14)
- # nbb (73)
- # practicalli (3)
- # reagent (8)
- # reitit (5)
- # releases (1)
- # ring (5)
- # rum (3)
- # sci (17)
- # scittle (7)
- # shadow-cljs (22)
- # tools-deps (26)
- # xtdb (9)
Hi all, Im having an issue with Stuart Sierra's component framework. Basically I have 2 components where one depends on the other. The first component makes an http call, gets a value, then stores it into an atom. The second component then uses that value stored in the atom to perform a task. In the start and stop methods for both components I am returning 'this' because I am not modifying the components. The issue I'm having is that after the task is performed the application seems to try to start up again, resulting into erroring out because the address is already in use. I believe I'm doing something wrong with the components. Has anyone faced this issue and how did you solve it?
Components don't do anything unless you call functions on them, there is no magic in there. You should take another look at your code, if you get an exception the stacktrace will tell you what code is on the stack and if component's start function is on the stack, it will give you a file and line number where it is called
My guess though, since your start does nothing is that your stop does nothing, so the http server from the first time you ran start is just hanging around using the port, and the "starting again" errors are actually just the errors starting
I’m doing a very simple test in which I’m requiring a .gitlib
style library:
com.grzm/awyeah {:git/url "" :git/sha "1810bf624da2be58c77813106a1d51e32db11690" :git/tag "v0.8.35"}
My “app” right now is simply:
(ns core (:gen-class))
(require '[com.grzm.awyeah.client.api :as aws]
'[com.grzm.awyeah.credentials :as credentials])
(defn main [opts]
(println "Hello world!"))
However, when I try to execute it with clj -X core/main
I get an error about clojure/tools/logging and I am not sure why:
Execution error (FileNotFoundException) at com.grzm.awyeah.client.api/eval228$loading (api.clj:4).
Could not locate clojure/tools/logging__init.class, clojure/tools/logging.clj or clojure/tools/logging.cljc on classpath.
Can anyone help point me in the right direction?Is there a “standard set” that normally get delcared?
Just deps in aliases, but for transitive deps tools-deps only looks at top level deps
Ok. I see when I added tools.logging to my edn, it gets past that error and now has an error for async. If I want to continue this, is the best thing just go through the errors and add them - or is there a way to know what all should be added?
Ok I check that out. Thanks sir - this should be enough of a hint to get me going.
Yep - you are exactly right, thanks!
You awyeah is intended to be a drop in replacement for another library in babaska scripts, if you are not using babaska scripts you can just use the aws-api library
I actually am using babashka, but what I am trying to achieve is: I’d like to create few “helper” libs for my AWS work. These libs I intend to use from babashka (just to keep my babashka scripts more simple / cleaner).
But in order for my libs to work in babashka, I can’t use the main cognitect api lib in them.
Sure, for whatever reason the author of the lib hasn't run into issues with his deps.edn, likely because they never use it as a library outside of babaska
yeah makes sense. I like the tool, it’s a great solution fo smaller scripting. Just trying to learn all this stuff as I’m fairly new to clojure 🙂
Just dropping a hint here for anyone that searches this at a later date: I got my lib fully working now in babashka, the final missing piece was getting the .gitlibs dep into the classpath. For that, I used Sean Corfield’s build-clj
(https://github.com/seancorfield/build-clj) with the :transitive
option.
I’ve got some code defined in an edn file: :basis (b/create-basis {:project "deps.edn"})
In my function I want to evaluate that code. So I’m trying: ((:basis opts))
(opts is that map).
I’m getting: class clojure.lang.PersistentList cannot be cast to class clojure.lang.IFn
I’m sure I’m missing something simple here?
If you have code in EDN, you will need to eval
it after reading it -- otherwise it is a literal list of symbols etc.
What you have above is like {:basis (list 'b/create-basis {:project "deps.edn"})}
So (:basis opts)
is a PersistentList
. And ((:basic opts))
is like ((list 'b/create-basis {:project "deps.edn"}))
so you're trying "call" a list of data.
Does that help @U0250GGJGAE?
Ahh I see -ok. I just tried with eval Yes! That works. Thank you very much, really appreciate it!
Actually lol - what I’m doing is directly working with your library - build-clj lol. Such a small world, thank you for the pointer 🙂
Things that seem to require eval
should be viewed as a "design smell". I wouldn't put code in the EDN, I would only put data, with enough annotations to be able to apply code to that data after it is read in.
So perhaps :basis-arg {:project "deps.edn"}
and then after reading that in: (b/create-basis (:basis-arg opts))
in your code.
You can put a fully-qualified symbol in EDN and then, after reading it in call requiring-resolve
on it to get it loaded and get its value.
Aaaaaah ok. You’re reading my mind. That’s exactly what I’m trying to do. I’m looking at:
“if you want your user deps.edn
included, you will need to explicitly pass :basis (b/create-basis {:user :standard})
into tasks,” (from your github)
and I was trying to figure the best way to do that (again new to clojure).
Ok - these suggestions make perfect sense, I will give that a go!
So {:basis-fn clojure.tools.build/create-basis :basis-arg {:project "deps.edn"}}
and then do ((requiring-resolve (:basis-fn opts)) (:basis-arg opts))
but at that point I might question your sanity 🙂
ha ha ha - ok. I’m trying to work out if I’m better off having a “generic” build.clj and putting everything specific in deps, or if I should have tailored build.clj s’ per-project.
Yeah, that means: you need a task to call (b/create-basis {:project "deps.edn"})
and add that to hash map of data under the :basis
key that it then passes to one of the build-clj
tasks.
You only want deps (and maybe some pure data) in deps.edn
-- not code.
I was also doing this to keep config values:
Thoughts on that?
(notwithstanding the code still in there)
Please post code/data, not images.
Images are not accessible to all people.
:exec-args {:transitive true
:lib 'com.beakstar/aws
:version "0.1.0"
:basis (b/create-basis {:project "deps.edn"})
:target "target"}}}}
Yeah, you can't have code in :exec-args
. Just data.
I was keeping :exec-args
in deps to store things like versions lib name, …etc.
Do you think that will get me into trouble, or that ok?
That's fine -- we do that at work.
Ok - thanks, I really appreciate your time. This was all very helpful. I think I’m on a better track now.
We use a standard alias in each project deps.edn
file like this:
:uberjar
{:uber-file "../../../build/uberjars/api-1.0.0.jar"
:main ws.api.main}
and then we build the basis for the project and have code to pull the alias as data from that...
Ok that makes sense. There are so many options, trying to get me head around things.
(defn- get-project-aliases []
(let [edn-fn (juxt :root-edn :project-edn)]
(-> (t/find-edn-maps)
(edn-fn)
(t/merge-edns)
:aliases)))
Ah, so we just use t.d.a. to read the EDN files and pull the aliases from them. Not the basis. But we use the basis "as usual" for a given project.Here's part of a task that walks over multiple projects in our monorepo and builds uberjars for each one:
(doseq [p projects]
(println "\nRunning: uber on" p)
(let [project-dir (str "projects/" p)
aliases (with-dir (io/file project-dir) (get-project-aliases))]
(binding [b/*project-root* project-dir]
(bb/clean {})
(bb/uber (assoc (:uberjar aliases)
:compile-opts {:direct-linking true}))
(bb/clean {}))))
Oh nice, thank you. I’m not using a mono-repo, but I went to build-clj because I needed to package some .gitlib repos and through searching this slack, I saw your message about the :transitive
option, which solved a big problem for me.
The :exec-arg
stuff only makes sense if you are invoking via -X
from the command-line.
But you can use arbitrary aliases as pure data, and use t.d.a to read/merge and extract :aliases
and have access to all of that.
What is t.d.a? (sorry)
tools deps alpha?
Yes (sorry). I tend to use it for a lot of low-level stuff.
ok thanks. Ok - I was invoking via -X, but only because I was testing / trying to figure it all out. But I didn’t realize I could use aliases as pure data. That would be even better. I’m going to give that a try as well.
If you have a basis, you can use t/combine-aliases
to apply those aliases to it. If you have a deps.edn
file (as a java.io.File
), you can use t/slurp-deps
to read it according to all the built-in rules to produce data as a shortcut. etc.
Feel free to ping me with any further Qs you have about build-clj
via DM if you want. It's lucky that I happened to notice the Q in #C053AK3F9 because it's fairly high-traffic. There's #C02B5GHQWP4 for build.clj
/ tools.build
Qs (i.e., the stuff below my wrapper library).
Thanks sir, again - really appreciate you spending a little time with me here. I helps to understand my options and some best practices as I’m getting spun up with clojure and the ecosystem.
is there any difference between (vector a b c)
and [a b c]
they cannot always be swapped for each other:
user=> (let [x 1] x)
1
user=> (let (vector x 1) x)
Could not resolve symbol: x [at <repl>]
Not sure if this is a good answer or not though.when do you need (vec)
?
Vec is different! It takes a single argument - a collection - and turns it into a vector. Vec let's you turn a list into a vector, a map into a vector, you name it :) More on vec: https://clojuredocs.org/clojure.core/vec
Thanks
You’ll often encounter “constructors” like vector
, list
and hash-map
in function literals:
(map #(vector % (inc %)) (range 10)) ; works
(map #([% (inc %)]) (range 10)) ; ERROR
Maybe @UK0810AQ2 meant that (list 1 2 3)
and (1 2 3)
is not the same, as list literals are evaluated as function calls and list
has implicit quoting, like '(1 2 3)
Hi guys, I start learning and I wonder what have I done wrong here. calling greeting without parameter returns only hello -_^
(defn greeting
([] greeting "hello" "world")
([x] greeting "hello" x)
([x, y] str x "," y)
)
(assert (= "Hello, World!" (greeting)))
(assert (= "Hello, Clojure!" (greeting "Clojure")))
(assert (= "Good morning, Clojure!" (greeting "Good morning" "Clojure")))
the forms in the function body are not function calls, needs to be (greeting ,,,)
not greeting ,,,
The syntax for lists: function application, macro application, special form OR function arity implementation (only in function body)
The syntax is very regular. Everything you want to happen, you put in a list (not a vector). The operator is always the first argument of the list
Examples of bindings:
Function parameters: (defn foo [a b c] ,,,)
Let bindings: (let [a 1 b 2 c 3] ,,,)
with-open (with-open [w (io/writer (io/file "outfile"))] ,,,)
with-rederfs (with-redefs [qualified/symbol (fn [x ,,, z] ,,,)] ,,,)
for comprehension (it's not a loop) (for [x xs y ys] ,,,)
loop (is a loop) (loop [a 1] (recur (inc a)))
doseq (doseq [x xs] (println x))
this example may help with what your trying to achieve
(defn greeting
([]
(greeting "Hello" "world"))
([x]
(greeting "Hello" x))
([x y]
(str x ", " y)))
https://archive.clojure.org/design-wiki/display/design/Function%2Boverloading.htmlIn every example of binding form besides function parameters, bindings are pairs of left-hand-side name and right-hand-side expression The RHS expressions are evaluated sequentially, so a subsequent RHS expression can use the bound name from a previous LHS binding form
i'd like to use criterium library to benchmark database calls. however, i'm worried that criterium will send a billion calls. is there an option i can pass to limit the number of executions? or the overall load?
thank you
that makes sense to me
You're better off using a trace library for this; safely
and mulog
are both really good (the former builds on the latter). You can write the logs to an EDN file or cloud log publisher for easier analysis after the fact while your app runs and get observability into real-world performance. For controlled testing, throttler
is a good library that lets you rate limit functions in a very intuitive and low overhead way.