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
messagestruct that is notabstract. - The return type must be a
messagestruct that is notabstract. - 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
givenclause holds at least one contextual parameter. - Their types are pairwise distinct within the clause.
?nameis 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
givingomitted, each contextual parameter of the callee is matched by type against the enclosing transform's owngivenclause. Distinct types make that match unambiguous. - An explicit
givingclause 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-paramsA 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-scopegiving 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-targetThe 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-countA 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-typeA 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