This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2020-06-12
Channels
- # aleph (1)
- # aws (2)
- # babashka (44)
- # beginners (178)
- # biff (12)
- # calva (22)
- # chlorine-clover (60)
- # cider (1)
- # clj-kondo (9)
- # cljdoc (6)
- # cljs-dev (37)
- # cljss (2)
- # clojure (43)
- # clojure-europe (3)
- # clojure-finland (23)
- # clojure-italy (1)
- # clojure-nl (4)
- # clojure-norway (3)
- # clojure-spec (56)
- # clojure-uk (148)
- # clojuredesign-podcast (1)
- # clojurescript (11)
- # conjure (5)
- # core-async (22)
- # cursive (9)
- # datascript (5)
- # datomic (4)
- # duct (8)
- # emotion-cljs (2)
- # figwheel-main (15)
- # fulcro (53)
- # graalvm (68)
- # helix (2)
- # jackdaw (1)
- # kaocha (9)
- # lambdaisland (1)
- # malli (10)
- # meander (2)
- # news-and-articles (1)
- # observability (12)
- # off-topic (17)
- # pathom (1)
- # pedestal (25)
- # practicalli (1)
- # protojure (4)
- # re-frame (2)
- # reagent (57)
- # reitit (1)
- # releases (2)
- # shadow-cljs (69)
- # specter (6)
- # tools-deps (10)
- # vim (16)
- # vscode (4)
- # yada (3)
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
nil
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
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))))
(let*
[map__240
{:foo [1 2]}
map__240
(if
(clojure.core/seq? map__240)
(clojure.lang.PersistentHashMap/create (clojure.core/seq map__240))
map__240)
vec__241
(clojure.core/get map__240 :foo)
a
(clojure.core/nth vec__241 0 nil)
b
(clojure.core/nth vec__241 1 nil)]
(println :a a :b b))
nil
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
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
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?
For the easiest I would say figwheel main. Should be very similar to and even mimic cljs main. Shadow does a lot more and patches the cljs compiler a bit to extend some features. Makes npm interop much easier (or possible) but api is not similar to nor does not try to imitate cljs main
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.
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.
Just via clj
-- similar to: clj --main cljs.main --compile hello-world.core --repl
where it opens a webpage to localhost:9000
.
With regard to https://download.clojure.org/papers/clojure-hopl-iv-final.pdf , 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?
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
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
did Faulhaber also write the reader?
> Clojure was partly written in Common Lisp
I missed that skimming the paper.
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
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?
Yes I saw jfli and foil mentioned in the paper. Didn't really know what they were though.
These historical perspectives are great stuff.
It's easy to forget unless someone documents them.
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?
Question: In clojure, what is an object ?
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.
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
@noisesmith, that seems to be a good description. It is the definition I infer from https://clojuredocs.org/clojure.core/read
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
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 [])
true
(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 java.io.Serializable 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?
i thought it was for java to introspect itself
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?
right on.
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
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
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
https://github.com/apache/groovy/blob/b78f5950795a48b741369feb163f1753f9930a18/src/main/java/org/codehaus/groovy/vmplugin/v9/Java9.java#L242-L272 Reflection canAccess warnings https://issues.apache.org/jira/browse/GROOVY-9103 https://issues.apache.org/jira/browse/GROOVY-9081 https://issues.apache.org/jira/browse/GROOVY-8339
when is the right time to fork Java
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?
yup:
(defn my-fn [{:keys [num] :as m}]
(when (pos? num)
(prn num)
(recur (update-in m [:num] dec))))
> (my-fn {:num 4})
4
3
2
1
nil
what is a nice way to split a collection on a predicate? i.e. split a map into two where one is for the key value pairs satisfying the predicate, and the other for the ones failing
all I can think of is reduce-kv
is there some kind of map-some
idiom with maps to map over them and remove nils?
Hi everyone. Is it possible to have circular dependencies on two multimethods? I got it working for normal functions using resolve, but now I wanted to change them both into a multimethod, but it isn't working anymore. Instead it gives the following exception: java.lang.NullPointerException at puf.codegen.b$code_V.invokeStatic(b.clj:8)
.
P.S.: I know this is generally not good code, but I am implementing something from uni where I have two functions which each depend on each other. Also I generally just want to find out why it would not work anymore with multimethods, I can of course just use simple functions instead.
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