Skip to main content

if

A choice between two branches on a bit condition. As an expression it has a value and the else is mandatory; as a statement it has no value and each branch is a list of statements.

const raw_bearing_deg: int32 = 400;

const bearing_deg: int32 = if raw_bearing_deg > 360 then raw_bearing_deg - 360 else raw_bearing_deg;

Branches​

The condition has type bit, only the taken branch evaluates, and the whole if has the type the two branches share.

  • The else is required. An if without one is a parse error.
  • Both branches must have a common type, which is the type of the expression.
  • Two structs in the same hierarchy have their nearest common ancestor as that type.
  • Two different numeric widths have no common type. int32 and int64 branches are rejected.
  • There is no else if. The else branch is another if, which is why the chain reads as one.
extensible struct Contact {
    contact_id: uint32;
}

struct Radar extends Contact {
    scan_period_ms: uint32;
}

struct Sonar extends Contact {
    depth_m: uint32;
}

const from_air: bit = true;

const primary: Contact =
    if from_air then Radar { contact_id = 1; scan_period_ms = 2000; }
    else Sonar { contact_id = 2; depth_m = 40; };

const range_m: int32 = 8000;

const band: int32 = if range_m > 10000 then 3 else if range_m > 5000 then 2 else 1;

Spellings​

then is optional, parentheses around the condition are just a parenthesized expression, and a braced branch is just a block. None of them is a separate form, so they mix freely and all of these are the same if.

const armed: bit = true;

if armed then "on" else "off"          // => "on"
if armed "on" else "off"               // => "on"
if (armed) then "on" else "off"        // => "on"
if armed { "on"; } else { "off"; }     // => "on"
if armed then { "on"; } else "off"     // => "on"

A { after the condition always opens the then-branch, so a condition that ends in a struct literal has to be parenthesized.

struct Threshold {
    limit_deg: uint16;
}

const over: bit = if (Threshold { limit_deg = 90; }).limit_deg > 0 { true; } else { false; };

Statement form​

Written with a bare then-branch, if is a statement: it produces no value and each branch is a list of statements. It belongs in a block before the tail expression, or in a contract body.

  • The then-branch decides which form this is. Bare makes it a statement; braced makes it an expression, and both branches then need a tail value.
  • Given a bare then-branch, the else may be one bare statement or a braced list of them. The else is still mandatory.
  • Each branch is its own scope. A let in a branch is invisible to the other branch and to everything after the if, where it would otherwise exist on only one execution path.
  • A contract body has no tail expression, so both branches may be braced statement lists there.
function bounded(raw_range_m: int32) -> int32 = {
    if raw_range_m > 0 then assert raw_range_m < 100000;
    else assert raw_range_m > -100000;
    raw_range_m;
};

contract InSector(bearing_deg: int32) {
    if bearing_deg > 180 { assert bearing_deg <= 360; }
    else { assert bearing_deg >= 0; }
    assert true;
}

Errors​

A condition that is not bit​

const armed: bit = if (1: int32) then true else false;   // error: type-mismatch

Branches with no common type​

const label: string = if true then "inbound" else 42;   // error: incompatible-branch-types

Branches of two different numeric widths​

const range_m: int64 = if true then (1: int32) else (2: int64);   // error: incompatible-branch-types
  • block, let, assert — the statements a branch is made of, and the block a braced branch is.
  • match — the choice over more than two cases, which also destructures.
  • operators — the comparisons a condition is usually built from.