clojure

New to Clojure? Try the #beginners channel. Official docs: https://clojure.org/ Searchable message archives: https://clojurians-log.clojureverse.org/
alexmiller 2020-10-15T00:25:58.486800Z

transit is designed for wires and should definitely be preferred for that use case

alexmiller 2020-10-15T00:27:34.487Z

in particular, it's designed to work over json and many languages (critically javascript) have very fast native json parsers. Also, it has caching built in for repeated keys etc so will generally be significantly smaller for common use cases (passing many maps with the same keys) and thus also faster.

alexmiller 2020-10-15T00:29:50.487500Z

edn is designed for places humans read and write data (like config files). transit is also designed to be more extensible in efficient ways - edn has tagged literals but they're not quite as good for crossing machine/language boundaries.

donyorm 2020-10-15T00:29:53.487700Z

Wow thanks, sorry I somehow missed that.

p-himik 2020-10-15T00:54:37.488900Z

Ah, it was the point on tagged literals that I was thinking of. Thanks!

deadghost 2020-10-15T01:05:25.489100Z

https://clojuredocs.org/clojure.spec.alpha/gen > Note that parent generator (in the spec or overrides map) will supersede those of any subtrees. Is there a better way to have a custom generator in a subtree than doing nested gen overrides?

;; Example

;; We love wasteful packaging so we want to create a ::crate that will always
;; contain a ::big-box. We want each ::big-box to contain one or more
;; ::small-box. In each small box, we want to have one Light Bulb.

(s/def ::crate (s/keys :req-un [::big-box]))
(s/def ::big-box (s/coll-of ::small-box :kind vector?))
(s/def ::small-box (s/coll-of string? :kind vector?))

(let [gen-override
      {::big-box
       ;; Override so ::big-box is not empty
       #(gen/not-empty
         (s/gen ::big-box
                ;; And that each ::small-box in ::big-box only has a Light Bulb
                {::small-box (fn [] (s/gen (s/coll-of #{"Light Bulb"}
                                                      :kind vector?
                                                      :count 1)))}))}]
  ;; Generate a ::crate with our nested overrides
  (gen/generate (s/gen ::crate gen-override)))

;; =>
{:big-box
 [["Light Bulb"]
  ["Light Bulb"]
  ["Light Bulb"]
  ["Light Bulb"]
  ["Light Bulb"]
  ["Light Bulb"]
  ["Light Bulb"]
  ["Light Bulb"]]}

Tom Helmuth 2020-10-15T01:34:37.489900Z

Not sure if this is the right place to ask this, but here's an oddity that greatly confuses me.

Tom Helmuth 2020-10-15T01:34:59.490500Z

I understand what is going on here:

> ('foo {'bar 5 'foo 3})
3

Tom Helmuth 2020-10-15T01:35:59.491200Z

But what is up with this behavior:

> ('* 3 2)
2

Tom Helmuth 2020-10-15T01:36:43.491800Z

Is it trying to look up the symbol '* in the "map-like thing" 3, and when it doesn't find it, returns the default value of 2?

2020-10-15T16:44:54.014200Z

@seancorfield Perhaps the frequently asked question might be "Why do several ways of looking up a key in an associative collection return nil when given something that isn't an associative collection?" starting with examples of both the get and symbol-as-first-form-in-parens expressions?

2020-10-15T16:46:04.014400Z

Granted that 'get' and the "symbol lookup as first form within parens" don't have good strings I can think of that people would be Google-searching for when they have this question, but folks like us who know what FAQs exist would know it was there on that page somewhere.

2020-10-15T16:47:21.014600Z

I wouldn't be surprised if Rich Hickey himself may have addressed the "why is it designed this way?" question around 2009-2010 in the Clojure Google group, but I didn't find such a thing in 10 mins of searching, again given the difficulty of finding the right search terms.

2020-10-15T16:53:56.014800Z

This old thread doesn't answer the question, but Chouser gives his quick rules of thumb for choosing between (key-or-symbol some-map) versus (some-map maybe-a-key) versus (get some-map maybe-a-key) alternatives. https://groups.google.com/g/clojure/c/Cxyz6UiTlVY/m/sm36hUX9L44J

2020-10-15T16:57:09.015Z

Ah, found one where Rich himself addresses the question of get returning nil vs. throwing an exception! https://groups.google.com/g/clojure/c/7nqh1Qc84iQ/m/DriZStY3k68J

seancorfield 2020-10-15T16:59:18.015200Z

It's a bit of a "throw away" reference to Common Lisp's behavior tho'...

2020-10-15T17:00:28.015400Z

Understood it doesn't explain with a lot of words ... Haven't seen anything more than that yet, though.

2020-10-15T17:01:59.015600Z

And I am sure that no matter what quotes I might find, any new FAQ will be reviewed by Alex and perhaps even Rich to avoid giving incorrect reasons/rationale.

2020-10-15T17:03:00.015800Z

FYI +10 from me if you do end up creating a new FAQ entry for this. It is slightly above my activation energy threshold to create one, but interested to see how it turns out.

2020-10-15T17:03:38.016Z

And those 10 points are redeemable, you know, in all the usual places šŸ™‚

seancorfield 2020-10-15T17:22:40.016200Z

A beer at a conference sometime in the post-plague era? šŸ™‚ šŸŗ

2020-10-15T17:23:11.016400Z

Sure. I could probably also arrange to have something from a place like Total Wine & more deliver to your doorstep šŸ™‚

2020-10-15T17:23:44.016600Z

So far the only chain of stores in USA that I have found carries Rauchbier from Bamburg, Germany. An acquired taste.

seancorfield 2020-10-15T17:27:42.016900Z

I've been ordering a lot from Belching Beaver in So. Cal. Lots of fruity sours, if you're into that (I'm not, but I love their other stuff). Also, I've clipped this entire convo into OneNote so I can use fragments of it to distill down to an FAQ at some point. Thanks everyone!

2020-10-15T17:38:13.017100Z

And looking more carefully at the last Clojure Google group thread I linked above, it appears Rich is only addressing the question of get returning nil vs. throwing exception when the key is not found. I don't think it addresses the issue of throwing exception when the collection is not an associative thing.

Tom Helmuth 2020-10-15T01:38:22.492700Z

This just caused some massive headaches when accidentally wrote '* instead of *', but luckily stumbled into the problem.

2020-10-15T01:47:18.492900Z

Yes, that is the explanation for that behavior.

Tom Helmuth 2020-10-15T01:51:08.493100Z

Why is it happy to treat 3 as a map-like thing without throwing an error? I guess that's the part that really baffles me.

2020-10-15T01:57:36.493300Z

I do not know the reason, but I think it is for the same reason that (get 3 '* 2) is happy to treat 3 as a map-like thing.

2020-10-15T02:10:17.493500Z

It has been that way in Clojure I believe since its beginning, and a JIRA bug was files in 2012 suggesting such expression throw an exception. Rich Hickey responded in 2014 that such a change would be a breaking change, and the Clojure core team values backwards compatibility very highly. I know that doesn't explain why it was like that in Clojure's original versions -- again, I do not know the reason, just giving you a little bit of history that this wasn't a recent thing, nor can I find anything in Clojure FAQ nor http://ClojureDocs.org entry for 'get' that gives a reason: https://clojure.atlassian.net/browse/CLJ-1107

seancorfield 2020-10-15T02:18:48.493700Z

If it's any consolation, I think pretty much everyone new to Clojure asks this... šŸ™‚

seancorfield 2020-10-15T02:20:51.493900Z

Symbols and keywords look themselves up in their argument and it's actually very convenient when writing generic code because you don't have to worry about type checking when you simply get nil from an "inapplicable operation".

seancorfield 2020-10-15T02:27:55.494100Z

It feels like this question should be added to https://clojure.org/guides/faq in some form -- @trhtom do you think you could formulate your question in a form suitable for that page? I'd be happy to help turn it into a contribution for that page (I just recently contributed the ?/`!` FAQ there).

Tom Helmuth 2020-10-15T02:53:24.494300Z

Sure, I'd be happy to.

Tom Helmuth 2020-10-15T02:55:27.494500Z

FWIW, I've been using Clojure for 10 years and just discovered that (get 3 5 :default) is happy to treat 3 as a map-like thing. @andy.fingerhut I appreciate the historical context!

seancorfield 2020-10-15T02:58:29.494700Z

Feel free to DM me to chat about it (tomorrow -- it's a bit late tonight for me to coherently process that).

Tom Helmuth 2020-10-15T02:59:35.494900Z

Looks like this will happen with any type of value that isn't a thing that expects to be used for lookup. My student who ran into this issue found the source code, and if none of those conditions are met, it just returns the notFound value: https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/RT.java#L789-L820

seancorfield 2020-10-15T03:01:34.495200Z

Yup, it's a very deliberate design decision: if the type is something you could "get" a thing from, attempt the lookup (and return notFound if it isn't there), else return notFound.

seancorfield 2020-10-15T03:02:05.495400Z

But it surfaces in a number of expressions and that's the part I'm not sure how to formulate as a "frequently asked question".

Tom Helmuth 2020-10-15T03:07:05.495600Z

The crazy part is that it makes the one-character typo of ('* 3 2) behave in a totally unexpected way. I totally get the two parts making sense independently (symbols can be used as functions for lookup and get returning notFound if you can't get from that type), but the combination is lethal.

2020-10-15T03:38:33.495800Z

As you can see from the JIRA issue, at least some people wish that get being called on a non-associative first arg were considered lethal (i.e. throw an exception instead of returning nil) all by itself, but that is unlikely to ever be changed, given backwards compatibility, plus whatever the original design reason was.

mavbozo 2020-10-15T06:50:20.496100Z

every time I see this familiar question, i remember

(get get get get)  
;;=> #function[clojure.core/get]

ryan echternacht 2020-10-15T13:18:06.499600Z

Iā€™m looking to build a ā€œjob queueā€. Itā€™d poll a db for work (Iā€™m doing that with http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/ScheduledExecutorService.html in java), and then fire off work. The work is mostly file imports, that are safe to be run concurrently. Whatā€™s the best clojure threading primitive for this type of work?

ryan echternacht 2020-10-15T13:18:39.000100Z

I care that jobs finish, and their finishing status. but iā€™m not sure how much more I really care about them while running.

ryan echternacht 2020-10-15T13:24:46.001100Z

I could see using core.async for it, but I donā€™t think I have that much of a pipelining need. Futures seem like they might work, but almost seem to barebone ā€” I feel like Iā€™ll be polling them for status, whereas Iā€™d rather react to when theyā€™re done.

kennytilton 2020-10-17T18:09:41.269400Z

Itā€™s not so much polling so much as iterating over a vector of go-loop channels doing a blocking read from each, which will stop at the first unfinished go-loop, ā€œforgettingā€ loops as they finish. I use core.async in cases like this even without a pipelining requirement. fwiw.

ryan echternacht 2020-10-18T00:40:06.286Z

Iā€™m working through building it with core.async, and iā€™m pretty happy. I think I had the mental block that core.async was only for ā€œbigā€ things, but thatā€™s a bad model

šŸŗ 1
borkdude 2020-10-15T13:27:38.001900Z

@ryan072 I borrowed this bit of code from CLJS to run an amount of tasks in parallel (to fully utilize CPU cores) and wait until they're done: https://github.com/borkdude/clj-kondo/blob/51db4e172478c369208ed90db4b7c29d94a8cbce/src/clj_kondo/impl/core.clj#L209-L233 Not sure if that's similar to your problem

šŸ‘ 1
borkdude 2020-10-15T13:28:23.002900Z

Each of the parallel tasks pick work from a queue, until there is no more work, then they lower some countdown latch.

ryan echternacht 2020-10-15T13:32:09.003500Z

thanks, that does help. Gives me some confidence on rolling my own, and a some good java primitives to look into

chunsen 2020-10-15T14:46:32.009200Z

HI, I've got an error saying java.lang.NoClassDefFoundError: manifold/bus/IEventBus when running my backend service using uberjar profile, which configed in project.clj as :profiles {:uberjar {:main hello-ring.main, :aot :all} . Finally I found the reason, the class loader of manifold/bus/IEventBus`` has a hierarchy as below

#object[clojure.lang.DynamicClassLoader 0xc7aac7c clojure.lang.DynamicClassLoader@c7aac7c]
#object[clojure.lang.DynamicClassLoader 0x2186d797 clojure.lang.DynamicClassLoader@2186d797]
#object[sun.misc.Launcher$AppClassLoader 0x23fc625e sun.misc.Launcher$AppClassLoader@23fc625e]
#object[sun.misc.Launcher$ExtClassLoader 0x50313382 sun.misc.Launcher$ExtClassLoader@50313382]
While the class loader of the AOTed class of my business code is #object[sun.misc.Launcher$AppClassLoader 0x23fc625e "sun.misc.Launcher$AppClassLoader@23fc625e"] , so it can not find the class which is loaded by child class loader(`#object[clojure.lang.DynamicClassLoader 0xc7aac7c clojure.lang.DynamicClassLoader@c7aac7c]`). Any one have an idea about this issue?

alexmiller 2020-10-15T14:56:10.011200Z

I think you'll need to share more to understand what you're seeing. where do IEventBus and the impl come from? how is this app run? are you using java -jar with the uber jar to launch that main class?

chunsen 2020-10-15T14:59:06.013300Z

I got this error either run the program through java -jar or through repl .

Endre Bakken Stovner 2020-10-15T18:25:33.020Z

I'd like to get the line and column number of every element in the below data structure. {:a {:aa 2} :b {:ba 0 :bb 3}} So the first { (the whole map) has coordinates 0 0, the :a has 0, 1 {:aa 2} has 0, 4, 3 has 1, 15. Is there anything in clojure or the ecosystem that will help me do this?

p-himik 2020-10-15T18:31:05.020200Z

Not directly, but you could write a macro for that that analyzes &form and its metadata:

user=> (defmacro form-meta [] (meta &form))
#'user/form-meta
user=> (form-meta)
{:line 1, :column 1}

šŸ‘ 1
phronmophobic 2020-10-15T18:31:33.020400Z

My approach would probably be use to use https://clojure.github.io/clojure/clojure.zip-api.html Would you need a need it to return a new data structure or adorn the passed in data?

phronmophobic 2020-10-15T18:32:00.020600Z

oh wait, you're looking for line and character positions?

alexmiller 2020-10-15T18:32:33.020900Z

if you use a LineNumberingPushbackReader to read it, you can get some of that from the reader (in metadata)

šŸ‘ 1
alexmiller 2020-10-15T18:32:55.021100Z

the maps can carry metadata but keywords and numbers cannot so you won't get those

alexmiller 2020-10-15T18:33:32.021600Z

I believe there are other tokenizing parser libs for Clojure that can though, just not sure which is the best recommendation

Endre Bakken Stovner 2020-10-15T18:35:23.021800Z

Thanks!

2020-10-15T19:21:54.022Z

But keywords can't have metadata, so any method that relies on it to return position information won't tell you where the keywords are

Endre Bakken Stovner 2020-10-15T19:35:45.022200Z

I wonder if any libraries are able to work around that limitation in some way? I will have to investigate tomorrow. https://github.com/Engelberg/instaparse

phronmophobic 2020-10-15T19:37:00.022600Z

presumably, libraries like https://github.com/borkdude/rewrite-edn and https://github.com/lread/rewrite-cljc-playground

2020-10-15T19:47:42.023Z

The only way I can think of to get position information for things where Clojure does not support metadata, e.g. keywords, numbers, strings, is to return NOT a Clojure collection that is equal to what clojure.edn/read would return, but instead to return some kind of 'parse tree' representation that had an explicit line/column data for each object of the parse tree.

2020-10-15T19:50:35.023200Z

For example, if when reading {:a 2} you tried returning a value x such that (= x {:a 2}) was true, then there is no place "inside" of there to put the line/column info of the number 2. If you instead return a value y such that (= y {:a 2}) was false, you have many choices for how y represents the string that it read and its parts, and many choices for how the line/column info is embedded in there.

2020-10-15T19:51:40.023400Z

@borkdude wrote https://github.com/borkdude/rewrite-edn very recently, so should have fresh on his mind whether it supports line/col info on all sub-parts of the returned data, or not.

borkdude 2020-10-15T19:56:44.023600Z

@endrebak85 rewrite-edn is based on rewrite-cljc and rewrite-cljc can give you that metadata There is also https://github.com/borkdude/edamame which will parse directly to sexprs with adding location metadata when possible. Using a post-process step you can also have metadata on numbers etc when you coerce them in something that can have metadata. See this test: https://github.com/borkdude/edamame/blob/ba93fcfca1a0fff1f68d5137b98606b82797a17a/test/edamame/core_test.cljc#L306

dominicm 2020-10-15T19:57:57.024200Z

I'm trying to extend this interface: https://github.com/eclipse/lsp4j/blob/release_0.8.1/org.eclipse.lsp4j/src/main/java/org/eclipse/lsp4j/services/TextDocumentService.java#L91-L94 like so:

(reify
  TextDocumentService
  (completion [position]))
but I'm getting:
Can't define method not in interfaces: completion
And I'm confused why, because it's definitely there!

dominicm 2020-10-15T20:04:58.024400Z

Apparently I wanted proxy, my bad!

lread 2020-10-15T20:06:42.024600Z

Did you need proxy because of the default method?

alexmiller 2020-10-15T20:32:19.025300Z

the completion method in your reify should take 2 args - a "this" arg and then the args on the method

alexmiller 2020-10-15T20:33:51.025600Z

(reify
 Ā TextDocumentService
 Ā (completion [this position]))

alexmiller 2020-10-15T20:34:23.026100Z

you probably don't actually need that arg, so fine to replace this with _ in that case

dpsutton 2020-10-15T20:35:33.026500Z

prior art (defining a type rather than reify though): https://github.com/snoe/clojure-lsp/blob/master/src/clojure_lsp/main.clj#L104

dominicm 2020-10-15T21:06:11.026800Z

@alexmiller D'oh! yes.

dominicm 2020-10-15T21:07:11.026900Z

Oh, I didn't know clojure-lsp also used Lsp4j, convenient as a reference!

dominicm 2020-10-15T21:07:25.027Z

I think Alex got it, it's because I forgot this

dominicm 2020-10-15T21:17:56.027300Z

I was going to take a stab at a runtime-LSP for Clojure, but LSP4J feels like the worst of Java šŸ˜ Maybe I'll leave that for a more determined Dominic in the future.

eggsyntax 2020-10-15T22:06:14.032200Z

Someone asked me a beginner question that turned out to be deeper than I expected.

(defn a [l]
  (prn "a:" (type l))
  (prn "a:" l))

(defn b [l]
  (prn "b:" (type l))
  (prn "b:" l)
  (a l))

user> (b (list 1 2))
"b:" clojure.lang.PersistentList
"b:" (1 2)
"a:" clojure.lang.PersistentList
"a:" (1 2)
nil
b evaluates (list 1 2) and it becomes the list (1 2), which it passes to a. a is a function, and functions evaluate their arguments, and lists are evaluated as function calls. Why doesn't a evaluate the list (1 2) and throw an error (given that 1 isn't a function)? I'm sure there's a simple answer to that, but damned if I can come up with it right now.

eggsyntax 2020-10-15T22:08:32.032400Z

From having implemented small lisps, I can handwave about (1 2) having already been evaluated into an data structure in the host language, but that feels a bit shaky to me.

Jan K 2020-10-15T22:15:27.032600Z

The list (1 2) is not getting evaluated anywhere in the code. When a is called its argument, the symbol l is evaluated, resulting in the list.

phronmophobic 2020-10-15T22:15:49.032800Z

b does not evaluate (list 1 2). the repl does

p-himik 2020-10-15T22:16:29.033Z

There are two kinds of evaluation. One is "what does this mean" and the other is "what value does it have". The reader does the former, a function call does the latter.

phronmophobic 2020-10-15T22:17:24.033200Z

I would say the reader does not eval and neither does a function call (unless that function is eval)

p-himik 2020-10-15T22:23:50.033400Z

I think I agree on the former but not the latter. A function doesn't evaluate its arguments. But a function call does IMO. But maybe we mean a bit different things by "a function call".

phronmophobic 2020-10-15T22:26:18.033600Z

https://clojure.org/reference/evaluation

eggsyntax 2020-10-15T22:28:22.033800Z

Thatā€™s the doc Iā€™ve been poring over. ā€œNon-empty Lists are considered calls to either special forms, macros, or functions. A call has the form (operator operands*).ā€ ā€œIf the operator is not a special form or macro, the call is considered a function call. Both the operator and the operands (if any) are evaluated, from left to right. The result of the evaluation of the operator is then cast to IFn (the interface representing Clojure functions), and invoke() is called on it, passing the evaluated arguments.ā€ It seems like b is receiving a non-empty list, so it should be evaluated and therefore treated as a function call.

eggsyntax 2020-10-15T22:28:58.034Z

> There are two kinds of evaluation. One is ā€œwhat does this meanā€ and the other is ā€œwhat value does it haveā€. The reader does the former, a function call does the latter. Iā€™d be interested to see where thatā€™s documented; I donā€™t feel like Iā€™ve heard of that before.

phronmophobic 2020-10-15T22:29:40.034200Z

> AĀ functionĀ doesn't evaluate its arguments. But a functionĀ callĀ does IMO. But maybe we mean a bit different things by "a function call". I guess that sounds right based on the reference page.

p-himik 2020-10-15T22:30:02.034400Z

That quote above about non-empty lists talks about list expressions, not values.

p-himik 2020-10-15T22:30:15.034700Z

It talks about "what it means", not "what its value".

Jan K 2020-10-15T22:31:23.034900Z

> Non-empty Lists are considered calls That is literal lists, but there is no literal (1 2) in the code.

p-himik 2020-10-15T22:31:33.035100Z

(def a ())
a
Here, a means "a symbol that denotes a var that has a value of a list". It's different from () that means "a list".

p-himik 2020-10-15T22:32:22.035300Z

Right, "literal" is a good word, although that evaluation page linked above doesn't mention it.

CĆ©lio 2020-10-15T22:56:27.044200Z

Hi all. Does anyone know why clojure.data.json/read-str behaves this way? I have this situation where some keys contain multiple slashes. In the first example below, the resulting map is tagged with #:a which appears to come from the key, and the resulting key is missing the a/ prefix. This only happens when the JSON object contains only one key with multiple slashes. In the second example though the object contains two such keys and the behavior changes, it doesnā€™t add any tag to the resulting map and preserves the form of the original key.

(clojure.data.json/read-str "{\"a/b/c/d\":1}" :key-fn keyword)
;; => #:a{:b/c/d 1}

(clojure.data.json/read-str "{\"a/b/c/d\":1,\"e/f/g/h\":2}" :key-fn keyword)
;; => {:a/b/c/d 1, :e/f/g/h 2}
The same behavior can be observed with Cheshire.
(cheshire.core/parse-string "{\"a/b/c/d\":1}" true)
;; => #:a{:b/c/d 1}

(cheshire.core/parse-string "{\"a/b/c/d\":1,\"e/f/g/h\":2}" true)
;; => {:a/b/c/d 1, :e/f/g/h 2}
Interesting though is that keyword does what I expect, it doesnā€™t cut off the keyā€™s prefix, so I suspect the behavior described above is caused by something else.
(keyword "a/b/c/d")
;; => :a/b/c/d
Is there any way to prevent the key from being parsed without the prefix, as in the first example?

2020-10-15T23:05:26.046400Z

In your first example, it appears to be a keyword with namespace a and name b/c/d

CĆ©lio 2020-10-15T23:07:47.047900Z

@lennart.buit At first I was surprised to see that the parser was taking off the a as a namespace and using it to tag the map. But that doesnā€™t explain the second exampleā€™s results.

phronmophobic 2020-10-15T23:09:57.050800Z

this isn't about the parser(s). it's just how the map is being printed out:

user> {:a/b/c/d 1, :e/f/g/h 2}
{:a/b/c/d 1, :e/f/g/h 2}
user> {:a/b/c/d 1, :a/f/g/h 2}
#:a{:b/c/d 1, :f/g/h 2}

phronmophobic 2020-10-15T23:10:07.051300Z

the prefix is just a shorthand

2020-10-15T23:10:14.051600Z

Well the #:a{...} syntax denotes all keys share the namespace :a, in your second example it canā€™t shorten it because there is no common prefix

šŸ‘† 1
phronmophobic 2020-10-15T23:10:36.051800Z

the key is still the same

user> (get {:a/b/c/d 1, :a/f/g/h 2} :a/b/c/d )
1

CĆ©lio 2020-10-15T23:11:59.053200Z

Hah! Interesting.

2020-10-15T23:12:33.054400Z

(map namespace (keys ...)) out of the top of my head will tell you what the namespace parts of the keywords are :)

CĆ©lio 2020-10-15T23:12:35.054600Z

Indeed:

(let [m (cheshire.core/parse-string "{\"a/b/c/d\":1}" true)]
  (keys m))
;; => (:a/b/c/d)

p-himik 2020-10-15T23:13:19.054800Z

I know that you didn't really ask about it but perhaps you might want reconsider keywordizing such maps where keys contain special symbols like /. Otherwise, it increases the potential to make things worse down the road.

šŸ‘† 1
CĆ©lio 2020-10-15T23:13:41.055Z

Thatā€™s comforting. Now I need to find out why the key :a/b/c/d is getting stored as b/c/d in mongodb.

p-himik 2020-10-15T23:14:25.055300Z

Oh no. That's exactly what I'm talking about. :)

p-himik 2020-10-15T23:14:31.055500Z

Because it uses name, I bet.

CĆ©lio 2020-10-15T23:15:41.055900Z

@p-himik Totally agree. Unfortunately in this particular case I cannot avoid that. šŸ˜ž

2020-10-15T23:16:14.056800Z

Hmm? Why do you need to keywordize while parsing?

eggsyntax 2020-10-15T23:17:51.057500Z

Iā€™m curious whether you all see yourselves as saying the same thing in different words, or whether you think there are real disagreements there. Iā€™m certainly not sure myself. Some of the statements above seem wrong to me, but I may be misinterpreting them.

eggsyntax 2020-10-15T23:18:21.057800Z

I definitely donā€™t feel like Iā€™ve got this nailed down to the point where I can explain it clearly to a beginner.

CĆ©lio 2020-10-15T23:20:14.058Z

@lennart.buit Itā€™s a huge map that comes thru a REST endpoint. 99% of the keys are just your good old normal keys but thereā€™s this little map deep inside which contains those bizarre keys with slashes.

CĆ©lio 2020-10-15T23:21:41.058200Z

I guess Iā€™ll have to give that little map a special treatmentā€”i.e.: converting those keys to stringsā€”before storing in mongoā€¦ ugh!

seancorfield 2020-10-15T23:22:10.058400Z

(list 1 2) evaluates to a data structure that looks like (1 2) -- and that's it: it's just data at that point. The call (a l) is calling a and passing in a data structure.

p-himik 2020-10-15T23:22:44.058600Z

Personally, I don't feel like I have any authority here. So feel free to use my ramblings to adjust your mental models but don't really rely on them. I still feel like Alex Miller is about to come and correct us all at the same time. :) Oh, or Sean Corfield. šŸ‘‹

seancorfield 2020-10-15T23:23:08.058800Z

There's no additional level of "evaluation" going on because in (a l) the argument is just data already.

seancorfield 2020-10-15T23:23:54.059Z

The evaluation there is that the local symbol l is "evaluated" (looked up) to get its value, which is a data structure.

eggsyntax 2020-10-15T23:24:45.059200Z

That seems like what @p-himik is getting at with the expression/value distinction above.

seancorfield 2020-10-15T23:25:35.059400Z

Or, to put it another way: (b (list 1 2)) evaluates its argument (list 1 2) to get a data structure (1 2) which is passed into b (a l) evaluates its argument l to get the data it is bound to -- the data structure (1 2) which is passed into a

ā­ 1
seancorfield 2020-10-15T23:26:30.059700Z

Evaluating a symbol means "look up the symbol's value" -- but there's no "evaluate the thing that the symbol is bound to".

seancorfield 2020-10-15T23:27:45.059900Z

If you had (a (eval l)) then, yes, you'd be evaluating what l is bound to... and that would then evaluate (1 2) and fail...

seancorfield 2020-10-15T23:27:47.060100Z

(let [l (list 1 2)] (println (eval l)))

eggsyntax 2020-10-15T23:28:08.060300Z

I think that makes sense to me. It certainly fits with ā€œThe expressions exprs [of a fn] are compiled in an environment in which the params are bound to the actual argumentsā€ (from the special forms https://clojure.org/reference/special_forms#fn).

eggsyntax 2020-10-15T23:31:24.060600Z

After five years or so of Clojure Iā€™ve gotten to the point where I rarely get confused by any of the specifics (writing macros, obscure core functions), but I periodically go through a fresh stage of confusion about the most basic things šŸ˜œ

p-himik 2020-10-15T23:32:40.060800Z

Heh, like noticing how exactly you're breathing. Or walking.

ā˜ļø 1
p-himik 2020-10-15T23:33:35.061Z

Or just don't keywordize the keys, separate the things that need to be keywordized from the things that don't need that, and keywordize the former manually.

šŸ‘ 1
eggsyntax 2020-10-15T23:46:01.061400Z

Thanks, yā€™all!

lread 2020-10-15T23:56:55.061700Z

Ah! Thanks.