Floats & bytes

Partly built

The float and bytes types: literals, arithmetic, conversion and the library calls that exist today.

Floats

float is a 64-bit IEEE 754 double. A literal has a decimal point (3.14159, 1.5e3, -0.0). The arithmetic and comparison operators are shared with int: the checker resolves each use by type, and an operand that could be either defaults to int. Mixing the two is a type error; convert explicitly.

let area = (r: float): float => 3.14159 * r * r

let half = (n: int): float => Float.fromInt(n) / 2.0

let root = Float.sqrt(16.0)
Verified through UCM: half(5) > 2.4 is true.

The library is Float.fromInt, truncate, floor, ceil, round (half to even), sqrt, abs, toText, pow, min, max, exp, log, logBase, the trigonometric and hyperbolic functions, atan2, and fromText (an option<float>). Division by zero gives Infinity or NaN, never a failure.

Known limits, all recorded in docs/open-issues.md: the last digit of transcendental functions can differ between UCM’s libm, the browser and the Rust engine, and a float to int conversion that overflows follows Unison’s saturate and wrap rules rather than anything a language would pick for itself.

Nats

nat is Unison’s unsigned 64-bit natural number. The literal has an n suffix, 3n; a bare 3 stays an int, and the two never mix (3n + 4 is a type error). +, *, / and the comparisons work on nats and wrap at 2^64. There is no - on a nat (it would need a result that is sometimes negative): use Nat.sub(a, b), which gives an int, or Nat.drop(a, b), which stops at 0.

let a = 3n + 4n                  // 7, shown as 7 (UCM shows a nat without a sign)
let b = Nat.toInt(a) + 1         // nat to int; Int.toNat(x) goes the other way, negatives become 0
let c = Nat.sub(2n, 5n)          // -3, an int
let d = Nat.fromText("12") ?? 0n

Also available: Nat.mod, Nat.pow, Nat.toText, Nat.toFloat, Nat.increment, Nat.isEven, Nat.isOdd, Int.fromNat, and for ints Int.signum, Int.increment, Int.isEven, Int.isOdd, Int.pow(base, exponent_nat), Int.fromText. A nat is matched with the same suffix (| 0n => ...), and an int pattern may be negative (| -1 => ...); see Variants & switch.

Bytes

bytes holds a sequence of 8-bit values. The literal is 0xs"..." with an even number of hex digits (either case, stored lowercase). == and the order operators compare lexicographically on unsigned bytes.

let blob = 0xs"0102ff"

let n = Bytes.size(blob)

let joined = Bytes.append(blob, 0xs"00")
Verified through UCM: n is 3.

Also built: Bytes.empty(), Bytes.take(b, n), Bytes.drop(b, n), Bytes.at(b, i) (an option<int>) and Bytes.toBase16(b), plus Bytes.fromBase16(b) (a result<string, bytes>, see Results). Bytes.empty() and the literal 0xs"" are different objects with different hashes.

Bytes patterns in switch are built too (| 0xs"0102" => ...).