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 is typed either by the untyped keyword function, which accepts any function, or by a declared signature, which additionally constrains the argument:

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.

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:

function twice(h: nuf) -> function
{
  function result<V: numerics>(attribute<V> x) -> attribute<V> := h(h(x));
}

see also

since version

20.9.0