mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 13:40:11 +01:00
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:
co-authored by
Josh L
Geoff Romer
parent
974850788c
commit
2ec915eab6
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user