Skip to main content

transform

A named translation between message types. Every parameter type and the return type must resolve to a non-abstract message struct. A given clause may add parameters that the call site supplies by type.

message struct Celsius { temp_c: float64; }
message struct Fahrenheit { temp_f: float64; }

transform CelsiusToFahrenheit(reading: Celsius) -> Fahrenheit =
    Fahrenheit { temp_f = reading.temp_c * 9.0 / 5.0 + 32.0; };

Declarations​

A transform is declared and called like a function, and the result type is the declared return type.

  • Each parameter type must be a message struct that is not abstract.
  • The return type must be a message struct that is not abstract.
  • A transform declares at least one parameter. An empty () parses, and the checker rejects it.
  • Parameter names are unique, contextual parameters included.
  • The body must be assignable to the return type.
message struct Celsius { temp_c: float64; }
message struct Fahrenheit { temp_f: float64; }

transform CelsiusToFahrenheit(reading: Celsius) -> Fahrenheit =
    Fahrenheit { temp_f = reading.temp_c * 9.0 / 5.0 + 32.0; };

CelsiusToFahrenheit(Celsius { temp_c = 100.0; })   // => Fahrenheit { temp_f: 212 }

Contextual parameters​

A given clause declares parameters that travel with the call rather than in its argument list. Each is ?name: Type, and the body refers to one as ?name.

  • A given clause holds at least one contextual parameter.
  • Their types are pairwise distinct within the clause.
  • ?name is valid only inside a transform that declares it.
  • A call supplies them explicitly with a giving (…) postfix clause, one argument per contextual parameter, matched by position.
  • With giving omitted, each contextual parameter of the callee is matched by type against the enclosing transform's own given clause. Distinct types make that match unambiguous.
  • An explicit giving clause bypasses the implicit match.
  • A contextual transform called with neither an explicit clause nor a matching enclosing one is an error.
newtype SensorId { value: uint32; }

message struct GroundFix { lat_deg: float64; }
message struct TrackReport { lat_deg: float64; sensor_id: uint32; }
message struct TrackBatch { lat_deg: float64; sensor_id: uint32; }

transform FixToTrack(fix: GroundFix) -> TrackReport given (?sensor: SensorId) =
    TrackReport { lat_deg = fix.lat_deg; sensor_id = ?sensor.value; };

transform FixToBatch(fix: GroundFix) -> TrackBatch given (?sensor: SensorId) = {
    let report: TrackReport = FixToTrack(fix);
    TrackBatch { lat_deg = report.lat_deg; sensor_id = report.sensor_id; };
};

const fix: GroundFix = GroundFix { lat_deg = 38.9; };
const radar: SensorId = SensorId { 7 };

const report: TrackReport = FixToTrack(fix) giving (radar);

FixToTrack(fix) inside FixToBatch names no context of its own. Its ?sensor is filled from FixToBatch's given clause, which declares the same type.

Errors​

A transform with no parameters​

message struct Fahrenheit { temp_f: float64; }

transform Freezing() -> Fahrenheit = Fahrenheit { temp_f = 32.0; };   // error: transform-min-params

A parameter that is not a message struct​

struct Celsius { temp_c: float64; }
message struct Fahrenheit { temp_f: float64; }

transform CelsiusToFahrenheit(reading: Celsius) -> Fahrenheit =   // error: transform-param-type
    Fahrenheit { temp_f = 32.0; };

A return type that is not a message struct​

message struct Celsius { temp_c: float64; }

transform CelsiusToKelvin(reading: Celsius) -> float64 =   // error: transform-return-type
    reading.temp_c + 273.15;

Two contextual parameters of one type​

Implicit passing matches by type, so a given clause cannot hold the same type twice.

newtype SensorId { value: uint32; }

message struct GroundFix { lat_deg: float64; }
message struct TrackReport { lat_deg: float64; sensor_id: uint32; }

transform FixToTrack(fix: GroundFix) -> TrackReport
    given (?primary: SensorId, ?backup: SensorId) =   // error: context-param-duplicate-type
    TrackReport { lat_deg = fix.lat_deg; sensor_id = ?primary.value; };

A contextual reference outside a transform​

newtype SensorId { value: uint32; }

const sensor_id: uint32 = ?sensor.value;   // error: unresolved-reference, context-param-ref-scope

giving on a call that takes no context​

newtype SensorId { value: uint32; }

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

const radar: SensorId = SensorId { 7 };
const total: int32 = add(40, 2) giving (radar);   // error: giving-target

The wrong number of contextual arguments​

newtype SensorId { value: uint32; }

message struct GroundFix { lat_deg: float64; }
message struct TrackReport { lat_deg: float64; sensor_id: uint32; }

transform FixToTrack(fix: GroundFix) -> TrackReport given (?sensor: SensorId) =
    TrackReport { lat_deg = fix.lat_deg; sensor_id = ?sensor.value; };

const fix: GroundFix = GroundFix { lat_deg = 38.9; };
const radar: SensorId = SensorId { 7 };

const report: TrackReport = FixToTrack(fix) giving (radar, radar);   // error: giving-arg-count

A contextual argument of the wrong type​

newtype SensorId { value: uint32; }

message struct GroundFix { lat_deg: float64; }
message struct TrackReport { lat_deg: float64; sensor_id: uint32; }

transform FixToTrack(fix: GroundFix) -> TrackReport given (?sensor: SensorId) =
    TrackReport { lat_deg = fix.lat_deg; sensor_id = ?sensor.value; };

const fix: GroundFix = GroundFix { lat_deg = 38.9; };

const report: TrackReport = FixToTrack(fix) giving ("Viper-1");   // error: giving-arg-type

A contextual transform called with no context available​

newtype SensorId { value: uint32; }

message struct GroundFix { lat_deg: float64; }
message struct TrackReport { lat_deg: float64; sensor_id: uint32; }

transform FixToTrack(fix: GroundFix) -> TrackReport given (?sensor: SensorId) =
    TrackReport { lat_deg = fix.lat_deg; sensor_id = ?sensor.value; };

const fix: GroundFix = GroundFix { lat_deg = 38.9; };

const report: TrackReport = FixToTrack(fix);   // error: giving-required
  • function — the same declaration without the message-type restriction.
  • struct — the message and abstract modifiers a parameter type answers to.
  • newtype — the distinct types that make a given clause resolvable.