This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2019-06-15
Channels
- # announcements (2)
- # aws (1)
- # beginners (200)
- # calva (136)
- # cider (32)
- # clj-kondo (1)
- # cljs-dev (14)
- # cljsrn (19)
- # clojure (147)
- # clojure-argentina (1)
- # clojure-dev (144)
- # clojure-nl (2)
- # clojure-spec (14)
- # clojure-turkiye (1)
- # clojure-uk (1)
- # clojurescript (7)
- # data-science (1)
- # datomic (5)
- # duct (6)
- # figwheel (2)
- # fulcro (4)
- # graalvm (10)
- # graphql (1)
- # immutant (1)
- # joker (4)
- # off-topic (30)
- # om (1)
- # re-frame (11)
- # reagent (2)
- # reitit (4)
- # shadow-cljs (63)
- # testing (1)
- # tools-deps (7)
Does using something like %:some-map-key
for destructuring maps in anon fn args sound like a reasonable idea?
so there would need to be special support at the reader level just for some special syntax that doesn't really readability
*improve readability
right, %
can be a valid symbol. So the existing variants don't break things syntactically
but even if you picked some other syntax that was a valid symbol, I'd still say not a good idea
anything that's about anon-fn-args would have to be implemented in the reader anyhow so "you'd have to do it in the reader" isn't...you know...like...the objection it sounds like...or whatever
Well, so a smaller change would be to simply allow %some-map-key
to destructure a map key from a passed in map
that's a valid symbol and could be contained in the fn* logic that picks out the indexed args
or maybe it wouldn't; I guess you could do it with a macro
the current stuff just happens to be implemented in the reader
It does add extra semantics, but it'd sorta match up the sugar anon syntax with the rest of the language of being able to deal with both indexical and associative parameters
(defmacro % [& crazy-fn-args] ...)
(% (+ %:foo %:bar))
can do it in userland@gfredericks well there's no "special" syntax for #()
, %
is read as a regular symbol and interpreted in a special way
@bronsa the reader expands it based on the largest %400 present
I mean I'm sure we both understand what's going on and are only going to argue about what you meant by "special"
@john I don't understand your question
@gfredericks oh, I see what you mean, like using that macro instead of using #()
okay you're just saying the name %
is bad
@john yes, though as @bronsa mentions it maybe needs a different name
I believe you'd need a similar "can't nest them" restriction as #()
, though I don't know how you'd detect/enforce it
user=> (let [x (with-local-vars [x nil] x)] (defmacro can't-nest [body] (if (bound? x) (throw (Exception. "can't nest")) (with-bindings {x nil} (macroexpand body)))))
#'user/can't-nest
user=> (can't-nest 1)
1
user=> (can't-nest (can't-nest 1))
Syntax error macroexpanding can't-nest at (REPL:1:1).
can't nest
is the worst part about this that macroexpand-all
doesn't work?
i suspect it could massively break when used in combinations with some crazy macros like go
though
I guess that's the best part about implementing the base parse in the reader -- detecting nesting cleanly
yeah, reader macros have the nice property of expanding in the "opposite" order of regular macros
they sorta do
ah and the %some-key
syntax wouldn't work because numbers and the &
symbol are valid keys too
yeah, I'm pointing out that just because "it apparently works", doesn't mean it should be used
although I'm not sure what the reader allows and disallows is ever going to change so that may not mean much in practice
All it really saves you is from having to do (:foo %)
... not much. But I don't know, this seems pretty clojurey to me #(Point. %:x %:y %:z)
@bronsa I like the idea of using the terms "de facto" and "de jure" to distinguish these cases 🙂
dunno if I can persuade anybody else of that though
@john there's also some weird asymmetry in the fact that you presumably can only reference the keys of the first arg, but you can otherwise access other args
Just from a usability side I would hate deciphering code like that. I don’t like complicated #(...) forms
From another angle, one might argue that this is more self documenting #(Point. %:x %:y %:z)
you mean it's not that deep 😉
What does that even mean anyway lol... anyway, good chat. Maybe I'll try the idea out sometime and report back
well %
becomes ambiguous; I can't think of any severe ambiguities/problems that would arise from an explicit "inner #()
can never reference args from any outer #()
" rule
I wouldn't be surprised if the fact that (fn [...] ...)
isn't very many more characters, and does not have the restriction or extra-rule-to-learn overhead for reading and understanding, factored into the decision somewhere.
But I didn't design it, so those are just guesses on my part.
yeah it's definitely the more conservative choice; easy to relax the rules later, hard to enstricten them
So I tried to do something in user land using just tagged readers, like #m/% #(Point. %:x %:y %:z)
or #(Point. #m/% :x ...
and neither work 😕
that looks makeworkable what's it do?
unless the transformation fro #m/% :x
to (:x %s)
happens too late in the reader, could be
oh I see, the reader won't just pass %:x
through
you could imagine it being implemented more leniently
user=> (binding [*data-readers* {'m/% (fn [k] (list k '%))}] (read-string "#(Point. #m/% :foo)"))
(fn* [] (Point. (:foo %)))
yeah, in the second the transformation happens too latenot even sure it's intentional, I'd say it's just a byproduct of the LispReader implementation
Right... so it's mostly an arbitrary restriction. I see. I've slowly started to just rely more and more on fn exclusively. There's still the occasional #() when there's a need for a short single fn call, since you don't need to duplicate parens. But I'm guessing the restriction is actually to kinda force people into this pattern
I may have done it wrong, but
(#(println #m/% :bob) {:bob 1})
WARNING: Use of undeclared Var lz.core/% at line 1 <cljs repl>
nil
nil
I mean, I'd have to change the logic that handles the char level reading of #() forms in clojure
and then you have to write your own walking code and decide what to do about nesting
@bronsa data readers and macros are equivalent approaches here, right?
but you're getting into insane edge cases territory if you want to know the difference
is there an emoji with a face expressing half horror and half eager anticipation
if it were a macro, other macros would see the unexpanded macro expression vs the fn expression for one
oh right
the other problem is more subtle and I can't give you a concrete case where it may break off the top of my head, but it exists and it has to do with the fact that fn*
are compiled at analysis time
it's just never going to ever be a problem to anybody ever unless they go look for it though :)
interestingly this is one of the reasons why in common lisp lambda
is special cases in both the compiler and the reader
Isn't it just that the code is first read, then tagged literals are applied. Then reading is done, and now macros are applied? So % being part of the reading means it's already been modified?
yes but if you're implementing #()
as a macro rather than a reader macro which is what @gfredericks was asking, then it would not be a problem
I think for @john it be easier he just went with a different tag. Like #f(Point. %key1 %key2)
. That would be possible I believe
heh, implementing the args via another tagged reader would be slightly easier than using %key
, but both approaches could work
I went down a similar rabbit whole a while ago. And realized there's pretty much nothing shorter then just fn. Everything else is barely saving you anything hehe
the tagged reader version would be a bit of a mindfuck too, for ad ifferent reason: the args would be read before the fn tagged reader function would be invoked
so they'd have to register to the function, not the other way round which is the intuitive impl