Update functions design doc (#7355)

Incorporates changes from these proposals:

- #2022 
- #2875 
- #3262 
- #3763 
- #3848 
- #5434 

A small amount of updating was done to lambdas.md and variadics.md to
harmonize with these changes.

Assisted-by: Gemini via Antigravity

---------

Co-authored-by: Josh L <josh11b@users.noreply.github.com>
Co-authored-by: Geoff Romer <gromer@google.com>
This commit is contained in:
josh11b
2026-06-18 22:58:51 +00:00
committed by GitHub
co-authored by Josh L Geoff Romer
parent 974850788c
commit 2ec915eab6
7 changed files with 465 additions and 95 deletions
+1 -1
View File
@@ -1313,7 +1313,7 @@ fn Positive(a: i64) -> auto {
> References:
>
> - [Type inference](type_inference.md)
> - [Function return clause](functions.md#return-clause)
> - [Function return specification](functions.md#return-specification)
> - Proposal
> [#826: Function return type inference](https://github.com/carbon-language/carbon-lang/pull/826)
+2
View File
@@ -376,6 +376,8 @@ These operators act like unary postfix operators for purposes of precedence:
again puts them in parentheses that clearly separate them for precedence
purposes.
The operand or result of a suffix operator is called a _suffix expression_.
## Conversions and casts
When an expression appears in a context in which an expression of a specific
+4 -2
View File
@@ -1018,8 +1018,10 @@ fn Use(a: A) {
}
```
Member access has lower precedence than primary expressions, and higher
precedence than all other expression forms.
Member access has [lower precedence](README.md#precedence) than primary
expressions (literals, unqualified names, and expressions in parentheses, as in
[C++](https://cppreference.com/cpp/language/expressions#Primary_expressions)),
and higher precedence than all other expression forms.
```
// ✅ OK, `*` has lower precedence than `.`. Same as `(A.B)*`.
+413 -54
View File
@@ -12,10 +12,20 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- [Overview](#overview)
- [Function definitions](#function-definitions)
- [Return clause](#return-clause)
- [Function signatures](#function-signatures)
- [Captures and function fields](#captures-and-function-fields)
- [Positional Parameters](#positional-parameters)
- [Return specification](#return-specification)
- [Unused parameters](#unused-parameters)
- [`return` statements](#return-statements)
- [Function declarations](#function-declarations)
- [Forward declarations](#forward-declarations)
- [Redeclaration matching](#redeclaration-matching)
- [Function types and values](#function-types-and-values)
- [Bound methods](#bound-methods)
- [Function calls](#function-calls)
- [Direct calls](#direct-calls)
- [Indirect calls and the `Call` interface](#indirect-calls-and-the-call-interface)
- [Overloaded call operator](#overloaded-call-operator)
- [Functions in other features](#functions-in-other-features)
- [Alternatives considered](#alternatives-considered)
- [References](#references)
@@ -24,28 +34,30 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
## Overview
> **TODO:** Update this document to reflect the introduction of function values,
> function types, and the `Call` interface in
> [#2875: Functions, function types, and function calls](/proposals/p002875-functions-function-types-and-function-calls.md).
Functions are the core building block for applications. A function definition or
declaration has one of the following syntactic forms (where items in square
brackets are optional and independent):
> **TODO:** Update this document to reflect the changes to named functions in
> [#3848: Lambdas](/proposals/p003848-lambdas.md).
- `fn` _name_ [_implicit-parameters_] [_tuple-pattern_] `=>` _expression_ `;`
- `fn` _name_ [_implicit-parameters_] [_tuple-pattern_] [`->` _return-form_] `{`
_statements_ `}`
- `fn` _name_ [_implicit-parameters_] [_tuple-pattern_] [`->` _return-form_] `;`
Functions are the core building block for applications. Carbon's basic function
syntax is:
The first form is a shorthand: `=> expression ;` is equivalent to
`-> auto { return expression; }`. When a body is present (the first and second
forms), it is a function definition. The body introduces nested scopes which may
contain local variable declarations. A function with only a signature and no
body (the third form) is a forward declaration.
- _parameter_: _identifier_ `:` _expression_
- _parameter-list_: _[ parameter_ `,` _parameter_ `,` _... ]_
- _return-clause_: _[_ `->` _< expression |_ `auto` _> ]_
- _signature_: `fn` _identifier_ `(` _parameter-list_ `)` _return-clause_
- _function-definition_: _signature_ `{` _statements_ `}`
- _function-declaration_: _signature_ `;`
- _function-call_: _identifier_ `(` _[ expression_ `,` _expression_ `,` _...
]_ `)`
The syntax for parameters and returns is the same for functions and
[lambdas](lambdas.md#syntax-overview):
A function with only a signature and no body is a function declaration, or
forward declaration. When the body is a present, it's a function definition. The
body introduces nested scopes which may contain local variable declarations.
- _implicit-parameters_: square brackets `[`...`]` enclosing default capture
modes, explicit captures, function fields, or deduced parameters, see
[lambdas](lambdas.md#implicit-parameters-in-square-brackets).
- _tuple-pattern_: parentheses `(`...`)` enclosing a list of explicit
parameter patterns, see
[pattern matching](pattern_matching.md#pattern-syntax-and-semantics).
## Function definitions
@@ -57,9 +69,14 @@ fn Add(a: i64, b: i64) -> i64 {
}
```
This declares a function called `Add` which accepts two `i64` parameters, the
first called `a` and the second called `b`, and returns an `i64` result. It
returns the result of adding the two arguments.
Or using the shorthand `=>` return expression syntax:
```carbon
fn Add(a: i64, b: i64) => a + b;
```
These declare a function called `Add` which accepts two `i64` parameters, the
first called `a` and the second called `b`, and returns an `i64` result.
C++ might declare the same thing:
@@ -74,31 +91,104 @@ auto Add(std::int64_t a, std::int64_t b) -> std::int64_t {
}
```
### Return clause
### Function signatures
The return clause of a function specifies the return type using one of three
possible syntaxes:
#### Captures and function fields
- `->` followed by an _expression_, such as `i64`, directly states the return
type. This expression will be evaluated at compile-time, so must be valid in
that context.
- For example, `fn ToString(val: i64) -> String;` has a return type of
`String`.
Like lambdas, named function definitions support [captures](lambdas.md#captures)
and [function fields](lambdas.md#function-fields), with these restrictions:
- They can only be used on functions where the definition is attached to the
declaration (so they cannot be forward declared).
- Captures and function fields are only supported on local function
definitions immediately defined inside the body of another function. They
are not supported on member functions of classes/interfaces.
#### Positional Parameters
Like lambdas, named function definitions support
[positional parameters](lambdas.md#positional-parameters), which are used when
the explicit parameter list is omitted. Like
[captures and function fields](#captures-and-function-fields), they may only be
used with function definitions and not forward declarations. In addition,
positional parameters can only be used in a context where there is exactly one
enclosing function or lambda that has no explicit parameter list.
#### Return specification
The return type of a function can be specified using a return clause (`->`), or
it can be deduced using a signature return expression (`=>`).
- `->` followed by a return form:
- Most commonly, this will be an _expression_ that directly states the
return type, such as `i64`.
- The expression will be evaluated at compile-time, so must be valid
in that context.
- For example, `fn ToString(val: i64) -> strbuf;` has a return type of
`strbuf`.
- A return form can also use `val`, `ref`, and `var` to control the
function call's expression category. For example, `-> ref i32` indicates
that the function returns by reference. See
["Function calls and returns"](values.md#function-calls-and-returns) for
details.
- `->` followed by the `auto` keyword indicates that
[type inference](type_inference.md) should be used to determine the return
type.
- For example, `fn Echo(val: i64) -> auto { return val; }` will have a
return type of `i64` through type inference.
- Declarations must have a known return type, so `auto` is not valid.
- Forward declarations must have a known return type, so `auto` is not
valid.
- The function must have precisely one `return` statement. That `return`
statement's expression will then be used for type inference.
- Omission indicates that the return type is the empty tuple, `()`.
- The `auto` keyword may be preceded by `val`, `ref`, or `var` to specify
the return expression category.
- Omission of both `->` and `=>` indicates that the return type is the empty
tuple, `()`.
- For example, `fn Sleep(seconds: i64);` is similar to
`fn Sleep(seconds: i64) -> ();`.
- `()` is similar to a `void` return type in C++.
- `=>` followed by an _expression_ defines a shorthand for a function body
that returns the expression. The return type is deduced as if `-> auto` were
used.
- For example, `fn Add(a: i64, b: i64) => a + b;` has a return type of
`i64` based on the type of the expression `a + b`.
- Because the return type is deduced and not explicitly known, functions
defined using `=>` cannot have a separate forward declaration.
> **TODO:** Update this section to cover return forms, as discussed
> [here](values.md#function-calls-and-returns).
#### Unused parameters
When a parameter introduced in a function definition is not used in the function
body, a compiler warning is issued. To suppress this warning, a parameter can be
explicitly marked as unused in one of two ways:
- **Anonymous parameters**: By using `_` in place of the parameter name (for
example, `_: i32`).
- **`unused` parameters**: By preceding the parameter name with the `unused`
keyword (for example, `unused size: i32`), which allows preserving the
parameter name for documentation purposes.
Both of these forms are patterns. For more details on the behavior of `unused`
name bindings and patterns, see the
[pattern matching design](pattern_matching.md#unused).
For example:
```carbon
// Function declaration (for example, in an API file)
fn Sum(x: List(i32), size: i32) -> i32;
// Implementation that does not use the `size` parameter, using an
// anonymous parameter:
fn Sum(x: List(i32), _: i32) -> i32 { ... }
// Or using the `unused` keyword to keep the name for documentation:
fn Sum(x: List(i32), unused size: i32) -> i32 { ... }
```
`unused` markers may only appear on definitions, not on non-defining
declarations. The names of parameters must match between redeclarations, but the
presence of the `unused` marker does not need to match, see
[redeclaration matching](#redeclaration-matching).
### `return` statements
@@ -106,9 +196,9 @@ The [`return` statement](control_flow/return.md) is essential to function
control flow. It ends the flow of the function and returns execution to the
caller.
When the [return clause](#return-clause) is omitted, the `return` statement has
no expression argument, and function control flow implicitly ends after the last
statement in the function's body as if `return;` were present.
When the [return clause](#return-specification) is omitted, the `return`
statement has no expression argument, and function control flow implicitly ends
after the last statement in the function's body as if `return;` were present.
When the return clause is provided, including when it is `-> ()`, the `return`
statement must have an expression that is convertible to the return type, and a
@@ -117,7 +207,7 @@ statement must have an expression that is convertible to the return type, and a
> **TODO:** Update this section to cover the requirements on the form of the
> expression.
## Function declarations
## Forward declarations
Functions may be declared separate from the definition by providing only a
signature, with no body. This provides an API which may be called. For example:
@@ -135,29 +225,284 @@ fn Add(a: i64, b: i64) -> i64 {
The corresponding definition may be provided later in the same file or, when the
declaration is in an
[API file of a library](code_and_name_organization/#libraries), in an
implementation file of the same library. The signature of a function declaration
must match the corresponding definition. This includes the
[return clause](#return-clause); even though an omitted return type has
equivalent behavior to `-> ()`, the presence or omission must match.
implementation file of the same library.
## Function calls
A function may only be forward declared once in any given file, and any forward
declaration must appear before the definition.
Function calls use a function's identifier to pass multiple expression arguments
corresponding to the function signature's parameters. For example:
To declare a function that is defined in a different library, the `extern`
modifier is used (for example, `extern fn F();`). A library that declares a
function as `extern` cannot define it. The `extern` modifier is only valid on
namespace-scoped functions, not on member functions of classes. For more details
on cross-library forward declarations and modifier merging, see the
[declaring entities design](declaring_entities.md#extern-and-extern-library).
### Redeclaration matching
Redeclarations of a function must match syntactically. The sequence of tokens
following the `fn` keyword (and optional scope name) up to the semicolon or open
brace must be identical.
Specifically, the following must match exactly between the forward declaration
and the definition:
- **Parameter names**: You cannot change a parameter name or replace it with
`_` in the definition.
- **Parameter types**: The types and grouping parentheses must match exactly.
- **Return clause**: The presence or omission of the return clause must match
exactly (for example, an omitted return type behaves equivalent to `-> ()`,
but they are syntactically different and cannot be mixed).
The only exception is the `unused` modifier on parameters, which is allowed on a
defining declaration (such as the definition) but disallowed on a non-defining
declaration.
Declaration modifiers (such as access control keywords or `virtual`) appear
before the `fn` keyword, so they are not involved in checking whether the two
signatures differ.
## Function types and values
A function declaration in Carbon introduces a new, unique, stateless type,
called a _function type_. The function name is bound to a value of that function
type.
Distinct functions have distinct function types, even if they have the same
signature. A function type is an empty, trivial type. There is no way to name a
function type other than asking for the type of the function value.
```carbon
fn Add(a: i64, b: i64) -> i64 {
return a + b;
}
fn F(x: i32) -> i32 { return x; }
fn Run() {
Add(1, 2);
// Compile-time function.
musteval fn TypeOf[T:! type](x: T) -> type { return T; }
// `F` is a first-class value with a first-class type.
let template FType:! type = TypeOf(F);
var my_f: FType = F;
```
Function values are regular values that can be stored in variables, passed to
functions, and so on.
```carbon
fn G() -> i32 {
// `my_f` has function type `FType`. This is a direct call to `F`.
return my_f(1);
}
```
Here, `Add(1, 2)` is a function call expression. `Add` refers to the function
definition's identifier. The parenthesized arguments `1` and `2` are passed to
the `a` and `b` parameters of `Add`.
For the purpose of the [orphan rule](generics/details.md#orphan-rule), a
function type is considered to be declared by the function declaration that
introduces the function value.
### Bound methods
A function with a `self` parameter is a method. The type of a method is a
stateless type, like other functions. Once the method is
[bound to an instance](expressions/member_access.md#instance-binding), for
example in the expression `object.MethodName`, the result is a _bound method
value_. The type of the result is a _bound method type_, with the same signature
as the method, but with the `self` parameter removed. A bound method type
describes the callee in a method call, and a bound method value specifies the
`self` parameter of the call.
```carbon
class HasMember {
// `HasMember.F` has a stateless function type, with signature
// `(self, n: i32) -> i32`.
fn F(self, n: i32) -> i32;
}
fn F(h1: HasMember, h2: HasMember) -> i32 {
// `h1.F` is a bound method value whose type is a bound method type,
// with signature `(n: i32) -> i32`.
var hf: auto = h1.F;
// `h1.F` and `h2.F` are of the same bound method type.
hf = h2.F;
// Same as `h2.F(4)`.
return hf(4);
}
```
## Function calls
Function calls use C-like syntax:
> _expression_ `(` _[ expression_ `,` _expression_ `,` _... ]_ `)`
It consists of an expression naming a callee followed by an argument list
enclosed in parentheses, which resembles a tuple of arguments. Calls take the
form `a(b, c, d)` or `a(b, c, d,)`, where:
- `a` is the callee, which can be a name, a literal, a member access, or some
more complex expression enclosed in parentheses.
- `b`, `c`, `d` are any number of argument expressions, each optionally
prefixed with `ref` if passing to a `ref` parameter. Arguments are separated
by commas, and if the argument list is not empty, an optional trailing comma
is permitted but not required after the final argument.
Call syntax is syntactically equivalent to a
[suffix expression](expressions/README.md#suffix-operators) followed by a tuple
literal, except that a tuple literal requires a trailing comma to form a
single-element tuple `(b,)`, whereas in call syntax both `a(b)` and `a(b,)` are
permitted.
A _callable value_ (or _callable_ for short) is a value that can be used as the
callee of a call expression. There are several kinds of callable:
- Functions, and more generally values of function types.
- Bound methods, such as `my_vector.Begin`.
- Lambdas.
- Parameterized entities, such as a generic class `Vector` or a generic
interface `AddWith`.
- Values of dependent types that are
[constrained to be callable](#indirect-calls-and-the-call-interface).
- User-defined class types that overload function call syntax.
Function calls are divided into _direct calls_ and _indirect calls_.
### Direct calls
A call expression is a _direct call_ when the callee:
- is the name of a parameterized entity, like a generic class or interface, or
- has a function type or bound method type.
Note that this includes virtual method calls, even though those can include some
indirection. In a direct call, a call signature is available which is used to
check the given arguments against the callee's declared implicit and explicit
parameters. This checking proceeds as follows:
- Argument deduction is performed by comparing the declared parameter types
against the actual argument types and deducing values for implicit arguments
that make the types equal.
- Then, for each binding in the explicit parameter list in turn, all argument
values that have been deduced are substituted into the parameter.
- If the parameter is a `ref` parameter (other than `self`), the
corresponding argument expression at the call-site must be prefixed with
`ref`. It is a compile-time error if the call-site has a mismatched
`ref` prefix:
- An argument to a non-`ref` parameter must not be prefixed with
`ref`, and
- An argument to a `ref` parameter must be prefixed with `ref`, except
in a generic context where the parameter's `ref` status may vary.
- If the parameter is a `template :!` binding, the argument expression is
converted to have the same type as the binding and template constant
expression phase.
- If the parameter is a symbolic `:!` binding, the argument expression is
converted to have the same type as the binding and symbolic constant
expression phase.
- Otherwise, the parameter is pattern-matched against the argument.
If a parameter is a `:!` binding, its corresponding converted argument
expression is evaluated, and its value is added to the list of deduced
argument values before any later parameters are processed.
The result of the call expression depends on the callee:
- If the callee is a parameterized entity such as a generic class or a generic
interface, the result is the specific instance of that generic, such as a
class or interface, and the call is a value expression of type `type`.
- If the callee has a function type, the call is an expression whose form is
the substituted return form of its signature. When evaluated, the call
expression will invoke the function and produce whatever result it returns.
- If the callee has a bound method type, it behaves the same as a function
value, except that the `self` parameter of the called function is bound to
the `self` value in the bound method value.
### Indirect calls and the `Call` interface
A generic parameter can be constrained to be a callable type using the `Call`
interface:
```carbon
interface Call(... each Arg: type) {
let Result:! type;
fn Op(self, ... each arg: each Arg) -> Result;
}
```
A call expression that is not a direct call is an _indirect call_. It is
translated into an invocation of `Call(Arg1, Arg2,` ... `ArgN).Op`, where
`Arg1`, `Arg2`, ... `ArgN` are the types of the call's arguments in order. So
`F(arg1, arg2)` is translated into `F.(Call(Arg1, Arg2).Op)(arg1, arg2)`.
For example, given:
```carbon
fn Sort[T:! type, F:! Call(T, T) where .Result = Ordering]
(ref v: Vector(T), cmp: F) {
// ...
auto ord: auto = cmp(v[i], v[j]);
// ...
}
```
The call `cmp(v[i], v[j])` is translated into:
```carbon
auto ord: auto = cmp.(Call(T, T).Op)(v[i], v[j]);
```
A function type or bound method type implements the `Call` interface for every
set of runtime argument types that a direct call to the function or bound method
would accept. The behavior of `Call.Op` is to call the function or bound method
with the provided argument list.
Implicit conversions are permitted for parameters whose types do not involve
deduced parameters. The intent is for the `impl` to support indirect calls in
the same cases where the function supports direct calls, with the same meaning.
```carbon
fn TakeI32Fn[F:! Call(i32)](f: F);
fn I64Fn(n: i64);
fn Run() {
// ✅ `I64Fn` can be called with an `i32`, because
// `i32 impls ImplicitAs(i64)`.
TakeI32Fn(I64Fn);
}
```
> **Future work:** The `Call` interface currently only supports value parameters
> and initializing returns. It is future work to remove this restriction.
### Overloaded call operator
The `Call` interface can be implemented to overload the meaning of the function
call operator for a type.
```carbon
class Func(Arg:! type) {
impl as Call((Arg,)) where .Result = () {
fn Op(self, arg: (Arg,)) { Print("hello, world"); }
}
}
fn Run() {
let f: Func(i32) = {};
// ✅ Prints "hello, world".
f(42);
}
```
There are no constraints on the callee type, beyond the normal constraints for
implementing an interface.
```carbon
class X { var n: i32; }
impl {.a: X} as Call(()) where .Result = i32 {
fn Op(self, args: ()) -> i32 {
return self.a.n;
}
}
fn Run() -> i32 {
// Returns 1.
return {.a = {.n = 1} as X}();
}
```
## Functions in other features
@@ -174,6 +519,8 @@ Other designs build upon basic function syntax to add advanced features:
- [Only allow `auto` return types if parameters are compile-time](/proposals/p000826-function-return-type-inference.md#only-allow-auto-return-types-if-parameters-are-generic)
- [Provide alternate function syntax for concise return type inference](/proposals/p000826-function-return-type-inference.md#provide-alternate-function-syntax-for-concise-return-type-inference)
- [Allow separate declaration and definition](/proposals/p000826-function-return-type-inference.md#allow-separate-declaration-and-definition)
- [Signature-based function types](/proposals/p002875-functions-function-types-and-function-calls.md#signature-based-function-types)
- [Make direct and indirect calls behave uniformly](/proposals/p002875-functions-function-types-and-function-calls.md#make-direct-and-indirect-calls-behave-uniformly)
## References
@@ -181,3 +528,15 @@ Other designs build upon basic function syntax to add advanced features:
[#438: Add statement syntax for function declarations](https://github.com/carbon-language/carbon-lang/pull/438)
- Proposal
[#826: Function return type inference](https://github.com/carbon-language/carbon-lang/pull/826)
- Proposal
[#2022: Unused Pattern Bindings (Unused Function Parameters)](https://github.com/carbon-language/carbon-lang/pull/2022)
- Proposal
[#2875: Functions, function types, and function calls](https://github.com/carbon-language/carbon-lang/pull/2875)
- Proposal
[#3762: Merging forward declarations](https://github.com/carbon-language/carbon-lang/pull/3762)
- Proposal
[#3763: Matching redeclarations](https://github.com/carbon-language/carbon-lang/pull/3763)
- Proposal
[#3848: Lambdas](https://github.com/carbon-language/carbon-lang/pull/3848)
- Proposal
[#5434: `ref` parameters, arguments, returns and `val` returns](https://github.com/carbon-language/carbon-lang/pull/5434)
+43 -36
View File
@@ -23,10 +23,11 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- [Captures](#captures)
- [Capture modes](#capture-modes)
- [Default capture mode](#default-capture-mode)
- [Function fields in lambdas](#function-fields-in-lambdas)
- [Function fields](#function-fields)
- [Copy semantics](#copy-semantics)
- [Self and recursion](#self-and-recursion)
- [Alternatives considered](#alternatives-considered)
- [References](#references)
<!-- tocstop -->
@@ -66,8 +67,8 @@ PushBack(my_list, fn -> T { return T.Make() });
### Return type
There are three options for how a lambda expresses its return type, parallel to
[how function declarations express returns](functions.md#return-clause): using a
return expression, using an explicit return type, or having no return.
[how function declarations express returns](functions.md#return-specification):
using a return expression, using an explicit return type, or having no return.
#### Return expression
@@ -124,8 +125,8 @@ Foo(fn { Print(T.Make()); });
### Implicit parameters in square brackets
Lambdas support [captures](#captures), [fields](#function-fields-in-lambdas) and
deduced parameters in the square brackets.
Lambdas support [captures](#captures), [fields](#function-fields) and deduced
parameters in the square brackets.
```carbon
fn Foo(x: i32) {
@@ -215,18 +216,18 @@ 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](#captures), [Function fields](#function-fields-in-lambdas) 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 |
| 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](#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 |
```carbon
// Lambdas (all the following are in an expression context and are
@@ -260,20 +261,20 @@ fn [B, C](D) -> F { E4; }
## Positional parameters
Positional parameters, denoted by a dollar sign followed by a non-negative
integer (for example, $3), are auto-typed parameters defined within the lambda's
body.
integer (for example, $3), are auto-typed parameters defined within the function
or lambda body.
```carbon
let lambda: auto = fn => $0
```
They can be used in any lambda 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 lambda body is free to omit lower-numbered parameters (ex:
`fn { Print($10); }`).
They can be used in any lambda or function definition 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 body is free to omit lower-numbered
parameters (for example, `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).
@@ -427,13 +428,13 @@ fn Foo2() {
}
```
## Function fields in lambdas
## Function fields
Function fields in lambdas 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 lambda
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.
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.
```carbon
fn Foo() {
@@ -449,10 +450,11 @@ fn Foo() {
## Copy semantics
To mirror the behavior of C++, 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.
To mirror the behavior of C++, lambdas and functions with captures or function
fields 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
@@ -485,3 +487,8 @@ function fields with a `self` parameter.
- [Sigil](/proposals/p003848-lambdas.md#alternative-considered-sigil)
- [Additional Positional Parameter Restriction](/proposals/p003848-lambdas.md#alternative-considered-additional-positional-parameter-restriction)
- [Recursive Self](/proposals/p003848-lambdas.md#alternative-considered-recursive-self)
## References
- Proposal
[#3848: Lambdas](https://github.com/carbon-language/carbon-lang/pull/3848)
+1 -1
View File
@@ -258,7 +258,7 @@ fn Min[T:! Comparable & Value](first: T, ... each next: T) -> T {
```carbon
// Invokes f, with the tuple `args` as its arguments.
fn Apply[... each T:! type, F:! CallableWith(... each T)]
fn Apply[... each T:! type, F:! Call(... each T)]
(f: F, args: (... each T)) -> auto {
return f(...expand args);
}
@@ -96,7 +96,7 @@ Example rules for forward declarations in the current design:
- [High-level](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/README.md#declarations-definitions-and-scopes)
- [Classes](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/classes.md#forward-declaration)
- [Functions](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/functions.md#function-declarations)
- [Functions](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/functions.md#forward-declarationss)
- Generics:
- [`impl`](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/generics/details.md#forward-impl-declaration)
- [`interface`](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/generics/details.md#declaring-interfaces-and-named-constraints)