Skip to main content
v0.13.0

Functions

fun add(a: i64, b: i64) -> i64 {
return a + b;
}

fun main() -> i64 {
return add(2, 3);
}

Named Function Declarations

Parameter type annotations are optional when types can be inferred from contextL2. The return type follows -> and is also optional — a function with no return annotation and no return expr; returns ()D1. return expr; and bare return; are both valid.

Formal rules
Legality Rule №1

Named function declarations begin with fun; fun is not an anonymous-function expression introducer.

Referenced by: rfc-0041

Legality Rule №2

Function parameter and return-type annotations may be omitted when their types can be inferred from context.

Dynamic Semantics №1

A function with no return annotation and no return expr; returns ().

Associated Functions

extend blocks may contain functions with no self parameter. These are called on the type via :: syntaxL1 and serve as the canonical constructor pattern:

struct Point {
x: f64,
y: f64,
}

extend Point {
fun new(x: f64, y: f64) -> Point {
return Point { x = x, y = y };
}
}

fun main() -> i64 {
let p := Point::new(1.0, 2.0);
return p.x as i64;
}
Formal rules
Legality Rule №1

A function declared in an extend block without a self, &self, or &var self parameter is an associated function and is called through its target type with ::.

First-Class Functions

Functions are first-class values and can be assigned, passed, and returnedL2:

fun add(a: i64, b: i64) -> i64 {
return a + b;
}

fun apply(f: |i64| -> i64, x: i64) -> i64 {
return f(x);
}

fun main() -> i64 {
let f := add;
let inc := |x: i64| -> i64 { return x + 1; };
return f(1, 2) + apply(inc, 4);
}

The type of a function or closure is written as |ParamTypes| -> ReturnType (RFC-0154; (ParamTypes) -> ReturnType before v0.13.0). -> and the return type are always present in a written function type.

A named function declared with its own <T> generics (fun identity<T>(x: T) -> T { ... }) may always be called directly (identity(3), identity::<i64>(3)).

Changed inv0.13.0RFC-0138generic named functions are no longer limited to direct calls or explicitly typed contexts

A generic named function may be bound with a bare, unannotated let; the binding stays polymorphic, so its later uses may each instantiate it differently (let alias = identity; alias(3); alias("x");). It may also be passed as a higher-order argument when the receiving parameter position is concrete — for example, apply(identity, 3) when apply's parameter is |i64| -> i64, not itself generic.

Referencing it in a position where nothing pins down a concrete instantiation — a parameter position that is itself still generic in the callee (rank-2), or an expression position with no expected type and no enclosing let — is still T0003. There is also no standalone instantiation-without-calling value form: identity::<i64> not immediately followed by a call is a parse error, not a type error — see TurbofishL3.

A written function type (|T| -> U) is move-only for its by-value useL3, at every nesting depth: a function value may be called through it any number of times, but using it by value twice — let a := f; let b := f; — is a use-after-move, even when the underlying value (a named function, or a closure whose captures are all Copy) is itself copyable (ClosuresL20). Copyability doesn't survive the written type: it's erased the moment such a value flows into a slot whose written type is a function typeL4 — a let / var binding, an ascription, an argument, or a return — and isn't recovered further downstream. A bare generic type parameter is not a written function type and keeps the resolved value's own capability. The full surface — a copy |T| -> U qualifier for an explicitly-copyable callable, and a distinct "capability unknown" state — is deferred to RFC-0163 (v0.17.0), which refines this move-only state rather than replacing it.

Formal rules
Legality Rule №1

Function and closure types use |ParameterTypes| -> ReturnType (RFC-0154); -> and the return type are always written. Neither (ParameterTypes) -> ReturnType (the spelling before v0.13.0) nor fun(ParameterTypes) -> ReturnType is a function-type syntax.

Referenced by: rfc-0041, rfc-0154

Tested by (2)
Legality Rule №2
Sincev0.13.0RFC-0138

A non-generic named function and a closure are values of their function type and may be bound, passed as arguments, and returned as results. A generic named function (declared with its own <T> generics) may be called directly, bound with a bare unannotated let (staying polymorphic across that binding's own later uses), or passed as a higher-order argument whose receiving parameter position is itself concrete. Referencing it anywhere else that doesn't pin down a concrete instantiation — including a parameter position that is itself still generic in the callee — is T0003.

Referenced by: rfc-0138

Tested by (8)
Legality Rule №3
Sincev0.13.0RFC-0166

A written function type (|T| -> U) has move-only by-value use-multiplicity at every nesting depth, matching exactly against a copyable one: a value of it may be called (subject to once / var) and moved, but not duplicated by value. A bare generic parameter is not a written function type.

Referenced by: rfc-0166

Tested by (6)
Legality Rule №4
Sincev0.13.0RFC-0166

A function value the compiler proved copyable is accepted into a written function-type slot by moving; the slot's copyability is not carried by the written type and is not recoverable downstream.

Tested by (2)

Closures

Anonymous functions are written with the [captures]? once? var? |params| (-> ret)? { body } formL1:

fun main() -> i64 {
let double := |x: i64| -> i64 { return x * 2; };
return double(5);
}

Capture lists

A closure that reads an outer binding captures it. A capture list […] before the parameter list names each captured binding with a specifier:

fun main() {
var count := 0;
let cfg := Config::load(); // non-Copy, read-only in the closure
let name := "log"; // non-Copy, moved in

let handler := [&var count, &cfg, name] var |req: Request| -> Response {
count += 1;
route(req, cfg, name)
};
}
  • [&var x] captures x by exclusive reference; the body may read and write it.
  • [&x] captures x by shared reference; the body may only read it.
  • [x] captures x by value — a copy for a Copy binding, a move for a non-Copy one (the outer binding is consumed).
  • [x.clone()] captures an explicit independent copy of a Clone binding, leaving the outer binding usable.

The list is required whenever the closure references a free non-Copy local, or captures anything by & / &varL6; it is omissible only when every free variable is a Copy binding captured by value, or there are none. When present it is exhaustiveL7: every free local binding the body references must appear. Module-level functions, constants, types, and aspects are not captures and never appear in the list.

A &var capture requires the outer binding to be declared varL13, and a closure literal cannot reference its own let bindingL13. A [&x] capture is read-only: assigning to it, or taking &var of it, in the body is a compile error at the captureL21, not a silent mutating upgrade. Capturing a free variable of a type parameter T follows the bounds in scope at the closure's definitionL17 — unbounded T is non-Copy for every instantiation. When an inner closure captures a binding that is itself an enclosing closure's capture, the enclosing closure must list it, an inner [s] cannot move out of an enclosing [&s] borrow, and an inner [s] that moves an enclosing-held binding makes the enclosing closure onceL22; an inner & / &var of an enclosing by-value capture is an interim rejectionL11.

Call multiplicity and the mutation axis

A closure's function type carries two written qualifiers besides its parameter and return types:

  • once — invoking the closure consumes one of its captures. Written when the body moves a capture out (returns it, or passes it by value to something that takes ownership). Omitting it when the body consumes a capture is an errorL8; the default is many (reusable).
  • var — invoking the closure mutates a capture. Written when the body assigns to a by-value capture, takes &var of one, or calls a &var self method on one, and always when the closure captures [&var x]L25, regardless of what the body does through it. The default is reading.

The two qualifiers are order-insensitive as a type spellingL24; in a closure literal the fixed order is [captures] once? var? (params)L23. An unqualified literal in a typed position takes its once / var from the expected typeL18 rather than defaulting and then failing. Verification runs in a L19; the two axes are checked independently.

A function value may be used where a less permissive multiplicity is expectedL9 — a many value satisfies a once slot, a reading value satisfies a var slot, a Copy value satisfies a non-Copy slot — at first-order argument, ascription, field-init, and return positions. The reverse is rejected. A conditional's type is the least-permissive of its arms, each arm widening to it. Widening changes only the static typeD12: a widened reading value keeps its plain call behaviour, and a var-typed field that holds one is thereafter observed var, with no re-narrowingD14.

Whether a closure value is Copy is exactly whether every capture is CopyL20; a Copy closure is necessarily many.

A mutating call needs exclusive access to the closure value for the call's durationL10: the callee must be an owned binding, an owned temporary, an exclusive projection off one, or a &var parameter — not a shared-& callee. Overlapping and reentrant mutating calls on the same closure value are rejectedD9. If a var call exits early via ? or return, its partial mutations persist and the closure stays callableD13; a panic is uncatchable and ends the process, so no post-exit state is observable.

Capture semantics

By-value capture of a non-Copy binding moves it into the closure at creation, consuming the outer binding; a Copy binding is copied; the captured environment is built once and not re-cloned per callD5.

fun make_counter() -> var || -> i64 {
let n := 0;
[n] var || -> i64 { n += 1; n } // `n` moved in; writes persist
}

fun main() -> i64 {
var c := make_counter(); // `var`: a `mutating` call is a `&var self`-shaped borrow of `c` (legality-10)
c();
c() // returns 2 — state lives in `c`'s environment
}

A mutating closure's writes to its captures persist across callsD7; a reading closure reads its environment in place. Copying a Copy mutating closure gives the copy independent environment stateD8 — the copies do not share a counter.

A [&x] / [&var x] capture stores a reference in the environment; the borrow is held for the closure value's whole lifetime. Captured owned values are dropped when the closure value is droppedD11, in capture-list order.

Closures satisfy no aspectsL12: ==, <, .clone(), and other aspect-gated operations on closure values do not type-check — including Share once RFC-0158 (1-under-review) adds it. A closure's Send / Sync follows the aggregate rule over its capturesL14; a mutating closure is not Sync.

Formal rules
Legality Rule №1

An anonymous function is written as an optional capture list […], optional once and/or var qualifiers, a pipe-delimited parameter list |…|, an optional -> ReturnType annotation, and a body block. It may appear wherever an expression is accepted. (RFC-0154: before v0.13.0 the parameter list was parenthesized and -> was written before every body; |…| self-disambiguates, so -> now appears only with a return type.)

Referenced by: rfc-0041, rfc-0154

Tested by (2)
Legality Rule №2

A closure literal is introduced by its |…| parameter list; the -> is written only when a return type is (RFC-0154, superseding RFC-0041's rule that -> precede every body). A bare block { … } with no preceding |…| is a block expression, not a closure.

Referenced by: rfc-0041, rfc-0154

Legality Rule №3

A zero-argument closure is written || { body } (RFC-0154; () -> { body } before v0.13.0). A bare block { … } is not an anonymous function.

Referenced by: rfc-0041

Legality Rule №4

The former anonymous fun(parameters) -> return_type { body } spelling is rejected.

Referenced by: rfc-0041

Legality Rule №5

A capture list is [ followed by zero or more comma-separated capture items and ]. A capture item is &var ident, &ident, ident, or ident.clone(). A binding may not appear more than once in the list, under any combination of specifiers. The literal prefix order is legality-23L23; the function-type spelling's order rules are legality-24L24.

Referenced by: rfc-0050

Legality Rule №23

In a closure literal the prefixes appear in one fixed order: capture list, then once, then var, then the parameter list. var once, a qualifier before the capture list, and a capture list placed after a qualifier are parse errors — even though the corresponding function type spelling is order-insensitive (legality-24L24).

Legality Rule №24

As a function type spelling the once and var qualifiers are order-insensitive: once var |T| -> U and var once |T| -> U denote the identical Type::Fun. The fixed order of legality-23L23 is a grammar rule for closure literals only.

Referenced by: rfc-0153, rfc-0154

Legality Rule №6

A closure must carry a capture list if its body references a free non-Copy local binding, or captures any binding by & or &var. Referencing a free non-Copy local with no capture list is a compile error. A closure whose only free variables are Copy bindings used by value, or which has no free variables, may omit the list.

Referenced by: rfc-0050

Legality Rule №7

When a closure has a capture list, every free local binding its body references must appear in the list, with a specifier consistent with how the body uses it. A referenced free local absent from a non-empty list is a compile error. Module-level functions, constants, types, and aspects are resolved by ordinary name resolution and are never capture items.

Legality Rule №8

once is a written qualifier, verified against the body at the closure's creation site; the default is many. A closure whose body moves a non-Copy capture out — returns it, or passes it by value to something that takes ownership — written without once, is a compile error naming the offending capture and the fix (add once, or stop moving the capture).

Referenced by: rfc-0134

Legality Rule №25

var is a written qualifier, verified against the body at the closure's creation site; the default is reading. A closure whose body assigns to a by-value capture, takes &var of one, or calls a &var self method on one — and always a closure that captures any binding [&var …], regardless of what the body does through it — written without var, is a compile error naming the offending capture and the fix (add var, stop the mutation, or capture [&x] instead).

Referenced by: rfc-0153

Tested by (2)
Legality Rule №9

A function value of call multiplicity m, mutation u, and Copy-ness c satisfies a slot requiring m', u', c' when m is at least as permissive as m' (manyonce), u at least as permissive as u' (readingvar), and c at least as permissive as c' (Copy ≥ non-Copy). The reverse — a less permissive value into a more permissive slot — is rejected.

Referenced by: rfc-0134, rfc-0152

Tested by (2)
Legality Rule №15

The satisfaction rule of legality-9 applies at first-order positions only: a function-typed argument passed to a function-typed parameter, a let / field ascription, a struct-field initializer, and a return. Below the first level of function-type nesting an exact match is required.

Referenced by: rfc-0152

Legality Rule №16

A conditional or match expression whose arms are function-typed has, as its type, the least-permissive arm type under legality-9's order, and each arm is widened to it. A diverging (!-typed) arm does not contribute. A join that would require narrowing an arm is the ordinary type mismatch.

Referenced by: rfc-0152

Legality Rule №10

A mutating call e(args) requires e to denote a place the caller can exclusively borrow for the call's duration: an owned var binding, an owned temporary, an exclusive (&var / owning) projection off one, or a &var parameter — the ordinary &var self receiver rule. An owned but non-var (let) binding, or any shared-& callee (a &Self / &self receiver, a place reached through a & step, or an &-captured closure), is a compile error. This holds for every mutating closure, whether it mutates its own by-value captures or only drives mutation through a captured &var.

Referenced by: rfc-0153

Legality Rule №11

An inner closure may not capture, by & or &var, a binding that is a by-value capture of an enclosing closure. It may capture such a binding by value ([s], which moves it out of the enclosing closure's environment). This restriction is lifted when the borrow checker lands.

Legality Rule №12

A closure value satisfies no aspects: ==, <, .clone(), and other aspect-gated operations on a closure value are a compile error. The only way to duplicate a closure value is the by-value copy available when it is Copy (legality-20L20). Structural equality of two function types is a type relation and does not make their values comparable.

Legality Rule №13

A [&var ident] capture requires ident to be a var binding; capturing a non-var binding by &var is a compile error. A closure literal cannot reference its own let binding — the name is not in scope inside its own initializer.

Referenced by: rfc-0050

Legality Rule №14

A closure value is Send (respectively Sync) when every one of its captures is Send (respectively Sync), applying the reference rules for &T / &var T captures. A mutating closure value is additionally not Sync.

Legality Rule №17

Whether a captured free variable of type parameter T is Copy is decided from the bounds in scope at the closure's definition, not re-decided per instantiation. A capture of an unbounded T is non-Copy: [t] moves it, and a body that moves it out makes the closure once for every instantiation of the enclosing generic, T = i64 included. A definition wanting the copyable behaviour adds T: Copy, after which [t] is a copy and consumes nothing.

Legality Rule №18

An unqualified closure literal in a position with an expected function type — a let or parameter ascription, a struct-field initializer, a return, or the tail expression of a typed block — is checked against that type's once / var qualifiers rather than taking the many / reading default and then failing. When the expected type does not fix a qualifier (an unresolved inference variable, or a bare generic parameter), the literal takes the default and legality-9L9 widening resolves any remaining gap at the concrete site.

Legality Rule №19

A closure literal is resolved in a fixed stage order: (1) capture classification (referenced free variables, each one's Copy-ness, whether a list is required, and — when present — its exhaustiveness and specifier-match); (2) use_multiplicity (legality-20L20); (3) once verification (legality-8L8); (4) var verification (legality-25L25). The first failing stage is reported; later stages are suppressed. Stages 3 and 4 are independent: a body that both consumes and mutates without the qualifiers is reported against both.

Referenced by: rfc-0050

Legality Rule №20

A closure value is Copy exactly when every one of its captures is Copy. [x] of a Copy binding and [&x] (a shared reference is Copy) preserve it; [x] of a non-Copy binding, [x.clone()] of a non-Copy type, and [&var x] (an exclusive reference is not Copy) make the closure non-Copy. A Copy closure is necessarily many — it holds nothing non-Copy for a call to consume.

Referenced by: rfc-0134

Legality Rule №21

A closure that captures x by shared reference [&x] and whose body assigns to x, takes &var x, or calls a &var self method on it is a compile error at the capture — the closure is not silently reclassified mutating. The fix is to capture [&var x], which requires x to be a var binding (legality-13L13) and makes the closure mutating (legality-25L25).

Legality Rule №22

When an inner closure captures a binding s that is itself a capture of an enclosing closure: the enclosing closure must list s in its own capture list; an inner [s] requires the enclosing closure to hold s by value (naming an enclosing [&s] / [&var s] capture is a move out of borrowed content error); and an inner [s] that moves an enclosing-held s makes the enclosing closure once (legality-8L8), even if the inner closure is never called. An inner [&s] / [&var s] does not change the enclosing closure's multiplicity, but is subject to legality-11L11's interim borrow restriction.

Referenced by: rfc-0050

Dynamic Semantics №1

When a closure is created, each free variable named in its capture list (or, for a listless closure, each free Copy variable) is placed into the closure's environment according to its specifier: [x] moves a non-Copy value / copies a Copy value, [x.clone()] stores an independent copy, [&x] / [&var x] store a reference.

Referenced by: rfc-0006

Dynamic Semantics №2

A reading closure does not modify its environment; a mutating closure modifies it in place. In neither case does a write inside the closure body affect an outer binding that was captured by value — the closure operates on its own environment copy.

Referenced by: rfc-0006

Dynamic Semantics №3

Closures that capture the same binding by [&x] / [&var x], or that capture the same reference value, observe the same referent; a write through an exclusive reference by one closure is visible through the others.

Referenced by: rfc-0006

Tested by (3)
Dynamic Semantics №4

A closure that escapes its defining function while holding a captured owned value keeps that value alive; the environment travels with the closure value. A closure holding a captured reference cannot outlive the referent (checked by the borrow checker when it lands; unenforced before then).

Referenced by: rfc-0006

Dynamic Semantics №5

Capturing a non-Copy binding by value ([x]) moves it, consuming the outer binding; a Copy binding captured by value is copied; [x.clone()] produces an independent copy regardless of Copy-ness. The captured environment is constructed once, at closure creation, and is not re-cloned per call.

Referenced by: rfc-0157

Tested by (3)
Dynamic Semantics №7

A mutating closure's assignments to its by-value captures are retained in its environment and are visible to subsequent calls of the same closure value — the closure holds private mutable state.

Referenced by: rfc-0153

Dynamic Semantics №8

Copying a closure value whose captures are all Copy copies its environment. The copies have independent environment state: a mutating call on one does not affect the other.

Referenced by: rfc-0153

Dynamic Semantics №9

For the dynamic extent of a mutating call the callee place is exclusively borrowed. A second mutating call on the same closure value reached from inside the first — directly or through a structure the body can reach — is rejected: before the borrow checker lands, as a runtime error; after, as a static borrow conflict.

Referenced by: rfc-0153

Dynamic Semantics №10

A once call consumes the callee at the call expression, before the body runs. Any later use of that closure value is a moved-value error, whether the body returned normally or exited early.

Referenced by: rfc-0134

Dynamic Semantics №11
Planned forv0.14.0RFC-0071metel-core#261closure-environment destruction follows the language's general destructor-execution work

When a closure value is dropped, its environment is dropped: each owned capture is dropped in capture-list order, as a struct's fields are. A once-consumed or partially-moved environment drops only its still-owned captures.

Exempt from fixture coverage — blocked on metel-core#261: Destructor invocation and observable drop order are scheduled separately. Non-empty drop bodies are rejected until #261 lands, so a fixture cannot observe closure-environment field destruction or its order.

Dynamic Semantics №12

Multiplicity widening (legality-9L9) changes only the static type at the slot, never the closure value's runtime behaviour. A reading closure value widened into a var-typed slot is still invoked by the plain, non-exclusive call path and consults no in-call flag, because call lowering branches on the closure value's own mutation axis, not on the slot type. A many value in a once slot is likewise not consumed by the call.

Tested by (2)
Dynamic Semantics №13

An early exit from a mutating call is a ?-propagated Err or an early return, not a panic — a panic is uncatchable and leaves no observable post-exit state (runtime.mdPanics D1). On such an exit from a var (non-once) call, mutations already made stay in the environment, the exclusive borrow and the in-call flag release as the frame returns, and the closure remains an ordinary, callable value; there is no rollback, and the next call runs the body from the top over that state. A once / once var closure was already consumed at the call expression (dynamics-10D10) regardless of how the body exited; its still-owned fields are dropped when the value goes out of scope.

Dynamic Semantics №14

Storing a reading closure into a var-typed field, or returning it where a var function type is named, yields a value thereafter observed as var: every later read of that field or result is a var value that callers must invoke under exclusive access (legality-10L10), even though the underlying closure never mutates. The coercion is one-way — there is no automatic re-narrowing back to reading.

Turbofish

Sincev0.8.0

When a generic function's type parameters cannot be inferred from the arguments, they can be specified explicitly with turbofish syntax: name::<T, U>(args)L1.

fun identity<T>(x: T) -> T { x }

fun main() -> i64 {
let x := identity::<i64>(42);
return x;
}

Turbofish is most useful when two or more independent type parameters must be pinned at the call site — for example, a zip function that pairs elements from arrays of different types:

fun zip<A, B>(a: A[], b: B[]) -> (A, B)[] { /* ... */ }

fun main() {
let pairs := zip::<i64, String>([1, 2], ["a", "b"]);
}

Type ascription (: T) remains available for annotating the result type. Turbofish and ascription can be used together:

let result := parse::<i64>("42") : Perhaps<i64>;
Formal rules
Legality Rule №1

A generic call may supply explicit type arguments with name::<T, U>(arguments), pinning each named parameter to the given type. A pinned type must satisfy that parameter's own bounds (e.g. T: Display).

Referenced by: rfc-0023

Legality Rule №2

The call's arguments must unify with their pinned types exactly as they would with an inferred one.

Referenced by: rfc-0023

Tested by (3)
Legality Rule №3

Turbofish is a call-postfix production, fused to the immediately following call's parentheses. There is no standalone instantiation-without-calling value form: name::<T> not immediately followed by (arguments) is a parse error.

The ? Operator

SinceMatching-error ?v0.1.0From-based error coercionv0.4.0

Inside a function returning Result<T, E>, ? propagates errors earlyD2:

fun parse_int(s: String) -> Result<i64, String> {
if (s == "21") {
return Ok { value = 21 };
}
return Err { error = "not a number" };
}

fun parse_and_double(s: String) -> Result<i64, String> {
let n := parse_int(s)?; // returns Err early if parse_int fails
return Ok { value = n * 2 };
}

fun main() -> i64 {
match (parse_and_double("21")) {
Ok { value } => value,
Err { error } => 0,
}
}

? desugars to: if the expression is Err(e), return Err(E2::from(e)) immediatelyD2 (where E2 is the enclosing function's error type); otherwise unwrap to the Ok valueD1.

The inner expression's error type E1 and the function's return error type E2 must satisfy E2: From<E1>L2. When E1 == E2 no conversion is performed. When they differ, From::from is called automatically on the error value before re-wrapping in Err.

? does not apply to Perhaps<T>L1 in this language version. It is supported only for Result<T, E>, so using ? on a Perhaps value is a type error (T0001) rather than an early None return.

Formal rules
Legality Rule №1

The ? operator requires a Result<T, E> operand; applying it to Perhaps<T> or any other type is a type error.

Legality Rule №2

The enclosing function's return type must be Result<U, E2>, and the operand error type E1 must equal E2 or satisfy E2: From<E1>.

Dynamic Semantics №1

Evaluating Ok { value }? produces value and evaluation continues in the enclosing function.

Dynamic Semantics №2

Evaluating Err { error }? immediately returns Err { error } from the enclosing function; when the error types differ, the returned error is E2::from(error).

Native Functions (Standard Library Only)

Standard library declarations may be marked native, binding them to an implementation provided by the host interpreter instead of a Metel body:

// from std::core — not writable in user code
native(@std.core.println) public fun println<T>(x: T);
native(@std.core.clock) public fun clock() -> i64;

A native declaration has no body — it ends with ; instead of a block. The @-path inside the parentheses is the binding key that selects the host implementation. The form is also valid on methods inside extend blocks; for example, the primitive Display implementations in std::core are declared this way:

extend i64: Display {
native(@std.core.to_string) fun to_string(&self) -> String;
}

native is reserved for the standard libraryL1. Using it in any module outside the std namespace is a compile error, and user projects cannot place modules under std:: (see §Modules). From the caller's side, native functions are indistinguishable from ordinary functions: they are imported, typechecked, and called exactly like any other declaration — the binding key is an implementation detail of the standard library's source.

Native declarations must annotate every parameter typeL3; an omitted return type means the function returns ().

Formal rules
Legality Rule №1

Only a declaration in the std namespace may use the native modifier; a native declaration in a user module is rejected.

Legality Rule №2

A native declaration has a dotted @ host-binding key and no Metel body: it ends with ;.

Legality Rule №3

Every native-function parameter has an explicit type annotation. An omitted return type denotes ().