This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2021-05-18
Channels
- # announcements (6)
- # babashka (137)
- # beginners (115)
- # biff (4)
- # calva (24)
- # cider (1)
- # clj-kondo (15)
- # cljs-dev (18)
- # cljsrn (1)
- # clojure (43)
- # clojure-australia (1)
- # clojure-europe (11)
- # clojure-germany (3)
- # clojure-nl (3)
- # clojure-spec (40)
- # clojure-uk (6)
- # clojured (1)
- # clojurescript (24)
- # code-reviews (14)
- # core-logic (5)
- # cursive (24)
- # datahike (2)
- # datomic (3)
- # deps-new (9)
- # events (5)
- # figwheel-main (1)
- # fulcro (7)
- # helix (3)
- # honeysql (8)
- # jobs (4)
- # jobs-discuss (7)
- # lsp (59)
- # malli (9)
- # mid-cities-meetup (1)
- # nrepl (2)
- # off-topic (27)
- # portal (11)
- # re-frame (16)
- # reitit (1)
- # releases (12)
- # remote-jobs (4)
- # shadow-cljs (48)
- # spacemacs (1)
- # vim (9)
- # xtdb (14)
anyone know of any good documentation on how deps.edn works?
i don't mind reading the code either
but not sure where it lives
@brandon.n.atkinson there's a link in the above, but https://clojure.org/reference/dep_expansion for more gory details
Any options on using a var binding as a require arg, instead of a literal?
do you have an example of what you're trying to do?
(def config {:namespace "clojure.string"})
(let [n (:namespace config)]
(when (n) (:require [n :as module])))
n is a var, require wants a list/vec of literals
not sure what you're actually trying to do, but requiring-resolve
might help
fwiw, the following works:
(let [config {:namespace "clojure.string"}
n (:namespace config)]
(when n (require [(symbol n) :as 'module])))
(module/lower-case "CLOJURE")
but you probably want something like requiring-resolve
thanks for the feedback
im looking for behavior parity between an existing python code base and what can be accomplished here. The python project leverages importlib and dynamic loading in a fashion somewhat similar to java spring ioc, interfaces, and varying implementations.
repl testing looks as if requiring-resolve can provide at least the edn config ->conditional import path here.
like this?
(let [str 'clojure.string]
(require str))
Is there a preferred way to add support for multiple languages? For example, use maps like {:en "Hello, World", :de "Hallo Welt!", etc...}
instead of strings and then let the display function choose depending on chosen language?
@U020G9UCM1N In case you're not familiar with this domain, you can find a plethora of stuff to read about searching for internationalization
and localization
(abbreviated as i18n
and l10n
). There is a lot of nuance to doing this well at scale. But if you're just looking for something quick to get you started grab an existing i18n library (e.g. https://github.com/ptaoussanis/tempura) and try not to go down the rabbit hole of learning everything about this - the well runs very deep. :)
That looks nice, thanks!
small question about this and keywords in general: if I nest the IDs, like :example {:foo {:bar "foobar"}}
in the dict for tr, according to the docs, I should refer it using :example.foo/bar
- my first thought would have been :example/foo/bar
and it works, as does :example.foo.bar
, but those two are illegal keywords according to https://clojure.org/reference/reader - is there a way to transform a keyword by adding prefix/suffix? like in this example, how can i get from :foo
to the :example.foo/bar
?
there has to be a better and safer way than (keyword (str "example." (name :foo)) "bar")
(with :foo coming from a parameter). to give more context, I want to put all item names for my game into that dictionary, nested under :item
- so for example :item {:sword {:name "Sword", :description "A Sword with a sharp blade."}}
and when outputting the name of the item, I would have :sword
and want to call tr with :item.sword/name
- is this a valid approach or should I rethink it?
Usually, keyword namespaces are not meant to be programmatically manipulated and interpreted in "parts". This is a subject that comes up once in a while e.g. in clojure.spec discussions, but my understanding is that this is not something the Clojure Core team feels is a good practice, so you won't find a lot of clojure.core functions to manipulate the namespace as a hierarchy of segments.
To not go against the grain, you can ask yourself if your request should be asking for the name of item :sword
or simply should be asking for the :item.sword/name
? The latter would make it unambiguous and no need for the monkeying around with strings; the former and writing some helper function to hide (keyword (str/join "." ["item" (name item)]) "name")
is probably as good as it gets.
Thanks again! I think I'll avoid nesting too deep, then. Maybe even use a separate dictionary for each category, like one for item names, one for item descriptions. Then I can use the keyword for the item as resource id (in this example, :sword
).
My gut reaction would be to have :item/sword
or even :item.weapon/sword
wherever you're dealing with the thing and then use the keyword/string logic to map all translations (so you automatically map :item/sword
to {:item.sword/name ".." :item.sword/description ".."}
)
Having unambiguous full-qualified keywords as identifiers means you can mix-and-match maps easier and pass things around without nesting for context. It seems like a small difference, but it has some nice long-term benefits that are not easy to articulate until you start growing bigger systems with it.
Check out https://www.youtube.com/watch?v=IS3i3DTUnAI if you want to learn more about the perks of fully-qualified keywords
{:item/id :magic-sword-of-parens ;; or more unique ala :
:item/kind :item.kind/weapon
:item/weapon :item.weapon/sword
:item/name "this is dynamically generated based on translation, but is generic key for UI"
:item/description "same here"}
^ Perhaps too much ceremony for you, but notice that all those keywords can be used in different parts of the codebase without any additional context and data - and you know exactly what they are.Any suggestions as to why (
is returning nil
even though the config.edn
file is on the classpath, as shown by lein classpath
command?
Its an older Clojure project with Leiningen. I tried using Java 8 and Java 11, a few different versions of Leiningen.
I can slurp the file...
I load a few files using io/resource, they all are in the resources folder
I can use io/resources in a newly created project. There is something strange in this legacy project that is messing with the class path I think.
try lein with-profile +repl cp
to get the classpath a repl would use
you can swap in other profiles of course
also for io/resource
to work, the document shouldn't be on classpath, its parent folder or zip file / other container should be
nb. "." shouldn't be on classpath, you should have specific subdirectories of the project like src
and resources
that are on classpath
Is there a way to see the classpath from the REPL ??
What I use most often is cider-open-classpath-entry
There's also a system property for this:
(System/getProperty "java.class.path")
Hmm, this just shows the leiningen jar as the class path. very strange.
It works fine for me. I’ve been using it for a long time from jdk 8 to jdk 16. Note that your code fails completely on modern JDKs
Yes, the code does work correctly in a new project. It gives the leinigen path in the older project, I think because something is failing... Thanks.
I have tried some variations around this example, https://gist.github.com/ekoontz/1502838 but I get nil returned. I though the code wasnt working, but perhaps Leiningen is not correctly setting the classpath for the repl, which would explain the issue
Curious that this code returns the leiningen jar as the class path, rather than the really long classpath I get when using lein classpath
command
(doseq [url (seq (.getURLs (java.lang.ClassLoader/getSystemClassLoader)))]
(println (.getFile url)))
Definitely something in the Leiningen project messing up the classpath when running the repl either on the terminal or via the editor. I think Leiningen has been silently failing to build the classpath. Also it seems Leinigen was not downloading all dependencies.
if it was "failing to build a class path" java wouldn't even be able to run anything. what is happening is you are asking for a different classpath than you think you are.
> it seems Leinigen was not downloading all dependencies.
that makes me think that something replaced the classpath / dep vector where it should have been adding to it (look for the ^:replace
metadata in a profile for example)
The project does something intricate with project.clj and profiles.clj (which is also in the root of the project). I commented out all the bits I didnt understand and at least I have a proper class path now. I also decided to add a deps.edn file and use Clojure CLI tools. This showed some issues with transitive dependencies which I've now resolved.
$ lein deps :tree
also helps diagnose transative dep problems, and $ lein help sample
has example based docs of most of lein's weird features
I found an issue with a transitive dependency of a transitive dependency. There were also issues with a user.clj file, which was automatically loaded on startup by Leiningen. The repl runs now, thanks all.
leiningen does not autoload user.clj, but clojure does (if it's seen on classpath) - perhaps the difference was lein putting the file on classpath
a transitive dep of a transitive dep is a transitive dep (by the transitive property :D)
(defn my-fn [{:keys [unparam1 unparam2]
:param/keys [param1 param2 param3]
param-type :param/type
:as params}]
...)
I have a function that looks like the above. Is there a more intuitive way (the change of the order throws me off quite a bit) to rebind ::param/type
to param-type
? The goal is to avoid having to use the name type
to refer to this parameter within the scope of the function.
I think it would be nice to be able to say something like {::param/keys [param1 [type :as param-type]]}
Hey everyone, is there a concept of a spread operator in Clojure? I want to merge a map with a sequence of maps, but I want to 'spread' out that sequence.
Hi, I was wondering if there was any way of making some simple dfs traversal work with recur? I understood that recur uses tail optimisation which is not otherwise possible given the JVM. Now , if I simply replace my recursive call to dfs, I get an error 'Can only recur from tail position'. Is there a clojurian way of making this sort of code work with tail optimisation? Thank you very much for your help.
(*fn* dfs [tree traversed]
(*let* [node (first tree)
c1 (second tree)
c2 (nth tree 2)]
(*if* (nil? tree)
(conj traversed tree)
(concat [node] (dfs c1 traversed) (dfs c2 traversed)))))
in order to tail recur over a tree you need to lift the rest of the tree traversal into a data structure, which leads to harder to read code. except when you have extreme efficiency needs, the right thing to do is just consume stack proportional to the tree size
I think typical default max JVM stack depth is on the order of a few thousand calls (it can be configured larger when you start the JVM via command line options). If you expect the DFS to traverse more nodes than that at maximum depth, then you should consider whether you might want to use a method that manages the DFS control flow via a data structure, vs. consuming call stack.
Thank you folks
by simple non-optimized self call
also your lazy structure via putting the self call inside concat already prevents stack usage explosion I think?
hi team, I am facing this issue during deployment after changing java10 to 11 version. I also added JAXB library but the issue is still there. any suggestion? can this be related to the leiningen version as well?
you are building an uberjar, and if you find the file named module-info.java
in the uberjar and remove it, then maybe that will fix things
you can maybe use this https://github.com/technomancy/leiningen/blob/master/sample.project.clj#L422 to do it automatically
not 100%, but my guess is some dependency you are pulling in includes some java module stuff, and that is all be blindly shoveled into the uberjar by lein, so then java now thinks your uberjar is a java module thing, which makes things weird
For anyone who’s perused https://www.manning.com/books/the-joy-of-clojure-second-edition?gclid=CjwKCAjwy42FBhB2EiwAJY0yQlgELvVtu7ErEKP-G0m9lqeWm4wS0BInny8yz1tHUxVM4gALeSBVohoCXVIQAvD_BwE, I was re-reading a section this morning and hit a point of confusion.
In 8.1.1, there’s a quick review of syntax-quote, unquote, and splicing, with the example function contextual-eval
:
(defn contextual-eval [ctx expr]
(eval
`(let [~@(mapcat (fn [[k v]] [k `'~v]) ctx)]
~expr)))
The author then writes:
> The bindings created use the interesting
'~s` pattern to garner the value of the built bindings at runtime.
I don’t understand why
'~s` is necessary/what problem it solves here. It seems redundant to me. When I attempted to write my own contextual-eval
, I ended up with:
(defn contextual-eval [ctx exp]
(eval `(let [~@(mapcat (fn [[k v]] [k v]) ctx)]
~exp)))
And it looks like the examples from the book evaluate to the same thing using my version.
(contextual-eval '{a 1 b 2} '(+ a b)) ; 3
(contextual-eval '{a 1 b 2} '(let [b 1000] (+ a b))) ; 1001
I'm not sure I would write it the same as their example, but there's a difference in behavior. Eg:
(defn contextual-eval [ctx expr]
(eval
`(let [~@(mapcat (fn [[k v]] [k `'~v]) ctx)]
~expr)))
(defn contextual-eval-mead [ctx exp]
(eval `(let [~@(mapcat (fn [[k v]] [k v]) ctx)]
~exp)))
(contextual-eval '{a (+ 0 1) b 2} '(+ a b)) ;; exception
(contextual-eval-mead '{a (+ 0 1) b 2} '(+ a b)) ;; 3
Notice that they quote the value in their implementationI haven't read the book and maybe it makes sense for their example, but calling eval
in a macro seems super weird to me
Oh it’s not a macro yet — just a regular fn being used to introduce the concept of macros
ohhh, right. duh. nvmd
Thanks for your response earlier, walking through it now
it's a little easier to see what's going on if you just show the code being passed to eval:
(defn contextual-eval-form [ctx expr]
(let [form `(let [~@(mapcat (fn [[k v]] [k `'~v]) ctx)]
~expr)]
form))
(defn contextual-eval-mead-form [ctx exp]
(let [form `(let [~@(mapcat (fn [[k v]] [k v]) ctx)]
~exp)]
form))
(contextual-eval-form '{a (+ 0 1) b 2} '(+ a b))
;; (clojure.core/let [a '(+ 0 1) b '2] (+ a b))
(contextual-eval-mead-form '{a (+ 0 1) b 2} '(+ a b))
;; (clojure.core/let [a (+ 0 1) b 2] (+ a b))
Their's has a '
before (+ 0 1)
It makes sense why the book version breaks, but I’m still confused as to why it might be the preferred/idiomatic version vs. the simpler one. Trying to think of an example where contextual-eval-mead
would be worse…
well, for the purposes of introducing macros, their version is closer to how a macro works
ah ok. maybe that’s the use of having it in there then.
thanks
I recently came across a use of this in the malli codebase: https://github.com/metosin/malli/blob/master/src/malli/core.cljc#L1969
I usually would write that as:
(list 'quote (symbol (str name)))
it's a little more verbose, but it's easier for my brain to followthe '~` idiom is a neat trick though. Might be worth getting used to.
Why did the book use '
` instead of just ` ? Their two examples give the same results. I don't understand.
A less opaque version of what the book is doing would be to replace `'~v with (list 'quote v)
In that particular case, I would never write it like that, but using '~ to embed quoted values in a syntax quote form is very useful
Thanks. I think now I understand what's going on in this code even if I don't understand what they're trying to get across. I'll keep reading.
I'm working my way through The Reasoned Schemer using core.logic
and I'm having some trouble with the nevero
goal defined in Chapter 6 frame 14:
(defrel (nevero)
(nevero))
This should be a goal that never succeeds and never fails. My naïve translation to Clojure is
(defn nevero [] (nevero))
According to the book, Chap. 6 frame 17
(run 1 q
#u
(nevero))
should produce (), as #u
fails before nevero
is encountered, but
(run 1 [q] u# (nevero))
does not terminate for me (well, it does when it blows the stack). Does core.logic
behave differently here or have I goofed something up?Hello! I'm 9 days into learning Clojure. How do you all approach solving problems?
For an in-depth look at problem solving techniques, G. Polya's "How to Solve It" is great, http://www.math.utah.edu/~pa/math/polya.html
The overview is a good starting point: 1. Understanding the problem 2. Devising a plan 3. Carrying out the plan 4. Looking back (eg. testing)
small question about this and keywords in general: if I nest the IDs, like :example {:foo {:bar "foobar"}}
in the dict for tr, according to the docs, I should refer it using :example.foo/bar
- my first thought would have been :example/foo/bar
and it works, as does :example.foo.bar
, but those two are illegal keywords according to https://clojure.org/reference/reader - is there a way to transform a keyword by adding prefix/suffix? like in this example, how can i get from :foo
to the :example.foo/bar
?
I have two files/namespaces that alias two different namespaces with the same symbol and I’m getting an alias conflict error. Why can’t I use the same-symbol (`:as some-symbol` ) when referring to two different namespaces in two different files?
// one.clj
(ns com.app.one
(:require [com.app.models.one :as model]))
// two.clj
(ns com.app.two
(:require [com.app.models.two :as model]))
looks ok to me. Maybe it's some other issue. What's the error you're getting? What's the symbol alias you're trying to use?
1. Caused by java.lang.IllegalStateException
Alias model already exists in namespace
neuromancer.core.pay.invoice, aliasing
neuromancer.models.pay.account
I’m using :as model
in both *.pay.invoice
and *.pay.account
is there another require in the same namespace using model
?
what do you mean by compile?
ah, I'm guessing there's some persistent state which won't let you change it
you can either restart your repl or run
(ns-unalias *ns* 'model)
for each namespace before trying again
there's probably a better way to reload each namespace without restarting the repl, but my repl workflow is fairly basic
Yeah there’s something weird going on with my repl. I restarted it, and it stopped giving me that error, but now It’s giving some other error that also looks like a caching problem
progress!
Ah nah figured it out, now everything’s compiling! just needed to restart the repl, thank you 🙂
Most likely you have evaled one in a namespace already and so now there was already an alias in that namespace.