<p>How can multiline lambdas be designed in indent based languages?</p>
<p>In Go, language with curly brackets, I will write multiline lambdas like this:</p>
<pre class="lang-go prettyprint-override"><code>. . .
func () {
    ...;
    ...;
    ...;
}
</code></pre>
<p>But how can multiline lambdas be written in indent based language?</p>

<syntax><language-design><indentation><lambda>
How can multiline lambdas be designed in indent based languages?
CC BY-SA 4.0
2023-06-05T09:53:07.937Z
Is your question focused on implementing the lambdas or merely the design of them?
FireTheLost
CC BY-SA 4.0
2023-06-05T09:54:24.790Z
@FireTheLost designing.
чистов_n
<p>In my language Déjà Vu, anonymous functions were written like this:</p>
<pre><code>labda x:
    do-something-with x
    another-line
</code></pre>
<p>They need to take up the entire line. It being a stack-based language, using a lambda (spelled <code>labda</code> in Déjà Vu because of reasons) as an argument requires you to have the function on the next line after that:</p>
<pre><code># Python: takes_callback(lambda: ...)
# Déjà Vu:

labda:
    ...
takes-callback
</code></pre>
<p>Returning them can be done implicitly:</p>
<pre><code># Python:
# def counter():
#     i = 0
#     def _anonymous():
#         nonlocal i
#         current = i
#         i += 1
#         return i
#     return _anonymous

# Déjà Vu:
func counter:
    local :i 0
    labda:
        i
        set :i + 1 i
</code></pre>

CC BY-SA 4.0
2023-06-06T11:53:54.297Z
2023-06-06T11:53:54.297Z
Jasmijn
<p>Like in F#:</p>
<pre class="lang-ocaml prettyprint-override"><code>List.map (fun x -&gt;
    printfn &quot;Got %d&quot; x;
    x*2) [1..10]
</code></pre>
<p>or</p>
<pre class="lang-ocaml prettyprint-override"><code>List.map
    (fun x -&gt;
        printfn &quot;Got %d&quot; x;
        x*2)
    [1..10]
</code></pre>
<p>Again, let me repeat @kaya3 and point out that this is only relevant to several <em>statements</em> inside a lambda-function. <em>Expressions</em> are not sensitive to whitespace, newlines and indentation in most programming languages, so one can format a single expression in any way they like.</p>
<p>As for the statements... In an expression-based language you're supposed to be able to wrap anything in parenthesis, which basically solves the problem &quot;automatically&quot;.</p>

CC BY-SA 4.0
2023-06-06T11:40:59.100Z
2023-06-06T11:40:59.100Z
Alex Chichigin
<p>In &quot;indent-based languages&quot; such as Python, indentation is needed at the statement level to determine the nesting of blocks. On the other hand, the nesting of expressions is defined without reference to indentation or any other whitespace, so there is no need to enforce any rules about indentation at the expression level, and arbitrary indentation is allowed when splitting expressions across multiple lines, either using backslash <code>\</code> to ignore a line ending or writing inside brackets.</p>
<p>Both of these can be implemented directly in the lexer:</p>
<ul>
<li>Recognise <code>\\\n\s*</code> (i.e. a literal backslash followed by a newline followed by any whitespace) as a whitespace token. It will then be ignored when parsing the expression, just like any other whitespace, and it won't get processed by the algorithm which tracks indentation at newlines because the newline character is part of the token.</li>
<li>The lexer counts the nesting level of brackets (increment a counter on each <code>(</code>, <code>[</code> or <code>{</code> token, decrement on <code>)</code>, <code>]</code> or <code>}</code>), and the algorithm which tracks indentation and inserts <code>INDENT</code> and <code>DEDENT</code> tokens ignores newlines when the counter is greater than zero. (A simple counter is enough, because if they don't match up correctly the parser will notice and report the error.)</li>
</ul>
<p>This is all enough for lambda functions to span multiple lines when the body of the lambda is an expression ─ the user just has to ensure that either the lambda or its body is wrapped in parentheses, then it can span multiple lines. (Of course, this applies to every expression, not just lambdas.)</p>
<hr />
<p>Lambda functions whose body is a block of statements, on the other hand, are considerably more difficult. Python's solution is to just not allow such lambdas; instead, you are supposed to declare a named function and then you can refer to it by name in an expression. There's a lot to be said for this approach ─ it means that expressions never contain statements, making the language grammatically simpler. It also results in more useful stack traces on runtime errors, because the lines have meaningful names instead of just saying <code>&lt;lambda&gt;</code>.</p>
<p>On the other hand, if you do want statements inside expressions, there's probably no way around having some kind of delimiters (like the braces in Rust), otherwise there will be nothing but whitespace separating the end of the last statement from the continuation of the expression that the lambda occurred in. Here's what it looks like without delimiters:</p>
<pre class="lang-python prettyprint-override"><code>foo(lambda:
    print('hello world')
    do_something_else()
, 3.5)
</code></pre>
<p>Without block delimiters, the comma is <em>required</em> to be in this ugly position at the start of the line, and moreover the indentation of the expression matters on that line ─ it must be less indented than the lambda body ─ when indentation inside expressions doesn't matter anywhere else. These problems both disappear when you require delimiters; if you don't like braces in an indentation-based language, you could use Lua-style <code>do</code>/<code>end</code> keywords instead:</p>
<pre class="lang-python prettyprint-override"><code>foo(lambda: do
    print('hello world')
    do_something_else()
end, 3.5)
</code></pre>
<p>So I think you have to just bite the bullet and use block delimiters, at least for statements that occur inside expressions. You don't need to require delimiters on every block, and you can still enforce the language's indentation rules between those delimiters; they're not there to define the nesting level of statements, they're there to separate the statements from the expression which contains them.</p>
<p>Implementation-wise, the lexer now needs to maintain a stack of those counters, so that when you see a <code>do</code> token it pushes a 0 to the stack, and when you see <code>end</code> it pops the counter from the stack. (No need to check that it's zero; the parser will ensure correct nesting of brackets.) This way you effectively &quot;reset&quot; the counter so that the lexer can insert the correct <code>INDENT</code> and <code>DEDENT</code> tokens inside the lambda body, while still being able to restore the old counter for the nesting level of the expression that the lambda occurs in.</p>

CC BY-SA 4.0
2023-06-05T10:44:40.473Z
2023-06-05T11:26:53.627Z
2023-06-05T11:26:53.627Z
kaya3
CC BY-SA 4.0
2024-02-21T23:04:09.330Z
There are other indentation-sensitive languages that do care about indentation of nested sub-expressions. The key issue with Python is that it disregards all indentation that's inside parenthesis or brackets.
hugomg
<h2>Koka's trailing lambda syntax</h2>
<p><a href="https://koka-lang.github.io/koka/doc/index.html" rel="nofollow noreferrer">The Koka language</a> has an interesting feature called <a href="https://koka-lang.github.io/koka/doc/book.html#sec-anon" rel="nofollow noreferrer"><em>trailing lambdas</em></a>: when the last argument of a function is a lambda, it can be written outside of the parentheses.</p>
<p>Let's take a look at this example (based on the example in <a href="https://langdev.stackexchange.com/a/1396/194">kaya3's answer</a>):</p>
<pre><code>foo(3.5, fn()
    println('hello world')
    do_something_else()
)
</code></pre>
<p>The final right-parenthesis is ugly. Koka allows us to write this instead:</p>
<pre><code>foo(3.5) fn()
    println('hello world')
    do_something_else()
</code></pre>
<p>Since this lambda doesn't take any arguments, the <code>fn()</code> can be omitted:</p>
<pre><code>foo(3.5)
    println('hello world')
    do_something_else()
</code></pre>
<p>I think this is an elegant syntax sugar. There are some other languages that have similar feature, such as <a href="https://docs.julialang.org/en/v1/manual/functions/#Do-Block-Syntax-for-Function-Arguments" rel="nofollow noreferrer">Julia</a> and <a href="https://doc.rust-lang.org/0.4/tutorial.html#do-syntax" rel="nofollow noreferrer">some early version of Rust</a>, but those languages are not indent based.</p>

CC BY-SA 4.0
2023-07-05T08:05:54.423Z
2023-07-05T08:05:54.423Z
alephalpha
CC BY-SA 4.0
2023-07-05T14:54:23.647Z
Swift and Kotlin also have trailing closure syntaxes that look like `foo(3.5) { print("hello world"); doSomethingElse() }`. You can even omit the explicit parameter if there is one, as it gets an automatic default name (`$0` in Swift and `it` in Kotlin).
Bbrk24
CC BY-SA 4.0
2024-11-18T00:13:17.633Z
Isn't Ruby blocks syntax similar? A difference is that you have to check in the function body that a closure/function/callable has been passed
Vincenzo Maggio
# How can multiline lambdas be designed in indent based languages?
Posted by: username_0 on 2023-06-05
How can multiline lambdas be designed in indent based languages?
In Go, language with curly brackets, I will write multiline lambdas like this:
. . .
func () {
    ...;
    ...;
    ...;
}

But how can multiline lambdas be written in indent based language?
**Tags:** syntax language-design indentation lambda
> **Comments:**
> **username_1**: Is your question focused on implementing the lambdas or merely the design of them?  
> **username_0**: @FireTheLost designing.  
---
## 4 answers
### Answer by username_4 on 2023-06-05. Score: 9 | Accepted answer
In "indent-based languages" such as Python, indentation is needed at the statement level to determine the nesting of blocks. On the other hand, the nesting of expressions is defined without reference to indentation or any other whitespace, so there is no need to enforce any rules about indentation at the expression level, and arbitrary indentation is allowed when splitting expressions across multiple lines, either using backslash \ to ignore a line ending or writing inside brackets.
Both of these can be implemented directly in the lexer:

Recognise \\\n\s* (i.e. a literal backslash followed by a newline followed by any whitespace) as a whitespace token. It will then be ignored when parsing the expression, just like any other whitespace, and it won't get processed by the algorithm which tracks indentation at newlines because the newline character is part of the token.
The lexer counts the nesting level of brackets (increment a counter on each (, [ or { token, decrement on ), ] or }), and the algorithm which tracks indentation and inserts INDENT and DEDENT tokens ignores newlines when the counter is greater than zero. (A simple counter is enough, because if they don't match up correctly the parser will notice and report the error.)

This is all enough for lambda functions to span multiple lines when the body of the lambda is an expression ─ the user just has to ensure that either the lambda or its body is wrapped in parentheses, then it can span multiple lines. (Of course, this applies to every expression, not just lambdas.)

Lambda functions whose body is a block of statements, on the other hand, are considerably more difficult. Python's solution is to just not allow such lambdas; instead, you are supposed to declare a named function and then you can refer to it by name in an expression. There's a lot to be said for this approach ─ it means that expressions never contain statements, making the language grammatically simpler. It also results in more useful stack traces on runtime errors, because the lines have meaningful names instead of just saying .
On the other hand, if you do want statements inside expressions, there's probably no way around having some kind of delimiters (like the braces in Rust), otherwise there will be nothing but whitespace separating the end of the last statement from the continuation of the expression that the lambda occurred in. Here's what it looks like without delimiters:
foo(lambda:
    print('hello world')
    do_something_else()
, 3.5)

Without block delimiters, the comma is required to be in this ugly position at the start of the line, and moreover the indentation of the expression matters on that line ─ it must be less indented than the lambda body ─ when indentation inside expressions doesn't matter anywhere else. These problems both disappear when you require delimiters; if you don't like braces in an indentation-based language, you could use Lua-style do/end keywords instead:
foo(lambda: do
    print('hello world')
    do_something_else()
end, 3.5)

So I think you have to just bite the bullet and use block delimiters, at least for statements that occur inside expressions. You don't need to require delimiters on every block, and you can still enforce the language's indentation rules between those delimiters; they're not there to define the nesting level of statements, they're there to separate the statements from the expression which contains them.
Implementation-wise, the lexer now needs to maintain a stack of those counters, so that when you see a do token it pushes a 0 to the stack, and when you see end it pops the counter from the stack. (No need to check that it's zero; the parser will ensure correct nesting of brackets.) This way you effectively "reset" the counter so that the lexer can insert the correct INDENT and DEDENT tokens inside the lambda body, while still being able to restore the old counter for the nesting level of the expression that the lambda occurs in.
> **Comments:**
> **username_5** (2024-02-21): There are other indentation-sensitive languages that do care about indentation of nested sub-expressions. The key issue with Python is that it disregards all indentation that's inside parenthesis or brackets.  
---
### Answer by username_6 on 2023-07-05. Score: 4
Koka's trailing lambda syntax
The Koka language has an interesting feature called trailing lambdas: when the last argument of a function is a lambda, it can be written outside of the parentheses.
Let's take a look at this example (based on the example in kaya3's answer):
foo(3.5, fn()
    println('hello world')
    do_something_else()
)

The final right-parenthesis is ugly. Koka allows us to write this instead:
foo(3.5) fn()
    println('hello world')
    do_something_else()

Since this lambda doesn't take any arguments, the fn() can be omitted:
foo(3.5)
    println('hello world')
    do_something_else()

I think this is an elegant syntax sugar. There are some other languages that have similar feature, such as Julia and some early version of Rust, but those languages are not indent based.
> **Comments:**
> **username_7** (2023-07-05): Swift and Kotlin also have trailing closure syntaxes that look like `foo(3.5) { print("hello world"); doSomethingElse() }`. You can even omit the explicit parameter if there is one, as it gets an automatic default name (`$0` in Swift and `it` in Kotlin).  
> **username_8** (2024-11-18): Isn't Ruby blocks syntax similar? A difference is that you have to check in the function body that a closure/function/callable has been passed  
---
### Answer by username_3 on 2023-06-06. Score: 3
Like in F#:
List.map (fun x ->
    printfn "Got %d" x;
    x*2) [1..10]

or
List.map
    (fun x ->
        printfn "Got %d" x;
        x*2)
    [1..10]

Again, let me repeat @kaya3 and point out that this is only relevant to several statements inside a lambda-function. Expressions are not sensitive to whitespace, newlines and indentation in most programming languages, so one can format a single expression in any way they like.
As for the statements... In an expression-based language you're supposed to be able to wrap anything in parenthesis, which basically solves the problem "automatically".
---
### Answer by username_2 on 2023-06-06. Score: 1
In my language Déjà Vu, anonymous functions were written like this:
labda x:
    do-something-with x
    another-line

They need to take up the entire line. It being a stack-based language, using a lambda (spelled labda in Déjà Vu because of reasons) as an argument requires you to have the function on the next line after that:
# Python: takes_callback(lambda: ...)
# Déjà Vu:

labda:
    ...
takes-callback

Returning them can be done implicitly:
# Python:
# def counter():
#     i = 0
#     def _anonymous():
#         nonlocal i
#         current = i
#         i += 1
#         return i
#     return _anonymous

# Déjà Vu:
func counter:
    local :i 0
    labda:
        i
        set :i + 1 i
---
