Bläddra i källkod

Lambdas (#3848)

This document proposes a path forward to add lambdas to Carbon. It
further proposes augmenting function declarations to create a more
continuous syntax between the two categories of functions. In short,
both lambdas and function declarations will be introduced with the `fn`
keyword. The presence of a name distinguishes a function declaration
from a lambda expression, and the rest of the syntax applies to both
kinds. By providing a valid lambda syntax in Carbon, migration from from
C++ to Carbon will be made easier and more idiomatic. In C++, lambdas
are defined at their point of use and are often anonymous, meaning
replacing them solely with function declarations would create an
ergonomic burden compounded by the need for the migration tool to select
a name.

Associated discussion docs:

* [Lambdas Discussion
1](https://docs.google.com/document/d/1rZ9SXL4Voa3z20EQz4UgBMOZg8xc8xzKqA1ufPQdTao/)
* [Lambdas Discussion
2](https://docs.google.com/document/d/14K_YLjChWyyNv3wv5Mn7uLFHa0JZTc21v_WP8RzC8M4/)
* [Lambdas Discussion
3](https://docs.google.com/document/d/1VVOlRuPGt8GQpjsygMwH2B7Wd0mBsS3Qif8Ve2yhX_A/)
* [Lambdas Discussion
4](https://docs.google.com/document/d/1Sevhvjo06Bc6wTigNL1pK-mlF3IXvzmU1lI2X1W9OYA/)

---------

Co-authored-by: Chandler Carruth <chandlerc@gmail.com>
Co-authored-by: Richard Smith <richard@metafoo.co.uk>
CJ Johnson 1 år sedan
förälder
incheckning
864c832971
1 ändrade filer med 710 tillägg och 0 borttagningar
  1. 710 0
      proposals/p3848.md

+ 710 - 0
proposals/p3848.md

@@ -0,0 +1,710 @@
+# Lambdas
+
+<!--
+Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+Exceptions. See /LICENSE for license information.
+SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+-->
+
+[Pull request](https://github.com/carbon-language/carbon-lang/pull/3848)
+
+<!-- toc -->
+
+## Table of contents
+
+-   [Abstract](#abstract)
+-   [Syntax Overview](#syntax-overview)
+    -   [Syntax Defined](#syntax-defined)
+-   [Introducer](#introducer)
+-   [Positional Parameters](#positional-parameters)
+    -   [Positional Parameter Restrictions](#positional-parameter-restrictions)
+-   [Function Captures](#function-captures)
+    -   [Capture Modes](#capture-modes)
+    -   [Default Capture Mode](#default-capture-mode)
+-   [Function Fields](#function-fields)
+-   [Copy Semantics](#copy-semantics)
+-   [Self and Recursion](#self-and-recursion)
+-   [Rationale](#rationale)
+-   [Alternatives Considered](#alternatives-considered)
+    -   [Alternative Considered: Terse vs Elaborated](#alternative-considered-terse-vs-elaborated)
+    -   [Alternative Considered: Sigil](#alternative-considered-sigil)
+    -   [Alternative Considered: Additional Positional Parameter Restriction](#alternative-considered-additional-positional-parameter-restriction)
+    -   [Alternative Considered: Recursive Self](#alternative-considered-recursive-self)
+-   [Future Work](#future-work)
+    -   [Future Work: Reference Captures](#future-work-reference-captures)
+
+<!-- tocstop -->
+
+## Abstract
+
+This document proposes a path forward to add lambdas to Carbon. It further
+proposes augmenting function declarations to create a more continuous syntax
+between the two categories of functions. In short, both lambdas and function
+declarations will be introduced with the `fn` keyword. The presence of a name
+distinguishes a function declaration from a lambda expression, and the rest of
+the syntax applies to both kinds. By providing a valid lambda syntax in Carbon,
+migration from from C++ to Carbon will be made easier and more idiomatic. In
+C++, lambdas are defined at their point of use and are often anonymous, meaning
+replacing them solely with function declarations would create an ergonomic
+burden compounded by the need for the migration tool to select a name.
+
+Associated discussion docs:
+
+-   [Lambdas Discussion 1](https://docs.google.com/document/d/1rZ9SXL4Voa3z20EQz4UgBMOZg8xc8xzKqA1ufPQdTao/)
+-   [Lambdas Discussion 2](https://docs.google.com/document/d/14K_YLjChWyyNv3wv5Mn7uLFHa0JZTc21v_WP8RzC8M4/)
+-   [Lambdas Discussion 3](https://docs.google.com/document/d/1VVOlRuPGt8GQpjsygMwH2B7Wd0mBsS3Qif8Ve2yhX_A/)
+-   [Lambdas Discussion 4](https://docs.google.com/document/d/1Sevhvjo06Bc6wTigNL1pK-mlF3IXvzmU1lI2X1W9OYA/)
+
+# Background
+
+Refer to the following documentation about lambdas in other languages. What
+separates these three and makes them more analegous to Carbon's direction is the
+use of "captures" such that the lambda has state, a lifetime, etc.
+
+-   [Lambdas in C++](https://en.cppreference.com/w/cpp/language/lambda)
+-   [Closures in Swift](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/closures/)
+-   [Closures in Rust](https://doc.rust-lang.org/rust-by-example/fn/closures.html)
+
+## Syntax Overview
+
+**Proposal**: A largely continuous syntax between lambdas and function
+declarations.
+
+At a high level, lambdas and function declarations will look like the following.
+
+```
+// In a variable:
+let lambda: auto = fn => T.Make();
+// Equivalent in C++23:
+// const auto lambda = [] { return T::Make(); };
+
+// In a function call:
+Foo(10, 20, fn => T.Make());
+// Equivalent in C++23:
+// Foo(10, 20, [] { return T::Make(); });
+```
+
+```
+// In a variable:
+let lambda: auto = fn -> T { return T.Make(); };
+// Equivalent in C++23:
+// const auto lambda = [] -> T { return T::Make(); };
+
+// In a function call:
+PushBack(my_list, fn => T.Make());
+// Equivalent in C++23:
+// PushBack(my_list, [] { return T::Make(); });
+```
+
+```
+fn FunctionDeclaration => T.Make();
+// Equivalent in C++23:
+// auto FunctionDeclaration() { return T.Make(); }
+```
+
+```
+fn FunctionDeclaration -> T { return T.Make(); }
+// Equivalent in C++23:
+// auto FunctionDeclaration() -> T { return T::Make(); }
+```
+
+There are functions which return an expression, such that the return type is
+`auto`.
+
+```
+// In a variable:
+let lambda: auto = fn => T.Make();
+// Equivalent in C++23:
+// const auto lambda = [] { return T::Make(); };
+
+// In a function call:
+Foo(fn => T.Make());
+// Equivalent in C++23:
+// Foo([] { return T::Make(); });
+```
+
+```
+fn FunctionDeclaration => T.Make();
+// Equivalent in C++23:
+// auto FunctionDeclaration() { return T::Make(); }
+```
+
+And there are functions with an explicit return type that provide a body of
+statements.
+
+```
+// In a variable:
+let lambda: auto = fn -> T { return T.Make(); };
+// Equivalent in C++23:
+// const auto lambda = [] -> T { return T::Make(); };
+
+// In a function call:
+Foo(fn -> T { return T.Make(); })
+// Equivalent in C++23:
+// Foo([] -> T { return T::Make(); });
+```
+
+```
+fn FunctionDeclaration -> T { return T.Make(); }
+// Equivalent in C++23:
+// auto FunctionDeclaration() -> T { return T::Make(); }
+```
+
+There are even functions that provide a body of statements but no return value.
+
+```
+// In a variable:
+let lambda: auto = fn { Print(T.Make()); };
+// Equivalent in C++23:
+// const auto lambda = [] -> void { Print(T::Make()); };
+
+// In a function call:
+Foo(fn { Print(T.Make()); });
+// Equivalent in C++23:
+// Foo([] -> void { Print(T::Make()); });
+```
+
+```
+fn FunctionDeclaration { Print(T.Make()); }
+// Equivalent in C++23:
+// auto FunctionDeclaration() -> void { Print(T::Make()); }
+```
+
+Functions support [captures](#function-captures), [fields](#function-fields) and
+deduced parameters in the square brackets. In addition, `self: Self` or
+`addr self: Self*` can be added to the square brackets of function declarations
+that exist inside class or interface definitions.
+
+```
+fn Foo(x: i32) {
+  // In a variable:
+  let lambda: auto = fn [var x, var y: i32 = 0] { Print(++x, ++y); };
+  // Equivalent in C++23:
+  // const auto lambda = [x, y = int32_t{0}] mutable -> void { Print(++x, ++y); };
+
+  // In a function call:
+  Foo(fn [var x, var y: i32 = 0] { Print(++x, ++y); });
+  // Equivalent in C++23:
+  // Foo([x, y = int32_t{0}] mutable -> void { Print(++x, ++y); });
+
+  fn FunctionDeclaration[var x, var y: i32 = 0] { Print(++x, ++y); }
+  // Equivalent in C++23:
+  // auto FunctionDeclaration = [x, y = int32_t{0}] mutable -> void { Print(++x, ++y); };
+}
+```
+
+Functions also support so-called
+["positional parameters"](#positional-parameters) that are defined at their
+point of use using a dollar sign and a non-negative integer. They are implicitly
+of type `auto`.
+
+```
+fn Foo() {
+  let lambda: auto = fn { Print($0); };
+  // Equivalent in C++23:
+  // auto lambda = [](auto _0, auto...) -> void { Print(_0); };
+  // Equivalent in Swift:
+  // let lambda = { Print($0) };
+
+  fn FunctionDeclaration { Print($0); }
+  // Equivalent in C++23:
+  // auto FunctionDeclaration = [](auto _0, auto...) -> void { Print(_0); };
+  // Equivalent in Swift:
+  // let FunctionDeclaration = { Print($0) };
+}
+```
+
+Of course, functions can also have named parameters, but a single function can't
+have both named and positional parameters.
+
+```
+fn Foo() {
+  // In a variable:
+  let lambda: auto = fn (v: auto) { Print(v); };
+  // Equivalent in C++23:
+  // const auto lambda = [](v: auto) -> void { Print(v); };
+
+  // In a function call:
+  Foo(fn (v: auto) { Print(v); });
+  // Equivalent in C++23:
+  // Foo([](v: auto) { Print(v); });
+
+  fn FunctionDeclaration(v: auto) { Print(v); }
+  // Equivalent in C++23:
+  // auto FunctionDeclaration(v: auto) -> void { Print(v); }
+}
+```
+
+And in additional the option between positional and named parameters, deduced
+parameters are always permitted.
+
+```
+fn Foo() {
+  let lambda: auto = fn [T:! Printable](t: T) { Print(t); };
+
+  fn FunctionDeclaration[T:! Printable](t: T) { Print(t); }
+}
+```
+
+### Syntax Defined
+
+Function definitions and lambda expressions have one of the following syntactic
+forms (where items in square brackets are optional and independent):
+
+`fn` \[_name_\] \[_implicit-parameters_\] \[_tuple-pattern_\] `=>` _expression_
+\[`;`\]
+
+`fn` \[_name_\] \[_implicit-parameters_\] \[_tuple-pattern_\] \[`->`
+_return-type_\] `{` _statements_ `}`
+
+The first form is a shorthand for the second: "`=>` _expression_ `;`" is
+equivalent to "`-> auto { return` _expression_ `; }`".
+
+_implicit-parameters_ consists of square brackets enclosing a optional default
+capture mode and any number of explicit captures, function fields, and deduced
+parameters, all separated by commas. The default capture mode (if any) must come
+first; the other items can appear in any order. If _implicit-parameters_ is
+omitted, it is equivalent to `[]`.
+
+The presence of _name_ determines whether this is a function definition or a
+lambda expression. The trailing `;` in the first form is required for a function
+definition, but is not part of the syntax of a lambda expression.
+
+The presence of _tuple-pattern_ determines whether the function body uses named
+or positional parameters.
+
+The presence of "`->` _return-type_" determines whether the function body can
+(and must) return a value.
+
+To understand how the syntax between lambdas and function declarations is
+reasonably "continuous", refer to this table of syntactic positions and the
+following code examples.
+
+| Syntactic Position |                        Syntax Allowed in Given Position (optional, unless otherwise stated)                        |
+| :----------------: | :----------------------------------------------------------------------------------------------------------------: |
+|         A1         |               Required Returned Expression ([positional parameters](#positional-parameters) allowed)               |
+|         A2         |             Required Returned Expression ([positional parameters](#positional-parameters) disallowed)              |
+|         B          |                                   [Default Capture Mode](#default-capture-mode)                                    |
+|         C          | Explicit [Captures](#function-captures), [Function Fields](#function-fields) and Deduced Parameters (in any order) |
+|         D          |                                                Explicit Parameters                                                 |
+|         E1         |           Body of Statements (no return value) ([positional parameters](#positional-parameters) allowed)           |
+|         E2         |          Body of Statements (with return value) ([positional parameters](#positional-parameters) allowed)          |
+|         E3         |         Body of Statements (no return value) ([positional parameters](#positional-parameters) disallowed)          |
+|         E4         |        Body of Statements (with return value) ([positional parameters](#positional-parameters) disallowed)         |
+|         F          |                                                Required Return Type                                                |
+|         G          |                                             Function Declaration Name                                              |
+
+```
+// Lambdas (all the following are in an expression context and are
+// themselves expressions)
+
+fn => A1
+
+fn [B, C] => A1
+
+fn (D) => A2
+
+fn [B, C](D) => A2
+
+fn { E1; }
+
+fn -> F { E2; }
+
+fn [B, C] { E1; }
+
+fn [B, C] -> F { E2; }
+
+fn (D) { E3; }
+
+fn (D) -> F { E4; }
+
+fn [B, C](D) { E3; }
+
+fn [B, C](D) -> F { E4; }
+```
+
+```
+// Function Declarations (all the following are allowed as statements in a
+// function body or as declarations in other scopes)
+
+fn G => A1;
+
+fn G[B, C] => A1;
+
+fn G(D) => A2;
+
+fn G[B, C](D) => A2;
+
+fn G { E1; }
+
+fn G -> F { E2; }
+
+fn G[B, C] { E1; }
+
+fn G[B, C] -> F { E2; }
+
+fn G(D) { E3; }
+
+fn G(D) -> F { E4; }
+
+fn G[B, C](D) { E3; }
+
+fn G[B, C](D) -> F { E4; }
+```
+
+## Introducer
+
+**Proposal**: Introduce with the `fn` keyword to mirror function declarations.
+If a statement or declaration begins with `fn`, a name is required and it
+becomes a function declaration. Otherwise, if in an expression context, `fn`
+introduces a lambda.
+
+```
+let lambda1: auto = fn => T.Make();
+
+let lambda2: auto = fn -> T { return T.Make(); };
+
+fn FunctionDeclaration1 => T.Make();
+
+fn FunctionDeclaration2 -> T { return T.Make(); }
+```
+
+## Positional Parameters
+
+**Proposal**: Positional parameters, introduced in the body of a function by way
+of the dollar sign and a corresponding non-negative parameter position integer
+(ex: `$3`), are `auto` parameters to the function in which they are defined.
+They can be used in any lambda or function declaration that lacks an explicit
+parameter list (parentheses). They are variadic by design, meaning an unbounded
+number of arguments can be passed to any function that lacks an explicit
+parameter list. Only the parameters that are named in the body will be read
+from, meaning the highest named parameter denotes the minimum number of
+arguments required by the function. The function body is free to omit
+lower-numbered parameters (ex: `fn { Print($10); }`).
+
+This syntax was inpsired by Swift's
+[Shorthand Argument Names](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/closures/#Shorthand-Argument-Names).
+
+```
+// A lambda that takes two positional parameters being used as a comparator
+Sort(my_list, fn => $0.val < $1.val);
+// In Swift: { $0.val < $1.val }
+```
+
+### Positional Parameter Restrictions
+
+**Proposal**: There are two restrictions applied to functions with positional
+parameters. The first restriction is that the definitions of function
+declarations must be attached to the declarations. The second restriction is
+that positional parameters can only be used in a context where there is exactly
+one enclosing function without an explicit parameter list. For example...
+
+```
+fn Foo1 {
+  fn Bar1 {}  // ❌ Invalid: Foo1 is already using positional parameters
+}
+
+fn Foo2 {
+  Print($0);
+  fn Bar2 {}  // ❌ Invalid: Foo2 is already using positional parameters
+}
+
+fn Foo3 {
+  fn Bar3 {
+    Print($0);  // ❌ Invalid: Foo3 is already using positional parameters
+  }
+}
+
+fn Foo4() {
+  fn Bar4 {
+    Print($0);  // ✅ Valid: Foo4 has explicit parameters
+  }
+}
+
+fn Foo5 {
+  fn Bar5() {}  // ✅ Valid: Bar5 has explicit parameters
+}
+
+fn Foo6() {
+  my_list.Sort(
+    fn => $0 < $1  // ✅ Valid: Foo6 has explicit parameters
+  );
+}
+```
+
+## Function Captures
+
+**Proposal**: Function captures in Carbon mirror the non-init captures of C++. A
+function capture declaration consists of a capture mode (for `var` captures)
+followed by the name of a binding from the enclosing scope, and makes that
+identifier available in the inner function body. The lifetime of a capture is
+the lifetime of the function in which it exists. For example...
+
+```
+fn Foo() {
+  let handle: Handle = Handle.Get();
+  var thread: Thread = Thread.Make(fn [var handle] { handle.Process(); });
+  thread.Join();
+}
+```
+
+```
+fn Foo() {
+  let handle: Handle = Handle.Get();
+  fn MyThread[handle]() { handle.Process(); }
+  var thread: Thread = Thread.Make(MyThread);
+  thread.Join();
+}
+```
+
+### Capture Modes
+
+**Proposal**: `let` and `var` can appear as function captures. They behave as
+they would in regular bindings.
+
+To prevent ambiguities, captures can only exist on functions where the
+definition is attached to the declaration. This means they are supported on
+lambdas (which always exist in an expression context) and they are supported on
+function declarations that are immediately defined inside the body of another
+function (which is in a statement context), but they are not supported on
+forward-declared functions nor are they supported as class members where
+`self: Self` is permitted.
+
+Capture modes can be used as
+[default capture mode specifiers](#default-capture-mode) or for explicit
+captures as shown in the example code below.
+
+```
+fn Example {
+  var a: i32 = 0;
+  var b: i32 = 0;
+
+  let lambda: auto = fn [a, var b] {
+    a += 1;  // ❌ Invalid: by-value captures are immutable
+
+    b += 1;  // ✅ Valid: Modifies the captured copy of the by-object capture
+  };
+
+  lambda();
+}
+```
+
+```
+fn Example {
+  fn Invalid() -> auto {
+    var s: String = "Hello world";
+    return fn [s]() => s;
+  }
+
+  // ❌ Invalid: returned lambda references `s` which is no longer alive
+  // when the lambda is invoked.
+  Print(Invalid()());
+}
+```
+
+Note: If a function object F has mutable state, either because it has a
+by-object capture or because it has a by-object function field, then a call to F
+should require the callee to be a reference expression rather than a value
+expression. We need a mutable handle to the function in order to be able to
+mutate its mutable state.
+
+### Default Capture Mode
+
+**Proposal**: By default, there is no capturing in functions. The lack of any
+square brackets is the same as an empty pair of square brackets. Users can opt
+into capturing behavior. This is done either by way of individual explicit
+captures, or more succinctly by way of a default capture mode. The default
+capture mode roughly mirrors the syntax `[=]` and `[&]` capture modes from C++
+by being the first thing to appear in the square brackets.
+
+```
+fn Foo1() {
+  let handle: Handle = Handle.Get();
+  fn MyThread[var]() {
+    handle.Process();  // `handle` is captured by-object due to the default capture
+                       // mode specifier of `var`
+  }
+  var thread: Thread = Thread.Make(MyThread);
+  thread.Join();
+}
+
+fn Foo2() {
+  let handle: Handle = Handle.Get();
+  fn MyThread[let]() {
+    handle.Process();  // `handle` is captured by-value due to the default capture
+                       // mode specifier of `let`
+  }
+  var thread: Thread = Thread.Make(MyThread);
+  thread.Join();
+}
+```
+
+## Function Fields
+
+**Proposal**: Function fields mirror the behavior of init captures in C++. A
+function field definition consists of an irrefutable pattern, `=`, and an
+initializer. It matches the pattern with the initializer when the function
+definition is evaluated. The bindings in the pattern have the same lifetime as
+the function, and their scope extends to the end of the function body.
+
+To prevent ambiguities, function fields can only exist on functions where the
+definition is attached to the declaration. This means they are supported on
+lambdas (which always exist in an expression context) and they are supported on
+function declarations that are immediately defined inside the body of another
+function (which is in a statement context), but they are not supported on
+forward-declared functions nor are they supported as class members where
+`self: Self` is permitted.
+
+```
+fn Foo() {
+  var h1: Handle = Handle.Get();
+  var h2: Handle = Handle.Get();
+  var thread: Thread = Thread.Make(fn [a: auto = h1, var b: auto = h2] {
+    a.Process();
+    b.Process();
+  });
+  thread.Join();
+}
+```
+
+## Copy Semantics
+
+**Proposal**: To mirror the behavior of C++, function declarations and lambdas
+will be as copyable as their contained function fields and function captures.
+This means that, if a function holds a by-object function field, if the type of
+the field is copyable, so too is the function that contains it. This also
+applies to captures.
+
+The other case is by-value function fields. Since C++ const references, when
+made into fields of a class, prevent the class from being copied assigned, so
+too should by-value function fields prevent the function in which it is
+contained from being copied assigned.
+
+## Self and Recursion
+
+**Proposal**: To mirror C++'s use of capturing `this`, `self` should always come
+from the outer scope as a capture. `self: Self` is never permitted on lambdas.
+For function declarations, it is only permitted when the function is a member of
+a class type or an interface, such that it refers to the class/interface and not
+to the function itself.
+
+Note: Given the direction in
+[#3720](https://github.com/carbon-language/carbon-lang/pull/3720), an expression
+of the form `x.(F)`, where `F` is a function with a `self` or `addr self`
+parameter, produces a callable that holds the value of `x`, and does not hold
+the value of `F`. As a consequence, we can't support combining captures and
+function fields with a `self` parameter.
+
+## Rationale
+
+Lambdas in Carbon serve two purposes. The primary purpose is in support of the
+["Code that is easy to read, understand, and write"](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write)
+goal. It is because of this goal that we leverage syntactic features such as the
+returned expression (indicated by `=>`) and positional parameters (indicated by
+the lack of a tuple pattern of explicit parameters as well as the use of `$N` in
+the body of such functions). In addition, Lambdas serve to support the
+[Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code)
+goal. They are defined at their point of use and are often anonymous, meaning
+replacing C++ lambdas solely with function declarations will create an ergonomic
+burden compounded by the need for the migration tool to select a name.
+
+## Alternatives Considered
+
+### Alternative Considered: Terse vs Elaborated
+
+Proposed above is a continuous syntax between lambdas and function declarations.
+Alternatively, Carbon could adopt a few different categories of functions, as
+was considered in a previous discussion doc
+([Lambdas Discussion 2](https://docs.google.com/document/d/14K_YLjChWyyNv3wv5Mn7uLFHa0JZTc21v_WP8RzC8M4/)).
+These categories would be terse lambdas, elaborated lambdas, and function
+declarations. Unfortunately, separating these categories out presented a
+syntactic challenge in the form of cliffs, explained below. As a result, they
+were decided against.
+
+Terse lambdas were slated to be the most compact form of a lambda. Combined with
+a [sigil introducer](#alternative-considered-sigil), they would be syntactically
+minimal. One way in which syntax was minimized was the granting of an
+**implicit** default [capture](#function-captures) mode. If no square brackets
+were present, by-value captures would be allowed. This, combined with the lack
+of an arrow to signify a return value, created syntax of the following form
+(being passed into the filter function below).
+
+```
+let zero: i32 = 0;
+let list_all: List(i32) = GetAllValues();
+let list_positive: List(i32) = list_all.Filter(
+  @ $0 > zero
+);
+```
+
+To give users more control over the feature set in a lambda, the next step up
+was an elaborated lambda. This provided the ability to add both square brackets
+and explicit parameters to lambdas at the cost of more syntax. Unfortunately,
+this also meant there was a bit of a _syntactic cliff_ and a stumbling block. It
+was considered desirable for empty square brackets to mean capturing is
+disabled. But since the no-square-brackets form needed to support capturing for
+terse lambdas, elaborated lambdas needed to both add the square brackets and
+also add an explicit default capture mode at the same time just to maintain the
+existing capturing behavior. The net result was code that looked like the
+following (being passed into the filter function again).
+
+```
+let zero: i32 = 0;
+let list_all: List(i32) = GetAllValues();
+let list_positive: List(i32) = list_all.Filter(
+  @[let](x: auto) x > zero
+);
+```
+
+Finally, if a user wanted to upgrade a lambda to a function declaration, this
+created another cliff where they needed to switch from the sigil to the `fn`
+keyword, on top of adding a name. Ultimately these downsides suggested that a
+continuous syntax was the better path forward, despite the face that the
+shortest spellable lambda would be a bit less terse than the alternative
+considered.
+
+### Alternative Considered: Sigil
+
+Proposed above is the use of `fn` as the [introducer](#introducer) for all
+functions/lambdas. An alternative considered was to tntroduce with a sigil, such
+as `$` or `@`. Since introducer punctuation is such a scarce resource, and since
+there was no consensus on what sigil would best represent a lambda, and since
+there was a desire to create a more continuous syntax between lambdas and
+function declarations, this alternative was decided against. It would have
+looked like the following:
+
+```
+let lambda1: auto = @ => T.Make();
+
+let lambda2: auto = @[]() -> T { return T.Make(); };
+```
+
+### Alternative Considered: Additional Positional Parameter Restriction
+
+In addition to
+[the above proposed restrictions](#positional-parameter-restrictions) to
+positional parameters, an additional restriction was considered. That being,
+visibility of functions with positional parameters could be restricted to only
+non-public interfaces. This alternative was considered by way of a leads
+question ([#3860](https://github.com/carbon-language/carbon-lang/issues/3860))
+and was decided against, with the speculation that such a restriction may be
+enforced by way of an HOA rule as opposed to a compiler error.
+
+### Alternative Considered: Recursive Self
+
+Proposed above is a deliniation between function declarations that can provide a
+`self` parameter and functions declarations (plus lambdas) which cannot. An
+alternative was considered such that, for use in recursion, `self: Self` could
+be permitted on all functions and lambdas and refer to the function itself.
+Unfortunately, it created a bit of a discontinuity between class members and
+non-class members and was thus decided against.
+
+## Future Work
+
+### Future Work: Reference Captures
+
+Much discussion has been had so far about the implications of capturing by
+reference. For now, such behavior is supported not through captures but instead
+through function fields formed from the address of an object in the outer scope.
+It is **imperative** that more work be done in this area to address the
+ergonomic concerns of the current solution.