-
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.
Identifiers in a function's expressions resolve to what is visible at the point of definition, as everywhere else in the language. A body sees, in order:
- the function's parameters,
- its own body items (nearest enclosing body scope first),
-
containers explicitly imported with a
usingclause, - the surrounding namespace of the definition — the definition parent and its ancestors, with their
usingdirectives — exactly as any ordinary calculation rule written at that spot would see them, - the auto-imported prelude of standard function definitions, as implicit outermost namespace.
So a unit, parameter or sibling function declared next to the function is simply visible in its body, without any import:
parameter<float64> outside := 42.0;
function F(unit<uint32> Rd; attribute<float64> x (Rd))
-> attribute<float64> (Rd) := x * outside;
What a function never sees is the call site. The only channel from the caller is the argument list, and argument expressions are resolved in the caller's scope. This call-site isolation is the crucial difference from a Template, whose instantiated body also keeps names at the instantiation point reachable as a fallback.
A using clause after the parameter list imports additional containers into the function's scope; 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 first, with the instantiation site as fallback | lexical definition scope; the call site is never visible |
| 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.