Function signature
Note: the syntax described on this page is available from GeoDMS version 20.9.0 and later.
A function signature describes the type of a function — its parameters and its result — without giving a body. It answers “what shape of function goes here?” rather than “what does the function compute?”.
A function signature is written (params) -> resultType;: a parameter list and a result type, with no result expression and no body.
A signature is most useful as a named Type alias, so it can be referred to by name wherever a type is expected:
unary_fn = (unit<uint32> D; attribute<float64> v (D)) -> attribute<float64> (D);
This declares unary_fn as the type of any function that takes a domain D and an attribute<float64> (D), and returns an attribute<float64> (D).
a signature is a type, not a callable
Because a signature has no body, applying a signature-only function is an error. It names a type; it does not compute anything. You use a signature to type a parameter or a result, never to call it directly.
typing a function-valued parameter
A higher-order function receives another function as an Argument. The receiving parameter states the signature that argument must match:
container lib
{
function Halve(unit<uint32> D; attribute<float64> v (D))
-> attribute<float64> (D) := v / 2.0;
// f is checked against unary_fn at every application
function ApplyTwiceT(unit<uint32> D; f: unary_fn; attribute<float64> x (D))
-> attribute<float64> (D) := f(D, f(D, x));
}
attribute<float64> quarter (Road) := lib/ApplyTwiceT(Road, lib/Halve, Road/flow);
When ApplyTwiceT is applied, the function passed for f (here lib/Halve) is checked against unary_fn: the arity must match, each parameter’s item class must match (a plain-item signature position acts as a wildcard), and the result class must match. A mismatch is reported at application time. For instance, passing a three-parameter function where unary_fn has two parameters is rejected with an arity-mismatch diagnostic.
Unit relationships between signature positions (the dependent part, such as “the result shares the domain of the argument”) are not yet compared; only the item classes are checked.
Since GeoDMS 20.20.0 a declared signature is the only way to type a function-valued parameter or result. The keyword function on its own is not a type: it says “some function” and constrains nothing, so it is now rejected in both positions, with a message naming the spelling to use. A configuration that wrote f: function or -> function declares a signature and refers to it by name; that has to be changed on upgrade, and the shipped examples/function.dms shows the typed form.
naming the signature
Since GeoDMS 20.20.0 the signature may be named by a path as well as by a plain name. A path is read against the namespace the declaration sits in, which for a parameter is the function itself, the same base an item’s calculation rule is read against:
unary_fn— the nearestunary_fn, looked for in the function itself and then in each enclosing container../unary_fn— dots as elsewhere in the configuration: one dot is that namespace, each further dot one level up, so..is the container holding the function and...the one above that/lib/unary_fn— from the configuration root
Before that version only the plain name resolved, and a path was silently accepted and then ignored, which left the parameter an untyped container without a word about it.
A signature declared further down the configuration is legal: the reference is kept as written and resolved when the whole configuration is in place. One that resolves to nothing then fails that function declaration, naming the parameter; on a function /lib/Apply the failure reads:
parameter 2: the type '/nosuch/unary_fn' does not resolve to a declared item
generic signatures and type application
A signature may be generic, carrying type variables in a function<...> clause. This describes a whole family of functions in one alias:
nuf = function<V: numerics, D: domains>(attribute<V> (D)) -> attribute<V> (D);
nuf is the type of any numeric-attribute-to-same-attribute function. Where a signature is used as a parameter or result type, its type variables can be applied with concrete arguments, written nuf<V, D>:
// f and g must both satisfy nuf at V, D; the result is a nuf at V, D
function compose2<V: numerics, D: domains>(f: nuf<V, D>; g: nuf<V, D>) -> nuf<V, D>
{
function result(attribute<V> x (D)) -> attribute<V> (D) := f(g(x));
}
Here the signature nuf types the two incoming functions and the returned closure, keeping the composition fully typed. The same nuf can equally type a parameter of a function that returns a fresh closure:
nuf1 = function<V: numerics>(attribute<V> x) -> attribute<V>;
function twice(h: nuf) -> nuf1
{
function result<V: numerics>(attribute<V> x) -> attribute<V> := h(h(x));
}
nuf types what twice accepts and nuf1 what it returns; the returned closure takes one attribute and carries no domain of its own, so it is a signature of its own rather than nuf.
see also
since version
20.9.0