Quantcast
Channel: Possible ways to determine reach of implicit lambda expressions - Programming Language Design and Implementation Stack Exchange
Viewing all articles
Browse latest Browse all 2

Possible ways to determine reach of implicit lambda expressions

$
0
0

When defining an inline function, even the shortest way to do so usually requires naming its argument (unless you're going for the point-free style and you have an expression that returns a function).

// JavaScriptarr.map(x => x + 1)// Rustiter.map(|x| x + 1)// Haskellmap (\x -> x + 1) arr

In maths, this is called “arrow notation”, e.g. $x \mapsto x + 1$.

This is already short and sweet, especially when you compare it to things like function (x) { return x + 1 }. But can we do better?

An arguably less common way of defining functions in maths is using the “dot notation”, which looks like this: $(\;\cdot\;) + 1$. Interpunct (the middle dot) is a common choice, but it's not a necessary one -- any symbol used rarely enough would work. The idea is to avoid giving any specific “common” name to a function argument, and to instead pick an esoteric one and stick with it.

So I thought why not try this in a language.

arr.map(# + 1)

Researching, I've found out that Scala supports exactly this using _, and that Kotlin has something very similar using a reserved variable name it.

// Scalalist.map(_ + 1)
// Kotlinlist.map { it + 1 }

A glaring question that comes up is how do you know how far out the implicit lambda reaches, i.e. do the examples above desugar into x -> list.map(x + 1) or list.map(x -> x + 1).

In Scala, desugaring happens up the syntax tree until the first pair of parentheses () is reached. In our case, that's the parenthesis for the function call, so the above snippet is what we intuitively expect.

In Kotlin, {} is used in general to denote lambdas. For example, you could rewrite the above as list.map { x -> x + 1 }. I'm not sure on the details, but apparently you can avoid writing the usual () parenthesis for function call if a lambda is the only argument to a function. So the full syntax sugar chain is list.map({ x -> x + 1 }) as list.map { x -> x + 1 } as list.map { it + 1 }. So the scope is determined just like always: using {}.

This shows two ways to determine the reach of an implicit lambda.

Scala becomes tricky as soon as you have a more complex expression, where you want to use parentheses for grouping. Something as simple as x -> 2 * (x + 1) cannot be expressed using 2 * (_ + 1) because it would inwind into 2 * (x -> x + 1), not x -> 2 * (x + 1). Kotlin's approach seems better as there's a pre-established delimiter pair so this kind of thing doesn't happen. This also plays nicely with tiny additional shortcuts you can take like collapsing ({}) into just {}. The price is the additional {} you have to introduce everywhere, so instead of val f = x -> x + 1, you need val f = { x -> x + 1 } (which is not necessarily bad, but since we're talking about a first world problem anyway, it's worth pointing it out as a con).

What are other possible ways to determine the reach of a lambda expression, found in other programming languages (including non-mainstream ones)? What are their pros and cons?


Viewing all articles
Browse latest Browse all 2

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>