Functions
A function is either a free function (camelCase, on a namespace or static host class) or a
method (a function written with a receiver block). Both share one declaration grammar, given in
Declarations → functions. This page specifies
methods, chaining, lambdas, and function pointers.
In this specification
Section titled “In this specification”This page remains the complete function reference. The linked subpages provide focused normative treatments with self-contained grammar productions and worked boundary cases:
- Function declarations and calls — declaration forms, parameters, defaults, named arguments, and return selection.
- Methods and static facets — receiver grammar, attachment, method sets, and the static-facet selection rule.
- Function values — lambda grammar and capture, function-pointer types, and the delegate boundary.
Methods
Section titled “Methods”A method is a function written with a Go-style receiver block before its name —
func (c: Circle) area(). The receiver is named (there is no implicit self) and spelled like a
parameter, in the colon form name: Type. A bare first-parameter function with no receiver block
(func area(c: Circle)) is an ordinary free function, never a method — a receiver is what makes a method.
Attachment is namespace-gated: a method’s receiver type must be declared in that method’s
namespace. This gives a type a single home — every method of Circle lives in Circle’s namespace, so
the method set is found in one place rather than scattered across the program. (Across files this is
unaffected — partial namespaces span files, so a method may be declared in any file of the type’s
namespace.) A receiver over a closed generic (func (h: Holder<int>) …) is ill-formed
(ES2132); make the method generic over the type’s parameters
(func (h: Holder<T>) get<T>()).
There are four receiver kinds:
| Receiver | this semantics | In method set of |
|---|---|---|
value func (c: T) | struct: a snapshot copy — mutating a field does not write back; class: the reference itself | T and *T |
pointer func (c: *T) | mutates in place (ref this); *class is ill-formed (a class is already a reference) | *T only |
readonly readonly func (c: T) | borrows in this — a field write through the receiver is rejected; [IsReadOnly] on a struct | T and *T |
static func (c: static T) | compile-time alias for T’s explicitly declared static facet; no runtime receiver | static facet only |
func (c: static T) is legal only when the current namespace declares static T { ... }. It attaches
as T.method(...); c names static fields and methods in the body but is not a value. It does not create
a static facet and cannot be borrowed, pointed to, or used for interface conformance. For a static-only
T, ordinary func (c: T) selects the static facet automatically.
A method is method-only: the free-call spelling f(x) is ES2142 for
every receiver kind, with a fixit pointing at x.f() — the Go method-set discipline on the CLR.
(A pointer-receiver method is emitted as a static host so its body can treat the receiver as a
first-class *T — walking a linked list, comparing to nil — but that host is reachable only through
the method spelling, never as f(x).) A plain free function whose first parameter happens to be a *T
(func bump(v: *T), no receiver block) is unaffected: it is a free function, called bump(v).
Default and named arguments
Section titled “Default and named arguments”A parameter of a func, method, init, or class header may carry a default with = expr:
Param = ( [ "out" ] [ "readonly" ] identifier ":" Type | identifier ":" "*" Type ) [ "=" Expr ] .The default is a constant shape. It shall fold to a literal, be nil, or be a composite-literal /
dot-case / Result construction over such constants; anything else is
ES2180. An omitted argument materializes the default expression inline at
the call site (each omission re-materializes it). A literal default also stamps [Optional] and a
.param constant onto the parameter’s metadata, so a C# caller sees the same optional.
A named argument binds by parameter name (connect("localhost", useTls: false)). At every call site —
free function, method, constructor, : this/: base, BCL call — the rule is the same: all positional
arguments first, then named arguments in any order. Named arguments are not accepted on union /
enum case construction.
| Code | Trigger |
|---|---|
| ES2180 | a default value is not a constant shape |
| ES2181 | a positional argument follows a named one |
| ES2182 | a named argument names no parameter |
| ES2183 | too few required arguments, or too many arguments |
| ES2184 | a parameter is filled twice (positional + named, or named twice) |
Two rules are normative and deliberately un-C#-like:
- Overloads resolve by arity and argument names, never by argument types. Two candidates that take
the same parameter count and the supplied argument names are indistinguishable — there is no
better-conversion ranking. (For constructors specifically, same-arity
inits are rejected at the declaration as ES2185.) - Arguments always evaluate in parameter order, regardless of the order written.
f(b: g(), a: h())evaluatesh()beforeg()becauseaprecedesbin the signature; omitted-argument defaults materialize in that same parameter order. This keeps side-effect order a property of the callee’s signature, not the caller’s spelling.
Calling a C# method that omits a trailing optional works — the callee’s declared default constant is
loaded from metadata, never default(T).
returns clause
Section titled “returns clause”ReturnType = "->" Type .ReturnsClause = "returns" Type . // class-level, inside class / static facetA signature’s return type is written with ->. returns T is only a standalone clause inside a
class or static body, where it sets the default return type for member functions that
omit their own annotation; an explicit -> T on a member overrides it.
Method chaining
Section titled “Method chaining”A method call chains when the method returns a value the next call lands on. A method returning its
receiver yields a fluent API: for a class this returns the same instance (mutation threads through),
for a value struct a fresh value each step. A chain may break across lines with a leading dot — a
newline before . continues the chain. A method returning Result<T, E> does not chain through .;
unwrap each step with ? (Errors → ?.).
Lambdas and closures
Section titled “Lambdas and closures”Lambda = "func" "(" [ TypedLambdaParam { "," TypedLambdaParam } ] ")" [ ReturnType ] ( Block | "=" Expr ) | "(" [ ArrowLambdaParam { "," ArrowLambdaParam } ] ")" "=>" ( Expr | Block ) .TypedLambdaParam = identifier ":" Type .ArrowLambdaParam = identifier [ ":" Type ] .A function literal — explicit func(…) -> T { … } or inference-first (x) => expr / (x) => { … } — closes over the enclosing
scope. Captures are mutable and shared: a write inside the closure is visible outside and a write
outside is visible inside, because the captured variables are hoisted into a generated display class
shared by the outer scope and every closure over those captures. A closure over a let binding may read
it but not assign it (the immutability of let is enforced into the closure). Inside a task func body a
function literal shall not capture a var from the surrounding scope
(ES2130) — shared mutable state across the concurrency boundary is a race;
thread it through a chan<T> (Concurrency).
Arrow-parameter types are inferred when a delegate type is expected at the use site — a typed let, a
parameter, a return, or an event (Delegates & events). An
arrow may annotate a parameter when helpful; its return remains inferred. The complete distinction between
the explicit func signature and an arrow literal, including standalone inference and representation, is
specified in Function values.
When that use is a generic function or method, inference flows both ways: a type parameter pinned by the
receiver or another argument types the lambda’s parameter, and the lambda’s body-inferred return then pins
the remaining open parameter — so w.mapped((x) => x + 5) (a generic method) and
xs.Select((x) => x.ToString()) (a BCL generic extension) close their type arguments without an explicit
<…> (Generics → type-argument inference).
Function pointers
Section titled “Function pointers”FuncPtrType = "&" "(" [ TypeList "->" ] Type ")" . // &(int, int -> int) , &(string -> void) , &(-> bool)&f takes a function’s address — zero allocation, single target, emitted as ldftn + calli. A
function-pointer type is first-class: a struct field, a parameter, a local, a return. The binder verifies
signature compatibility at the call site. Function pointers are the systems tier; heap-allocated,
multicast delegates are the interop tier (Delegates & events). The
disambiguation between &f (a function pointer) and &x (the address of a variable) is by what the name
resolves to — a function versus a storage location (Pointers & by-ref).