-
Notifications
You must be signed in to change notification settings - Fork 1
Function definition
Note: the syntax described on this page is available from GeoDMS version 20.9.0 and later.
A function is a typed, reusable unit of model logic. Like a Template, it packages calculation rules so the same logic can be applied to different arguments. Unlike a template, a function declares the value type and domain of every parameter and of its result, and is type-checked once, at its definition — not re-checked separately at each call site.
Because a function has a declared result type, an application F(args) is an expression of that type, and so it nests inside larger expressions like any operator or function call.
The parameters follow the function name in parentheses, separated by semicolons. Each is an ordinary typed declaration, and together they form a telescope: a later parameter may refer to an earlier one. A domain parameter is a unit; an attribute parameter names its domain in parentheses.
function CongestionRatio(
unit<uint32> Rd;
attribute<float64> flow (Rd);
attribute<float64> cap (Rd))
Here flow and cap are attributes over the unit Rd, which is itself a parameter. The argument list at the call is matched against this telescope by position, and its length (arity) is checked at the call.
Declarations may also be written in name: type style, and several names may share a single type and calculation rule:
Rd: unit<uint32>;
c1, c2: parameter<float64> := 1.0;
The result specification follows ->. It gives an optional result name, the result type, and — after := — a result expression that relates the result to the body:
-> attribute<float64> (Rd) := min_elem(raw, 1.0);
If a name is given it precedes the type; otherwise the synthesised result item is named result:
-> link_counts: attribute<uint32> (nw/nodeset) := pcount(nw/F1) + pcount(nw/F2);
A function may carry an optional body block in braces, holding intermediate items. How the result expression relates to that body gives three forms.
Form a — the result expression is a plain name designating a body item (matched case-exactly, else case-insensitively-unique):
function CongestionRatio3(
Rd: unit<uint32> { flow: attribute<float64>; cap: attribute<float64>; })
-> attribute<float64> (Rd) := Result
{
attribute<float64> raw (Rd) := Rd/flow / Rd/cap;
attribute<float64> result (Rd) := min_elem(raw, 1.0);
}
Form b — the result expression is the calculation rule of the result, written over body items:
function CongestionRatio(
unit<uint32> Rd; attribute<float64> flow (Rd); attribute<float64> cap (Rd))
-> attribute<float64> (Rd) := min_elem(raw, 1.0);
{
attribute<float64> raw (Rd) := flow / cap;
}
Form c — expression-only, with no body block at all:
function CongestionRatio2(
unit<uint32> Rd { attribute<float64> flow; attribute<float64> cap; })
-> attribute<float64> (Rd) := min_elem(Rd/flow / Rd/cap, 1.0);
A unit parameter may carry a member block declaring the attributes the argument table must supply. This is a declared interface only: the body reaches the members through the parameter, as Rd/flow.
unit<uint32> Rd {
attribute<float64> flow;
attribute<float64> cap;
}
At the call the parameter binds by reference to the actual argument — the argument's data is not copied.
A function's scope is closed: only its parameters, its own body items and explicitly imported containers are visible; the surrounding namespace is not. To bring outside names in, add a using clause after the parameter list. Its paths are resolved at the definition site.
function Capped(
unit<uint32> Rd;
attribute<float64> x (Rd))
, using = shared
-> attribute<float64> (Rd) := min_elem(x, limit);
Here limit is found in the imported container shared.
A parameter declared with the item keyword is a meta-reference parameter: its argument binds as a raw item reference rather than as a computed value, so the body can read the argument item's own metadata. The built-in property accessors are defined this way:
function name(item t) -> parameter<string> := PropValue(t, 'name');
An application F(args) is an expression of the result type, and calls compose freely — a function may apply another in its result expression, and a top-level item may apply that function in turn:
function F(unit<uint32> Rd; attribute<float64> x (Rd)) -> attribute<float64> (Rd) := 2.0 * x;
function useF(unit<uint32> U; attribute<float64> y (U)) -> attribute<float64> (U) := F(U, y);
attribute<float64> c (Road) := useF(Road, Road/flow);
Where a function has a body block whose intermediate items you want to keep, instantiate lands the whole body as a container and you reach the result through it:
container cr := instantiate CongestionRatio(Road, Road/flow, Road/cap);
attribute<float64> congestion (Road) := cr/result;
A Template and a function both package reusable model logic, but differ in what is checked and when.
| aspect | template | function |
|---|---|---|
| parameters | untyped; the "first N subitems", matched by argument count | declared, named, typed telescope; arity checked at the call |
| checking | body checked per instantiation, at every call site | type-checked once, at the definition |
| composability | cannot be used as a sub-expression |
F(args) is an expression of the result type, nestable |
| scope | definition scope, with the surrounding namespace as fallback | closed: parameters, locals and explicit using imports only |
| result | some subitem, by convention; untyped | a typed, named result (default result) |
- Template
- Function-signature
- Type-alias
- Generic-function
- Anonymous-function
- Closure
- Variadic-parameters
- Map
- Operators and functions
20.9.0
GeoDMS ©Object Vision BV. Source code distributed under GNU GPL-3. Documentation distributed under CC BY-SA 4.0.