Does using something like %:some-map-key
for destructuring maps in anon fn args sound like a reasonable idea?
Or at that rate, %[...
and %{...
with their destructure semantics
no
neither of those would be a valid symbol
so there would need to be special support at the reader level just for some special syntax that doesn't really readability
*improve readability
ty
Np
right, %
can be a valid symbol. So the existing variants don't break things syntactically
:
is not a valid charcter for as ymbol, and [
or {
can't be part of symbols either
right
but even if you picked some other syntax that was a valid symbol, I'd still say not a good idea
it doesn't buy you much and adds extra special syntax
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
ah
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 userlandwould that work with anon fns though?
@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
right
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
% is intercepted at read time before it reaches macroexpansion time
so if % was a macro it wouln't nest the same way as it does(n't) now
@gfredericks oh, I see what you mean, like using that macro instead of using #()
okay you're just saying the name %
is bad
yep
any other symbol would do
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
π
(you'd need to macroexpand-all nestedly, but you get the idea)
is the worst part about this that macroexpand-all
doesn't work?
you could use t.a.jvm/macroexpand-all or ridley if you really felt inclined
i suspect it could massively break when used in combinations with some crazy macros like go
though
not that i'd think it was a good idea even if it didn't break, was mostly a joke
sure
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
not really opposite but can't think of a better way to say it
anyway this is going a bit off topic, sorry
they sorta do
ah and the %some-key
syntax wouldn't work because numbers and the &
symbol are valid keys too
I didn't think symbols with colons in them were allowed but that apparently works
loads of theoretically unreadable symbols are accepted by the reader
That's specifically not supposed to be allowed though right?
%:foo
is not a readable symbol, but the reader accepts it anyway
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
lol
@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
%4:y
π
I was afraid that would happen
But yea, asymmetric
and %4:y
is a little ugly to my current sensibilities
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)
But yeah, %4:y
is starting to look a little like perl or something
But it's only one level deep... not that confusing :thinking_face:
you mean it's not that deep π
It's not a second or third order that, is all I'm saying
What does that even mean anyway lol... anyway, good chat. Maybe I'll try the idea out sometime and report back
Speaking of which, what's the reason for not allowing #() to nest?
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?
%
is interpreted too early in the reader in #()
for the first to work
the second may work
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 latethe reader would need to "re-read" the form after the tagged reader has run
since it only interprets literal %
symbols in a #()
context
quite a subtle edge case
not even sure it's intentional, I'd say it's just a byproduct of the LispReader implementation
could definitely be made to work, not sure if it's worth the effort
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
that's compiling to (fn [x] (:bob %))
which is wrong
there's no way to make it work correctly with #()
yeah
#()
interprets %
args at the char level not at the sexpr level
ah
so it's too early for any custom reader macro, which returns sexprs
(well, edn)
and too late for the other way round
So to try it out, I'd have to play with the reader
no, you'd have not to use #()
at all
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
something like #f/% (foo #v/% :foo)
could be made to work
but the implementation would be extremely complex
and you're getting more verbose than the alternative which already works :)
@bronsa data readers and macros are equivalent approaches here, right?
kinda sorta not really
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
hah, not that fun
if it were a macro, other macros would see the unexpanded macro expression vs the fn expression for one
and that's a problem if your wrapping macro codewalks
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
wtf
this is all supposing that you're trying to use that macro exactly as you'd use #()
it's a similar thing that of course exists with fn
being a macro
but if you're asking if they're equivalent, then this does exist
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
what you're saying is kinda the reason why @john reader macros don't work
Right, cause you could plug yourself before it is applied then correct?
yes
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
yes
it's what I was suggesting with #f/% (foo #v/% :foo)
Haha ya I see. I think your choice of namespace confused me π
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
yours requires code walking of the body though, the tagged reader version wouldn't
Ah ya, I see that's true
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
Then you have to reimplement #()
s logic for %numbers, which I was trying to flow through
yeah
it's really not worth it :)
fun to think about though
Ya... but what if your key is 1 π
I was thinking :4:ans/akey... Another illegal form :)
Another plus of (fn), which I've started to use over #(), is you can name the anonymous fn, which helps a lot when exceptions are thrown