Fork me on GitHub

If you had a map whose values were vectors and you wanted to use destructuring to name the values in that vector would you use something like this: (defn my-fn [{:keys [foo bar]}] (let [[a b] foo] ... or is there a more idiomatic way to get those a and b values out of that vector?


(ins)user=> (let [{[a b] :foo} {:foo [1 2]}] (println :a a :b b))
:a 1 :b 2


I think the normal non-keys destructure is clearer


ahh yeah. I can see that. Wonder if that gets unwieldy if I'm destructuring many keys. I don't have a use case, just checking for understanding.


often multiple lines in a let is clearer than a nested destructure


and it's likely to compile to more efficient code as well if that matters


that was my other question. does destructuring provide any performance benefits or regressions versus just grabbing things using (first vector-in-foo) or whatever


Or is it kind of like syntax sugar over such calls?


if you expand the macro, you'll see that destructure is pretty much always worse perf wise than a manual binding - it is syntax sugar, but it does a lot of things your case won't need


(ins)user=> (pprint (macroexpand '(let [{[a b] :foo} {:foo [1 2]}] (println :a a :b b))))
  {:foo [1 2]}
   (clojure.core/seq? map__240)
   (clojure.lang.PersistentHashMap/create (clojure.core/seq map__240))
  (clojure.core/get map__240 :foo)
  (clojure.core/nth vec__241 0 nil)
  (clojure.core/nth vec__241 1 nil)]
 (println :a a :b b))


Oh, interesting. I was just telling myself yesterday to up my destructuring-fu as I've been seeing more and more of it while reading other's code. I don't mind just using let bindings though if it's a big performance difference. I find the latter more readable so far too. I'm sure a proper balance of both is the right answer


IMHO clarity is king - the less effort required to figure out what the code is trying to do, the less likely a bug can remain unseen


with the ammendment that it's worth learning certain idioms and constructs to make them clear :D


(because in the long run they help your code be clearer if you learn them)


so I'd learn destructuring, but not throw multiple or nested destructures into one line of code


Haha. I can agree with all of that. Thank you.


That being said, I really liked this whole :keys things as to me it clearly shows what keys you want to use from the map but you were thinking it's less clear. hahaha


it's a trade-off - the reverse-key destructure is less commonly used, and slightly less elegant, but it lets me replace two lines with one


and I'd argue that it's not obfuscating


but communicating to human beings isn't a science, it's an art


Well said. Thanks for the chat!


I personally like the reverse-key destructure 🙂 but always multiple lines, yes


That’s how I usually destructure a ring request, for example… some might say it’s unorthodox, but I do prefer it; it is almost documentation-without-documentation


It describes the shape of the datastructure, semi-formally, in a reverse order, and serves as code/local bindings at the same time


If you're binding something to a different name, the reverse destructure is very useful, but I never find it as readable since it always looks like value :key value :key which is 👀 🤯 and then you have :as something at the end so you go back to the more "natural" :key value order.


That's a good reminder that you can destructure on string keys (`:strs`) and symbol keys (`:syms`)


The mnemonic is always symbols you’re binding on the left


until you hit :as


Modifiers are keywords but those are either symbols or maps with symbols on the left (like :or)


The “problem” with {:keys [a b c]} from my perspective is that you can’t “continue” the destructuring further down through a, b, c, etc


… and then later, if you want to destructure a, b, c either you need to do it in another data structure (which for me has the mental cost of piecing together the data structure later on), or replace/retype {:keys […]} with a reverse destructure


Like in :headers in the example above, I’m almost 100% certain that I won’t dig further down, since that’s typically key/value pair; so I use :keys/:strs there, no need to be verbose like {time-zone “time-zone”}


Using just a simple clj and deps.edn setup, what's the best way to reload ClojureScript code via the REPL?


You should be able to just eval and reload code just like clojure. Are you looking for hot reloading a react app though?


Yeah, primarily say I make a change in blah.core, just want a one-liner way to reload that and re-render to the DOM (e.g., via reagent.dom/render as I'm using Reagent).


For easiest hot reloading, I guess I will need to use something like shadow-cljs though?


I have used figwheel in the past. I'm really trying to do things as boiled down to the basics as possible here, thus the deps.edn/`clj` setup.


Could be that's just not worth it for this workflow though?


Figwheel main is where you want to be the


I guess just repasting a modified defn and the reagent.dom/render does get me partway there -- just no way to re-eval an entire file w/ one line?


But yeah, I think for a serious tight loop, I've just gotta use something with proper hot reloading.


how are you serving the webpage?

Jim Newton12:06:36

With regard to , it's a nice historical perspective. I haven't finished reading all 40 pages of it. His recounting of the history of the development of the language is a bit different that I guessed from learning the language. for example, when I look at the low levels of many things I see someone who first tried to implement Common Lisp in Java, and later changed his mind. Does anyone know whether that is part of the history which has been rewritten?


Not sure I understand the question at the end?

Jim Newton12:06:46

For example there is the cl-format function which must have been a huge undertaking, and the way the reader and printer work with global variables like *print-base* , *print-readably* , and *read-eval*


cl-format was contributed, that wasn’t written by Rich

Jim Newton12:06:40

ah ha, that's an interesting piece of information.


Tom Faulhaber did all of that


very early on, Clojure was partly written in Common Lisp

Jim Newton12:06:51

did Faulhaber also write the reader?

Jim Newton12:06:57

> Clojure was partly written in Common Lisp

Jim Newton12:06:11

I missed that skimming the paper.

Jim Newton12:06:01

Something I did glean from the paper was that Hickey worked on clojure for about a year before realizing what he had done.


Rich wrote the reader


there were some pre-Clojure projects too with other attempts


different points in the java/lisp interop space

Jim Newton12:06:00

He decided against reader macros (good choice BTW), but he did implement several # reader macros. not sure how that works, perhaps they are all explicitly baked in and not configurable?

Jim Newton12:06:18

Yes I saw jfli and foil mentioned in the paper. Didn't really know what they were though.

Jim Newton12:06:33

These historical perspectives are great stuff.

Jim Newton12:06:50

It's easy to forget unless someone documents them.

Jim Newton13:06:31

But we have a tendency to document the successes. Documenting failures is not really something people want to dedicate large efforts to, nor do readers really care to read about them.


Does the latest Clojurescript make it easier to use Storybook'. How has anyone used storybook clojurescript?

Jim Newton13:06:47

Question: In clojure, what is an object ?

Jim Newton13:06:33

In lisp and SmallTalk every value is an object. but in some OO langauges, an object is only an instance of some subclass of the Object class.

Jim Newton13:06:12

In Scala the term object is difficult to use because there is a langague construct called object which creates (as I understand) a companion object, and instance of a hidden class who contributes to the scope of a class of the same name as the object.


all clojure values are objects, in some corner cases they can be unboxed by the compiler into primitives, but not in a way you can directly manipulate in the language


clojure intentionally avoids hidden / clojure only compiler properties with no implementation in bytecode


there's no spooky parallel reality here :D

Jim Newton13:06:55

@noisesmith, that seems to be a good description. It is the definition I infer from


if you look at the clojure compiler / core code in github, everything takes and returns Object


as in, the literal java superclass of all classes

Jim Newton13:06:05

hmmm, so object does have the implication that it is an instance of the Object class?


conceptually the core definition as far as java code is concerned is Object IFn(Object ...)


well, the way the java class system works, all instances of all classes are instances of object


(ins)user=> (instance? Object [])
(ins)user=> (supers (class []))
#{clojure.lang.Indexed java.lang.Runnable java.lang.Iterable clojure.lang.IFn clojure.lang.IReduce clojure.lang.APersistentVector java.util.List clojure.lang.IPersistentCollection clojure.lang.IPersistentVector clojure.lang.IMeta java.util.Collection clojure.lang.Sequential clojure.lang.Seqable clojure.lang.Counted clojure.lang.Associative java.util.RandomAccess clojure.lang.IKVReduce clojure.lang.IReduceInit java.util.concurrent.Callable clojure.lang.ILookup java.lang.Object clojure.lang.IEditableCollection clojure.lang.Reversible clojure.lang.IPersistentStack clojure.lang.IHashEq clojure.lang.AFn java.lang.Comparable clojure.lang.IObj}


that empty vector is an instance of each of those classes


ClojureScript is a clojure and has a very different notion of what a js/Object is. That might give some hesitation to conflating Object and java.lang.Object


don't have time to find the passage from the HOPL document but I believe he called it Clojure in Clojure


oh, yeah, cljs is its own beast and I wasn't attempting ot describe it at all here


so there's an illegal reflection party in pretty much every package i clone now... can someone explain what reflection is in the case of clojure?


when you make a Java interop call, Clojure has to emit bytecode to make the call


if it can infer (or if you provide type hints), it will emit exactly the right invocation


if not, it will emit a call to use Java reflection to inspect the object at runtime to find the method to call


since they introduced the module system in Java 9, many reflective calls to implementation classes are also illegal accesses due to the new module visibility rules (you need to invoke the public exported interfaces, not the module-private impl classes)


so it's invoking private methods** rather than methods that are exposed?


"methods" are not reified in the vm, you inspect a class and get descriptions of methods


it is asking about an implementation class, instead of the public superclass, that's the error


oh... that's a good point.. just a bag of instructions


okay, i still don't get that 100%, that's only 10% clear to me, but i wonder... is there a way for clojure to do the inspect and then let me know the right type hint? some of the reflection bits are on packages upstream


IMHO it's weird and brittle to allow access to a method, but only allow reflective access to its description via a superclass, but that's water under the bridge


haha it seems like a weird deviation


you can fix that for functions you define, but the hint would need to be offered in the body being compiled, so you'd need to own the code of the lib to fix it


the reflection api is designed to tell you about stuff, including stuff you can't invoke


the frustrating part is to not be able to know whether the call you are reflectively looking at is invokable without invoking it


so when it warns on reflection... is it still invoking the wrong method? right method? right method and needs a typehint now that it found it?


you can tell whether it's illegal and the Clojure reflector will use that information (if you set --illegal-access=deny ) to filter out those options


but currently, because it's only a warning, there is no feedback mechanism in the reflection api


where do i put that flag?


the best solution is for the call to not be ambiguous in the first place


but you may not control that code


the common case for this is having a public IFoo interface with a method m(), and then a non-visible implementation FooImpl extends IFoo that implements m(). At runtime you have a FooImpl object instance. If you try to invoke FooImpl.m(), it will complain but invoking IFoo.m() will be fine.


in both cases you're invoking the same method on the same object, it's just how you're saying it


technically Foo Impl could offer a different definition of m(), non?


IFoo is an interface here so there is no impl at all, it just defines the signature


you can't get two different implementations of the same method on one object can you?


but yes, IFoo could have a default method now and FooImpl could provide a different version


@noisesmith no, but that's not the right question


the question is given an object and a method, which method impl will be invoked


if you extend an object i believe you can redefine a method, so when you call extended-object.method you get something different than original.method


and there are other weirder cases


oh right because the method is bytecode, it's not a "thing" owned by the object per se - so eg. you can call the superclass' method inside your own impl without creating a separate instance of the super, right?


is there some preprocessing step that could happen that would delete the need for reflection?


yeah, type hints :)


if you just tell the compiler it's an IFoo, it will call IFoo.m() :)


oh I know - a source code monkey-patcher that adds hints programmatically! (sarcasm, please don't do this)


so, inference?


i was thinking on the other side, before it gets to clojure land


but i see what you mean, type hints solve it


in some sense, what they did with the access warnings is the worst case for us


java is the bank and java is also feeling around in the dark for my account information


they defined a new way to restrict access but allowed it to continue working anyways, with no feedback mechanism for knowing what it would complain about


haha okay it's not just me a little unhappy with upstream java


how about this really irritating change that doesn't affect performance ? push globally


if they had a) not warned, no one would see it and things would continue working as before or b) denied it and provided feedback via isAccessible() then we can work harder - we do this now


.isAccessible makes sense... static bytecode analysis seems like not something we should be doing


that exists, and we use it (that's why it works when you set it to deny)


sure - but yes, and we also commit to late extension, which means that you could see a class at runtime that hadn't been written yet when your code compiled


yeah... anything that adds brakes (stop lights) to compilation seems problematic for such endeavorrs


made sense to me but i'm out there


this one of many compromises they had to make in bolting on the module system semantics. to me, they punted on the interesting hard problems (like having version semantics for modules and classloader entanglement) and just added a bunch of complexity. plus they had to weaken all of the enforcement so that existing stuff could keep working, so you don't even get the benefits they were going for.


yeah but why work on interesting hard problems?


thumbs down from me - worst thing they ever did to java


they did work on it - it was really hard and they prototypes that did do interesting stuff in this area


but I think it's just impossible to do the right things at this point in Java's lifecycle


they should have just killed it


modules, not Java :)


there's no harm in deciding not to do something

Michael J Dorian14:06:26

Is this also tough for the other JVM languages right now?


I'm sure scala doesn't notice, as it uses inference / declaration and doesn't rely on runtime reflection

Michael J Dorian14:06:18

Right, more dynamic more reflection


iirc kotlin uses inference rather than reflection too


those are the big competitors :D


yeah, the statically typed langs have the types


jruby would be comparable, I am sure there has been some cross-seeding of techniques between their impl and clojure


groovy might be one to ask about


I haven't paid attention to it for a long time


might be interesting to see what they did


that post mentions invokedynamic - it hadn't occured to me that would help


when is the right time to fork Java


I did some research on this. Alex is correct that Groovy is the one to look into


that top code is interesting, we could do something similar


it's annoying to do it while maintaining java 8 as well (which has none of those apis), but it's doable


Ghadi if you wanted to make a jira for that, go for it


hi folks. How can I recur a whole map to defn the same way I do it with vectors or values? and have access to all its keys and values?


What's wrong with my destructuring here (fn [{:keys [lang routes :as request] {:keys [email]} :params}] It's working fine for all args except request is empty. I am trying to capture the original map


:as should not be inside the vector


also, moving some of the destructuring into a let will make this easier to read / maintain without a performance cost


Thanks @noisesmith that solved it. I will see if I can make it cleaner with let