This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2018-04-01
Channels
- # beginners (28)
- # cider (39)
- # cljs-dev (2)
- # cljsjs (1)
- # clojure (67)
- # clojure-conj (1)
- # clojure-dev (103)
- # clojure-gamedev (3)
- # clojure-uk (2)
- # clojurescript (46)
- # data-science (7)
- # datascript (1)
- # fulcro (5)
- # lein-figwheel (2)
- # mount (2)
- # off-topic (55)
- # portkey (7)
- # protorepl (11)
- # re-frame (45)
- # reagent (21)
- # shadow-cljs (34)
- # tools-deps (3)
- # vim (8)
- # yada (1)
One of the most useful features in Clojure for me would be nested function literals. I really can't see a problem with them design-wise as in they wouldn't affect my ability to reason about code. Maybe that's the result of working with nested lambdas in Scheme, but is it just an issue of one step more work in the implementation or has an argument been made as for why they should be prohibited in the language?
@sophiago You can't nest anonymous function literals but you can nest (fn [])
literals just fine
@bronsa it would obviously refer to the inner one unless Clojure is no longer lexically scoped. That example is no different than (fn [] (fn [x] x))
and similarly anyone should be able to tell which function x
binds to so I don't see the argument here. I find it hard to believe anyone doesn't use the same parameter names in different scopes of their Clojure code (or any other code for that matter).
@sophiago the difference is that you can do (fn [x] (fn [y]
but you can't use a different %
I think it starts to get really confusing around %1 etc. Though. If you're using lexical scope, depending on how you're called, %2 could refer to the parent.
As mentioned I don't find it significantly harder to read, in fact I constantly do this and don't realize it until the compiler tells me. What I do find confusing is when bindings are used anywhere other than the level where they're defined. I see this comparison more to how literals would ambiguously desugar to named bindings, i.e. the implementation, rather than them being a confusing language feature.
making this work requires deleting a check and 2 lines of code. this not working is an explicit design decision
That's what I figured. My guess for the reason would not be that shadowing bindings is a good thing, but rather that it's a bad thing people will do anyway that opens up several avenues as to how to interpret the bindings in literals.
it would introduce quite a bit of cognitive load when reading code that uses it (writing it might feel natural at first, sure), for no particular extra conciseness
I guess I just didn't see this as such a contentious issue seeing as: a) Clojure is the only dialect of Lisp where nested lambdas aren't very commonly used, and b) the literal syntax is a purely syntactic (yet very helpful) so I wouldn't think it would represent a verdict on the former. It seems clear from this conversation I had the wrong impression and people do have opinions about the first part of that.
I'm just saying something like #(map #(inc %) %)
is much more legible to me than (fn [x] (map (fn [x] (inc x)) x))
. In fact, I had to count parens just to type the latter without syntax highlighting.
but it's also much more ambiguous and will definitely make it harder to read in 2 weeks time
I'm saying one or the other is basically unavoidable for me and the latter is the less legible one.
And it seems we agree one or the other is necessary and it's just a matter of syntax.
the latter is artificially constructed to be less readable and many wouldn't agree with you anyway
tons of people prefer to use (fn [x] ..)
over #()
even when the sugar would be possible to use
the short answer is I've given you the rationale as to why it's not allowed and I very much doubt rich would change his mind on it
I don't quite see the rationale. nested lambdas are fine, shadowing bindings are fine, yet using literal syntax for either is bad. That doesn't make sense to me. I do think it's an issue of trying to discourage the first two, which is fine even if I disagree with it.
if you find hard to read nested lambdas I'd suggest just lifting the inner one to a let binding
nested #()
is objectively harder to read because it requires mentally keeping track of which #()
you're in to know what %
refers to; nested fn
s do not have that cognitive load
"Objectively harder to read" doesn't make much sense. I understand I'm in the minority here, but it's very much a matter of opinion.
more than one and the cognitive load to keep track of which lambda % refers to becomes more than the 3 keystrokes you save from using fn
@bronsa you got to the point about it being explicit. The problem with nested lambdas is generally linking where variables are used to where they're bound. For me, making them implicit eliminates that scoping issue.
i get where you're coming from, but that only applies to this particular case, with your particular reading style. in general, nested #()
would be harder to read
@sophiago meh that argument doesn't make any sense to me, so let's just agree to disagree on that :)
When you mention lifting them into a let
that literally desugars into the case I find confusing 😛
again, you're in a very small minority with this opinion fwiw, in my ~8 years of doing clojure I've never heard anybody argue that explicit binding is more confusing than implicit locals to them
we’re not going to do this, so don’t make a ticket.
https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/LispReader.java#L854-L855
it's easier to make a ticket and see what the core team thinks about it than argue endlessly about it
Well, as I pointed out off the bat I think I'd be in the vast majority if you included all Lisps. And the issue of function syntax is one where Clojure has made great syntactic improvements, but maintains the same semantics. On that note, I'm definitely not making a ticket because I know how those things go...I was just looking for the rationale and I do think you provided that for me after a while: there are multiple ways one could interpret nested implicit bindings. That's a general Rich peeve in my experience. My counterargument, were I ever to design my own Lisp (which I really hope I never choose to), is that picking one interpretation encourages better code style, i.e. using bindings inside deeper scopes as little as possible.
>Well, as I pointed out off the bat I think I'd be in the vast majority if you included all Lisps
but you keep making this point and it doesn't make any sense to me -- #()
is shorthand syntax and doesn't dictate at all whether nested lambdas are to be used
>On that note, I'm definitely not making a ticket because I know how those things go. why? it literally takes 2 minutes
and trust me, I think on this issue it would take alex less than 2 minutes to respond to it ;)
I don't think you see my point, but I think I understand yours so I'm happy with that.
please don't assume what I understand or not, that's a tiny bit insulting, I've already said I get why you're asking this & I've wanted it myself from time to time, I just don't think it's a good idea at all
My bad. Fwiw, I was referring to the issue of matching variables to where they're bound. You didn't seem to relate to that.
sure but my "no" is not authoritative, if you care and wish to see whether the core team would accept it, a ticket is the only way
Conclusion: I think I'm going to write a blog post where I CPS a function with nested lambdas to demonstrate why the binding site issue is obnoxious and then show an example with one possible interpretation of nested literals that disallows that usage.
I would really like to read this blog post. I'm not following too well so this would be good reading. Also I've seen you chat in slack a bit and you always seem to be doing interesting things so it's just another reason I'd like to read it.
I already have one draft of a technical post that's been sitting around for about six months now.
This one at least I think I know how to do, although there's an actual theoretical point that first made me think about this issue and I'm not sure how well I can explain that. I'm writing the person who relayed it to me rn, actually. Something about the global nature of lambda binders making them undecidable to certain term rewriting systems.
Essentially the first part of this answer: https://cstheory.stackexchange.com/questions/36090/how-is-lambda-calculus-a-specific-type-of-term-writing-system
(FWIW, I've never liked the #(..)
anonymous function syntax at all -- I almost never use it, and prefer (fn [x] ..)
but when I first started doing Clojure, I did use the #(..)
form and I did occasionally want to nest them)
I while ago I had an emacs thing that turned the #
into a lambda which made it a bit more bearable but now I just tend not to use it