What are the reasons behind Elixir's lambda syntax?

This is highly subjective but in my mind Haskell and Ruby/Rust has one of the most beautiful lambda syntax
\a b → a + b
and
|a, b| a + b

so if Elixir syntax is mostly form Ruby why they ditched one of the most beautiful things on Ruby and goes with this abomination monster:

&(&1 + &2)
??
:grin: :laughing: :sweat_smile:

PS: Please note, my intent is not to criticize; I’m genuinely interested in understanding the design choices behind this syntax. :smile:

edit/note: |a, b| a + b is just Rust syntax, I thought that it’s also Ruby, but while Ruby is similar it wraps it in labda { ... }

1 Like

I dont know the answer to your question but just a small note, this is not the lambda syntax.

The lambda syntax would be fn a, b -> a + b end

What you mention is the capture operator which is similar to method reference in java/kotlin

6 Likes

Yep, I know this syntax, and it’s okay—neither bad nor awesome. I was just curious about the other one and whether there’s a particular reason Elixir didn’t adopt the Ruby syntax. :open_mouth:

What you mention is the capture operator which is similar to method reference

Oh, my bad.

1 Like

Ruby now has collection.map { _1 } so clearly they were a little jealous of Elixir :wink:

But ya, highly subjective.

I actually prefer Elixir’s since you don’t have to invent a variable name, even if just one letter. It makes refactoring that much more smooth.

4 Likes

Would the ruby syntax support multiple function heads? I wouldn‘t expect there‘s a clear way on how that would be handled.

5 Likes

Oh, I didn’t know about this feature. Sorry for my ignorance.
Now it really make sense.

1 Like

Thanks for the explanation. I’m just starting to learn Elixir, so I’m still a beginner. Given this information, I have to admit that the lambda syntax in Elixir does make a lot of sense for its use-cases.

1 Like

Ruby lambda syntax is lambda { |a, b| a + b } which is far from pretty IMHO. You probably meant block syntax, but that is slightly different from lambdas, as you can have only one block, with that fancy pants syntax, per function call. So it is not fully apples to apples.

6 Likes

Most people use the shorthand -> a, b { a + b } syntax, which I think is quite pretty.

4 Likes

True, sorry I have bad info - thought that Rust get it from Ruby but that labda { ... } wrap is <not so good> :sweat_smile:

That’s interesting… I’ve never seen that before. It’s definitely an improvement! :sweat_smile:

…and if you want to capture a block then it’s proc { ... }, or Proc.new { ... }!

I do still have a softspot for Ruby, but I wish it would have stopped adding syntax a long time ago. I happened to switch fully over to Elixir just as field punning was introduced to Ruby. Just in the nick of time!

2 Likes

By default Rubocop (at least the last time I used it) makes you write it like this: ->(a, b) { a + b } and the arrow must touch the parens! But if it spans multiple lines, it wants you do write it as lambda do ... end.

1 Like

Exactly the same and it’s also one of my top 5 reasons to leave it behind. That, plus it being so hopelessly dynamic to the point that even Elixir looks strict like Rust when compared to it.

Ruby remains a very friendly to look at language (and to write in, until you have to do something serious at least; cue 1000 angry comments saying “BUT SO MUCH SERIOUS STUFF IS DONE IN RUBY, YOU ARE WRONG!” :sweat_smile:) but its runtime properties are a complete “nah I am good” factor for me.


To the OP @JsonKody, I too find Rust’s syntax one of the best, and it leaves room for adding types too. OCaml is nice too.

And btw I kind of agree with him, I don’t think that the capture syntax with &1 and &2 etc. adds something tangible to the language. :person_shrugging:

Though there are two kinds of capture syntaxes. This one I like a lot:

Enum.map(list_of_strings, &String.trim/1)

…and this one I avoid like the plague most of the time:

Enum.map(list_of_tuples, &elem(&1, 2))

To me it just looks confusing and even after 7.5y with Elixir I still find it an eye-sore that prevents me to scan code well and comprehend it quickly. No idea why but I never got used to it.

Finally, this SO thread is good: Why do we need a function "capture operator" in Elixir? - Stack Overflow

4 Likes

This one baffles me too, I’m not sure what the thought process behind it was. This syntax is barely readable in isolation, not to talk in situations where there are multiple chained calls like this one.

1 Like

Yeah, it ruins readability.

Not to bikeshed (except that I’m gonna) but I do agree the &elem(&1, 2) is a little jarring even though I do use it sometimes :grimacing: It’s a bit of a tough example for me to judge because I find elem/2 jarring :sweat_smile:

I do this kind of thing with abandon:

Enum.find(users, &(&1.name == "Bob"))
Enum.map(users, & &1.id)

I see users so &1 must be a user because even though naming is hard, it’s not that hard :upside_down_face:

Also, the odd time I need an identity function (almost always as a parameter default) I like & &1 as a nice little concise option, as in:

def foo(thing, mapper \\ & &1)

fn x -> x end just looks like someone made a mistake to me. Of course, the best option is likely &Function.indentity/1. You’re still using a capture there, but it’s the nice capture syntax and clear as day.

I do also do stuff like this:

update(socket, :users, &[user | &1])

I can’t explain it but that looks totally fine to me, again, so long as there is clear naming. It also certainly makes a difference if it’s used in a common pattern or not.

I work solo, though. I wouldn’t fight anyone too hard on any of this stuff in a team situation.

One thing I never use (anymore) is &2. &2 can heck off!

6 Likes

I have used Enum.reduce(enumerable, initial, &reducer(extraarg, &1, &2) numerous times when I don’t want to use a lambda for whatever reasons. Maybe I’ve gotten used to it and I don’t find it hard to read.

2 Likes

You are right, there are degrees. Your examples I too don’t find bad.

But you are also right that elem/2 is jarring. :smiley:

1 Like

That actually doesn’t look bad to me as you have it but I have never written something like that myself before where I extract the reducer with extra arg(s). In any event, I was mostly being silly—I grepped a recent codebase right after I posted to make sure I wasn’t lying about not ever using &2 :sweat_smile:—it’s more of an arbitrary line I draw for myself as & can definitely look janky to me in many cases. I imagine it’s one of those things that’s easier for teams to either say yes or no to.

2 Likes