Skip to content

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.

This page remains the complete function reference. The linked subpages provide focused normative treatments with self-contained grammar productions and worked boundary cases:

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:

Receiverthis semanticsIn method set of
value func (c: T)struct: a snapshot copy — mutating a field does not write back; class: the reference itselfT 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 structT and *T
static func (c: static T)compile-time alias for T’s explicitly declared static facet; no runtime receiverstatic 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).

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.

CodeTrigger
ES2180a default value is not a constant shape
ES2181a positional argument follows a named one
ES2182a named argument names no parameter
ES2183too few required arguments, or too many arguments
ES2184a 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()) evaluates h() before g() because a precedes b in 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).

ReturnType = "->" Type .
ReturnsClause = "returns" Type . // class-level, inside class / static facet

A 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.

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 → ?.).

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).

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).