Skip to main content

function

A top-level declaration taking at least one parameter and returning a declared type. The body is either an expression after = or a block whose tail expression is the return value.

function slant_range_m(ground_range_m: float64, altitude_m: float64) -> float64 =
    sqrt(ground_range_m * ground_range_m + altitude_m * altitude_m);

function classify(range_m: int32) -> string {
    let near: bit = range_m < 5000;
    if near then "near" else "far";
}

Declarations​

A function may be imported by another module. Parameter types and the return type may be any type.

  • An empty parameter list is a parse error.
  • Parameter names are unique within the declaration.
  • The function's name is unique among the file's declarations.
  • The body must be assignable to the declared return type.
  • Parameters are in scope in the body and nowhere else.
  • The function's own name is in scope in its body, so it may call itself. A recursive call types at the declared return type.

The body forms differ only in punctuation. An expression body is = expr;. A block body is a braced block with no = and no closing semicolon. A block is itself an expression, so = { … }; is the same body written the other way.

function bearing_delta_deg(from_deg: int32, to_deg: int32) -> int32 = (to_deg - from_deg) % 360;

function countdown(remaining: int32) -> int32 =
    if remaining <= 0 then 0 else countdown(remaining - 1);

function sweep_area_m2(diameter_m: float64) -> float64 = {
    let radius_m: float64 = diameter_m / 2.0;
    3.14159 * radius_m * radius_m;
};

Calls​

callee(args) is a postfix form. The result type is the callee's declared return type.

  • The argument count must match the declared parameter count.
  • Arguments match by position, and each must be assignable to its parameter's type.
  • A numeric literal argument pins to the parameter's type, so it needs no ascription.
  • A call reached through a module alias is written alias.name(args).
  • A call result takes further postfix operators, so make_point(38.9, -77.0).lat_deg is one expression.
struct GeoPoint { lat_deg: float64; lon_deg: float64; }

function make_point(lat_deg: float64, lon_deg: float64) -> GeoPoint =
    GeoPoint { lat_deg = lat_deg; lon_deg = lon_deg; };

function add(a: int32, b: int32) -> int32 = a + b;

add(40, 2)                          // => 42
make_point(38.9, -77.0).lat_deg     // => 38.9
Functions Are Not Values

There is no function type: a function cannot be passed as an argument, returned, or held in a let. A call resolves to a declaration, which is why map and fold take a block body rather than a function to apply.

Errors​

Declaring the same parameter twice​

function bearing_delta_deg(from_deg: int32, from_deg: int32) -> int32 =   // error: duplicate-param
    from_deg % 360;

A body that is not the return type​

function classify(range_m: int32) -> string = range_m + 1;   // error: type-mismatch

Calling with the wrong number of arguments​

function add(a: int32, b: int32) -> int32 = a + b;

const total: int32 = add(40);   // error: call-arg-count

An argument of the wrong type​

function add(a: int32, b: int32) -> int32 = a + b;

const total: int32 = add("Viper-1", 2);   // error: call-arg-type
  • transform — the same shape restricted to message types, with contextual parameters.
  • contract — the other named body, and the one nothing calls.
  • builtin functions — the functions already in scope with no declaration.
  • block, let, assert — the statements a block body holds.