11
$\begingroup$

A lot of languages have anonymous functions, also known as lambda functions. Anonymous, in-line, functions that can be used inside expressions for things like mapping and filtering.

What are common syntax options for lambdas, and what are their pros and cons?

$\endgroup$
6
  • 2
    $\begingroup$ Closures are not the same thing as lambdas at all $\endgroup$
    – mousetail
    May 18, 2023 at 17:20
  • 1
    $\begingroup$ A closure is any function that uses variables from a outer scope, a lambda is just a function without a name $\endgroup$
    – mousetail
    May 18, 2023 at 17:21
  • $\begingroup$ @mousetail fixed, whoops $\endgroup$
    – Ginger
    May 18, 2023 at 17:26
  • $\begingroup$ You changed it to the wrong thing, the text is all about lambdas not closures now. $\endgroup$
    – mousetail
    May 18, 2023 at 17:27
  • $\begingroup$ If you don't want explicit control over closures, I'd say reusing the function syntax (except allowing to omit the name) is a nice approach. One less grammar item for the programmer to remember $\endgroup$
    – abel1502
    May 18, 2023 at 19:22

14 Answers 14

9
$\begingroup$

Scala Style

Welcome to the kitchen sink of lambda syntax! All of the following are valid in Scala and produce an anonymous function which adds one to its argument.

x => x + 1
(x: Int) => x + 1
{ x => x + 1 }
{ x: Int => x + 1 }
_ + 1
$\endgroup$
1
  • 1
    $\begingroup$ There's also 1.+ and 1.+(_), since + is simply a method on Ints $\endgroup$
    – user
    Jul 21, 2023 at 17:57
9
$\begingroup$

Swift-style

If you haven’t seen it yet, let me introduce you to https://fuckingclosuresyntax.com.

More seriously, the full syntax is this:

{ [byValueCapture] (argName: ArgType) -> ReturnType in body }

…but most of that is optional.

A few things to note:

  • Only by-value captures are specified; by-reference captures are implicit.
  • Unlike in C++, the square brackets are omitted when there are no captures.
  • The argument types are optional if the type checker can infer them.
  • The return type and -> are optional if the type checker can infer it, but must be together; you can’t have one without the other.
  • If the argument types and return type are omitted, the parentheses around the argument list are optional.
  • If the argument types and return type are omitted, the arguments do not need to be named; their names default to $0 for the first argument, $1 for the second argument, and so on.
  • If there’s nothing before the word in (it’s all implied or unnecessary), that word must be omitted, too.

There’s also a shorthand for property access known as a key-path expression; these expressions are equivalent1:

{ (x: MyClass) in x.foo }
\MyClass.foo
\.foo // when the argument type can be inferred

This can lead to some nice abbreviations, such as:

let names = people.map(\.name)

Key-path expressions even support optional chaining, so \.output.first?.substring is valid and equivalent to { $0.output.first?.substring }.

Of course, the wide variation in everything causes some confusion, hence the existence of the aforementioned website.

1 Save for compiler oddities.

$\endgroup$
1
  • 1
    $\begingroup$ SFW mirror of the linked site (you know which one) for anyone else reading this on their work machine lol goshdarnclosuresyntax.com $\endgroup$ Aug 2, 2023 at 12:16
8
$\begingroup$

Haskell style

\a b -> expr

Advantages:

  • Concise: uses symbols instead of long keywords
  • The \ symbol is a mnemonic for λ
  • Can be parsed unambiguously without requiring any brackets (but see below)

Disadvantages:

  • Might not be immediately clear to a language learner that this is a lambda function syntax
  • Usually must be wrapped in parentheses, because juxtaposing identifiers in Haskell represents function application (if map \a b -> c d were legal syntax, it would parse as map (\a b -> (c d)) rather than map (\a b -> c) d)
    • A language in which juxtaposed identifiers cannot be part of the same expression wouldn't have this problem
  • Function body must be a single expression (not a problem in Haskell, but might be less desirable in an imperative language)
$\endgroup$
2
  • 1
    $\begingroup$ map \a b -> c isn't even valid syntax, the parser requires parentheses. You can use it with $ though $\endgroup$
    – naffetS
    May 18, 2023 at 21:46
  • 1
    $\begingroup$ map \a b -> c d is valid syntax (and parses as map (\a b -> c d) with GHC, if you turn on the BlockArguments language extension. $\endgroup$
    – Cactus
    May 22, 2023 at 13:33
8
$\begingroup$

JS style

(parameters) => body

Very concise. You can also have multiple statements if you wrap it in braces.

Python style

lambda parameters: body

A bit verbose with the keyword lambda, and also only allows one expression.

Ruby style

do |parameters| body end

Allows expressions or statements, but still a bit verbose.

Alternatively:

{ |parameters| body }

More concise, and also allows expressions or statements.

Perl style

sub {my ($parameters) = @_; body};

Once again, allows expressions or statements, but it's even longer than Ruby.

C++ style

[&](parameters) {body}

Concise and allows multiple statements.

$\endgroup$
3
  • $\begingroup$ The braces in Ruby also allow multiple statements. $\endgroup$
    – xigoi
    Aug 2, 2023 at 15:03
  • $\begingroup$ @xigoi thanks, updated. $\endgroup$
    – The Thonnu
    Aug 2, 2023 at 16:09
  • $\begingroup$ Haskell style: \parameters -> expression (e.g. \x y -> x + y) $\endgroup$
    – md2perpe
    Aug 3, 2023 at 10:47
7
$\begingroup$

APL style

APLs (APL, J, K, BQN) have lambdas that are are unique in having a pre-defined set of argument names. E.g. APL uses prefix/infix lambdas with for the left argument (optional) and for the right argument. Here, we define a root function (* is exponentiation and prefix ÷ is reciprocal):

      2 {⍵*÷⍺} 100
10

A lambda can be ambivalently prefix/infix by either checking if a left argument is present, or by using the convention that an attempted assignment to is ignored if already has a value ( is the statement separator):

      Root←{⍺←2 ⋄ ⍵*÷⍺}
      Root 100
10
      3 Root 8
2

As with all APL functions, if many arguments are needed, they can be given as an array, one element per argument, and destructured inside the function. The destructuring expression then functions as a type of header ( is the range function):

      {(start count step)←⍵ ⋄ start+step×⍳count} 10 5 2
10 12 14 16 18

Some APLs (K, BQN) have an explicit header syntax too, and BQN allows multiple headers for various uses, but this is the gist of it.

$\endgroup$
6
$\begingroup$

Rust-style

|parameters| -> returntype {body}

This is a rather unorthodox form, as I think only Rust uses it. However, it may be less prone to syntactic ambiguity by virtue of not using curved brackets.

$\endgroup$
3
  • 2
    $\begingroup$ I believe other languages do too...I think Ruby does? It's definitely somewhat common tho $\endgroup$ May 18, 2023 at 18:28
  • $\begingroup$ It's inspired by Ruby, but not the same; Ruby has the parameters inside the braces. $\endgroup$
    – xigoi
    Aug 2, 2023 at 15:05
  • $\begingroup$ | parameters | appeared in Smalltalk, I think. See this answer. $\endgroup$
    – Pablo H
    Aug 11, 2023 at 16:30
6
$\begingroup$

Arrow style

(args) -> body

This is subject to a lot of variation:

  • Single vs. double arrow (-> vs. =>)
  • Args list styles (bare identifiers allowed for single arg lambdas, or require parens? destructuring? etc.)
  • Body may have to be wrapped in {}s, or single expressions may be allowed without those
$\endgroup$
6
$\begingroup$

ML style:

one arg
fun x -> x

multiple arg
fun f xs -> List.map f xs
==
fun f -> fun xs -> List.map f xs
$\endgroup$
6
$\begingroup$

In Clojure:

(fn [a b] (+ a b))

Alternative syntax (uses indexing like %1 to access params):

#(+ %1 %2)

body can be multiple expressions or statements.

Similarly, in Common Lisp:

(lambda (a b) (+ a b))
$\endgroup$
0
5
$\begingroup$

PHP Style

fn (parameter1, parameter2) => value

It's the same as Javascript style except it also adds a required fn keyword before. Requiring a keyword makes things a bit more explicit which is nice but it's also more verbose.

There is no benefit of consistency because normal functions are defined with function not fn.

As VLAZ pointed out there is also a long form:

$f = function ($b) use ($a) { return $a + $b; }

Which requires explicitly specifying what variables you want to capture

Java Style

(parameter1, parameter2) -> expression

It's the same as javascript except using -> instead of =>

Javascript Standard Style

function(argument_1, argument_2) { code }

JavaScript has 2 ways of defining closures. this is handled differently in both cases.

I like this one because of the symmetry of function name() {} for named functions and function() {} for anonymous functions. A shame about how this works or I'd prefer it over ()=>{}

$\endgroup$
1
  • 1
    $\begingroup$ In PHP when using function you also need to import/capture values from outer scope(s), which the short fn does automatically. E.g., $a = 1; $f = function ($b) use ($a) { return $a + $b; } is the long and explicit capturing syntax, while with an arrow function it's $a = 1; $g = fn($b) => $a + $b; $\endgroup$
    – VLAZ
    May 18, 2023 at 17:36
5
$\begingroup$

Kotlin style

The full syntax is

{ a: Foo, b: Bar -> body() }

The type annotations may be omitted, and if there is only one parameter, that can be omitted and replaced with the implicit parameter it:

{ it.bar() }
$\endgroup$
5
$\begingroup$

Blocks

Blocks are a clang extension to C enabled with the -fblocks flag, though they're more common in -- and therefore usually associated with -- Objective-C.

The type syntax is similar to function pointers: return_type(^)(arg_types). These are typically used in typedefs, such as

typedef int(^int_function_t)(int);

The full syntax for defining one is this:

^return_type (arg_type arg_name) {
  body();
}

However, the return type can usually be inferred from the body of the block. If the return type is omitted and the argument list is (void), that can be omitted, too. For example:

int (^square)(int) = ^(int x) { return x * x; };
int (^randomNumber)(void) = ^{ return 4; };

NB: the trailing semicolon after the closing brace is necessary to end the declaration statement, and is not part of the block.

Blocks are called with the same syntax as function pointers.

If you need to capture a variable by reference, you must mark it with the __block storage specifier. Otherwise, capture by value is implicit:

int x = 4;
__block int y = 4;

void (^printX)(void) = ^{ printf("%d\n", x); };
void (^printY)(void) = ^{ printf("%d\n", y); };

++x;
++y;

printX(); // prints '4'
printY(); // prints '5'
$\endgroup$
3
$\begingroup$

going back to the older ones:

LISP style

(lambda (params) body)

Smalltalk style

[| parameters | body ]
[ body ]     -- when there are no parameters
$\endgroup$
0
2
$\begingroup$

Go-style

Go uses the same function declaration syntax everywhere, and lambdas are just anonymous functions.

func(){ body }
func() returnType { body }
func(parameters) { body }
func(parameters) returnType { body }

These syntaxes are equivalent, all declare a function and bind it to FuncName:

func FuncName(){...}
var FuncName = func(){...}
FuncName := func(){...}

A common pattern is to use or call functions immediately after definitions, mainly for goroutines, defer, and initializing something with a complicated calculation:

value := func(a,b,c) returnType { ... }(x,y,z)
go func() { ... }
defer func() { ... }
$\endgroup$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .