Browse documentation
Docs/Language

Named Functions

Grid infers an independent type for every source-authored named function. Definitions still use the compact existing syntax:

Watch it in Grid

See the concept in Grid.

Use the film for the product interaction and this article for the complete contract and reference detail.

Named Functions

Grid infers an independent type for every source-authored named function. Definitions still use the compact existing syntax:

identity(value) = value
twice(value) = value * 2
amount(row) = row.amount

A1 = identity(42)
A2 = identity("forty-two")
A3 = twice(21)
A4 = amount({ amount: 12, currency: "USD" })

The inferred signatures are:

identity : (a) -> a
twice    : (number) -> number
amount   : ({ amount: a | r }) -> a

a is a type variable. Each call receives a fresh instance, so identity can be used with unrelated types without one call specializing the others. r is an open record row. It says that amount requires an amount field but accepts any additional fields. A concrete object without that field is a compile error.

Collection Protocols

Higher-order collection helpers infer requirements on the collection constructor rather than hard-coding a single container:

same_shape(values) = MAP(values, value => value)

The inferred signature is:

Mappable f => (f<a>) -> f<a>

The protocol names are Sized, Iterable, Sequence, Keyed, Mappable, Foldable, and Scannable. They use the same native collection capability matrix as runtime execution. For example, List, Deque, Map values, Ranked Set, Trie, Interval Index, Multimap values, Stream entry field values, Record, Option, and Result satisfy Mappable; Set does not. Stream maps through its streaming traversal even though it is not an ordinary Iterable collection. Passing a known non-mappable collection to same_shape is therefore rejected during compilation rather than becoming a runtime #VALUE!.

The f<a> notation is schematic rather than a claim that every collection has one type argument. Instantiating f with Map<key, value> retains the key contract and maps only the value: Map<k, a> -> Map<k, b>. Result mapping can visit either active variant, so a generic one-callback wrapper requires its success and error payload contracts to agree; use MATCH when the two branches need different transforms.

MAP preserves its complete collection shape, FILTER preserves its complete input type, REDUCE returns the accumulator type, and SCAN preserves the sequence shape while changing its element type to the accumulator type.

One Name, One Definition

A canonical function name owns one definition and one generalized type scheme. Grid does not form overload sets from several same-name definitions, even when their arities or inferred types differ. Use one polymorphic definition when the behavior is shared, or give distinct behavior distinct names.

Duplicate local definitions report GRID_DEF_DUPLICATE. Two flat EXPOSING imports that contribute the same function name report GRID_DEF_IMPORT_CONFLICT; use USE "…" AS alias to keep independent libraries namespaced. This keeps call resolution lexical, deterministic, and independent of import order.

Forward Calls And Partial Application

Definitions are collected before calls are resolved, so a call can precede its definition. Local definitions shadow same-named builtins throughout the module. Self-recursion and cycles between definitions are rejected with GRID_DEF_RECURSION; forward visibility is not recursive execution.

Use _ for a missing argument when passing a named function as a callback:

tax(rate, amount) = rate * amount
taxes = MAP({ 10, 20, 30 }, tax(0.05, _))

This creates an immutable callable with the rate bound and one missing amount. Use it in an immediate call or a supported higher-order callback position. These callables are not ordinary cell data: storing one in a cell, exporting it across a model boundary, or treating it as a capability is unsupported. The same placeholder form works for supported builtins. Direct calls still require the declared arity.

Effects

The signature also carries an inferred effect row. Connector-backed calls use the canonical label job_queue:<FUNCTION>:

fetch(url) = HTTP_JSON(url)
fetch : (a) -> any ! {job_queue:HTTP_JSON}

An empty effect row means pure. Contracts also identify required authority, determinism, termination, incremental eligibility, and the execution route. Effectful callbacks are not admitted by pure MAP, FILTER, REDUCE, or SCAN; use the supported bounded traversal contract when appropriate. Capability-backed network reads have a stricter one-stage tail-call contract; they cannot be moved into a traversal callback. See external functions.

Compatibility And Diagnostics

Named functions remain non-recursive. One typed callable definition is shared by its direct call sites rather than copying a LET expansion at every call. Inference happens once at the definition and the scheme is instantiated at each call:

  • GRID_DEF_TYPE means the definition body contains incompatible known types.
  • GRID_DEF_CALL_TYPE means concrete call arguments do not satisfy the inferred signature or collection protocol.
  • GRID_DEF_IMPORT_CONFLICT means two visible definitions would claim the same canonical function name.
  • Arity errors and unsupported callable-storage positions are compile errors.

Grid is gradual at external boundaries. An untyped cell reference or a specialized builtin without a compiler signature contributes any; it does not cause a speculative rejection. Literal values, authored type contracts, object fields, operators, control flow, lambdas, and the core higher-order collection operations remain statically checked.

Scalar mismatches that loose coercion can represent remain source-compatible: they emit the type diagnostic as a warning. strict promotes them to compile errors. Structural violations—missing required record fields, incompatible collection constructors, and absent collection protocols—are always errors because scalar coercion cannot repair them.

FUNCTION(name) includes the inferred type, effects, callable identity, semantic fingerprint, captures, and structured contract. Required network selectors and logical secret purposes may appear; grants and credentials never do. WHY(target) explains the selected callable route and dependency boundary; for capability calls it can also show admission/refusal and cache identity.