Types
Types are inferred, so a script rarely writes them. They show up where they help: a function’s signature, a refined type, a secret.
Enums and exhaustive match
Section titled “Enums and exhaustive match”enum Shape { Circle(Int), Rect(Int, Int), Empty }
fn area(s: Shape) -> Int { match (s) { Circle(r) => 3 * r * r, Rect(w, h) => w * h, Empty => 0 }}print(area(Rect(3, 4)))match must cover every case. Leave one out and sub check says which:
error[E0642]: this `match` does not cover: Empty --> fail_enum.sub:4:3 |4 | match (s) { | ^^^^^^^^^^^ not every case is covered = help: add those cases, or end with `_ => ...` to cover the restExhaustiveness covers enums, Option, Result and Bool.
Refined types
Section titled “Refined types”A refined type is a base type with a condition. A literal that breaks the condition is a compile error. A value that arrives at run time is checked where it is converted.
type Port = Int where 1..=65535
error ConfigErrorerror NotNumber <: ConfigErrorerror BadPort <: ConfigError
fn parse_port(text) -> Result<Port, ConfigError> { let n = int(text) or { return NotNumber("not a number") } Port(n) or { return BadPort("out of range") }}
match (parse_port("8080")) { Ok(p) => print("port", p), Err(BadPort) => print("too big or too small"), Err(ConfigError) => print("some other config problem")}match (parse_port("0")) { Ok(p) => print("port", p), Err(BadPort) => print("too big or too small"), Err(ConfigError) => print("some other config problem")}Failure families
Section titled “Failure families”error Name <: Family makes Name a subtype of Family. A handler can name the family or the exact case. OutOfRange and a few others are built in.
Secrets
Section titled “Secrets”Secret<T> is not a subtype of T. A secret cannot reach print, log, a string or a record’s printout:
error[E0302]: a secret cannot be passed to `print` --> fail_leak.sub:3:7 |3 | print(token) | ^^^^^ this is a secret = help: a secret can only be compared with secret_eq(a, b) or passed to your own function that takes a `Secret`Records, lambdas, options and defer
Section titled “Records, lambdas, options and defer”let record = { name: "sub", port: 8080, ratio: 7 / 2.0 }let moved = { ..record, port: 9090 } // a copy with one field changed
let total = fold([1, 2, 3], 0, fn(sum, n) { sum + n })let found = find([1, 5, 8], fn(n) { n > 4 }) // an Optionprint(unwrap_or(found, 0))
defer print("done") // runs when the enclosing function or script endsList, Option and Result are generic. Your own functions are generalised by inference but cannot be annotated with type parameters yet.