Fork me on GitHub

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


@sophiago what would d % in #(#(%)) refer to? the inner or outer one?


you still can use fn instead of the literal syntax 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 %


you'd have to #(let [x %] #(..


which is longer than the fn version


so what's the point


and it's also significantly harder to read


@bronsa it would help where the inner lexical scope doesn't need to share.


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


it's not going away


That was my original question: was this an explicit design decision?


i think you're in the minority in finding this more useful than error-prone


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.


nested lambdas in clojure are used all the time


and literal syntax doesn't represent a verdict of -a


i don't undertand how you jumped to that conclusion from what i've said


we're discussing syntax


i've never talked about anything else


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.


#(map inc %)


it might be more legible now


You can easily imagine a better example.


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


So we're back into usage?


but ok we can keep arguing subjective preferences all we want


Yes, not so helpful for either of us I'm sure.


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


for good reasons IMO


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


@sophiago no that's just not true


it's nonsense to say that clojure discourages nested lambdas


Adding more bindings contradicts most of the arguments made here.


i'm sorry but that just doesn't make much sense


bindings are explicit


% is implicit


nested #() is objectively harder to read because it requires mentally keeping track of which #() you're in to know what % refers to; nested fns do not have that cognitive load


the implicity of %'s binding is the whole point


one level is fine


"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


readability is not a matter of opinion


you really don't see much code that does (fn [x] (fn [x] for the same reason


you do (fn [x] (fn [y]


@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


I understand where you're coming from, I myself wish for nested #() from time to time


there are certainly few cases where it would be convenient


but it would be just bad design


@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


but hey, if you feel strongly about this make a ticket

Alex Miller (Clojure team)22:04:30

we’re not going to do this, so don’t make a ticket.


and even a patch, as I said it's only a matter of deleting 2 lines


those two specifically


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


in fact clojure uses them all the time


just look at how transducers are implemented


>On that note, I'm definitely not making a ticket because I know how those things go. why? it literally takes 2 minutes


Not 2 minutes for the people responding to it.


it's not taking me 2 minutes to answer you either


and trust me, I think on this issue it would take alex less than 2 minutes to respond to it ;)


My understanding is that you volunteered in an informal setting 🙂


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


no hard feelings


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.


no worries


Eliminates the need to open a ticket.


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.


Oh no, this means I actually have to do 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.

Alex Miller (Clojure team)22:04:01

we’re not going to do this. don’t make a ticket.

👍 4

or there you go


(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


apart from in quick snippets


Tbh it's a breath of fresh air for me vs. other Lisp code. let was essentially invented because of that, although Clojure's let is like let* (at least in Scheme, I think CL too but not sure) so much more useful in that regard.


I hope we've at least determined it's subjective though 😛