Lazy Assertions

Lazy assertions rely on the let expression in order to constrain the value of future variable bindings.

They provide 2 kinds of constraints:

Syntax

let_expression
let ( let_type_bind , , ) let_guard { expression ; }
Show source
let_expression
  = "let" "(" let_type_bind ("," let_type_bind)* ","? ")"
  let_guard? "{" (expression ";")+ "}"
let_type_bind
identifier : type_expression
Show source
let_type_bind
  = identifier ":" type_expression
let_guard
with expression
Show source
let_guard
  = "with" expression

Example

let (x: int, y: int) with x > y {
  x := 1;
  y := 2;  # throws an exception because 1 > 2 is false
};

Semantics

let expression MUST create a new scope.

When destructuring an expression using the pattern matching operator :=, the runtime MUST verify that all let constraints that are in scope are satisfied.

If a let constraint is not satisfied, pattern matching MUST fail.

When nesting let expressions with a constraint on the same name, all constraints MUST be satisfied:

let (x: int) {
  let (x: string) {
    # ...
  };
};

# is equivalent to:

let (x: int & string) {
  # ...
};

The guard expression of a let expression MUST return a boolean (true or false).

If an exception is thrown in the guard expression, it SHOULD be ignored and be equivalent to returning false.