Named Functions
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 }) -> aa 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.
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. Effect metadata is descriptive at the named function boundary; execution and bounded effectful traversal use the ordinary workflow execution path.
Compatibility And Diagnostics
Named functions remain non-recursive and lower to the same LET expansion as
before. There is no new runtime function object or calling convention.
Inference happens once at the definition, and the scheme is instantiated at
each call before expansion:
GRID_DEF_TYPEmeans the definition body contains incompatible known types.GRID_DEF_CALL_TYPEmeans concrete call arguments do not satisfy the inferred signature or collection protocol.GRID_DEF_IMPORT_CONFLICTmeans two visible definitions would claim the same canonical function name.- Existing arity and partial-application diagnostics are unchanged.
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 string and effects array.
Compiler tooling can use the structured
ParsedProgram::function_signatures() API instead of parsing that display
string.