Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Prelude

The prelude is crates/slc-driver/src/prelude.sl. It is ordinary SLC, appended to every program. A program's own declaration of a prelude name shadows it. Names from the standard library are not in the prelude; they are cited.

Bool, printing, and IO

enum Bool { False, True }

hook IO {
    func write(text: String) -> (,);
    func write_line(text: String) -> (,);
}

func println<+T: Display>(x: T) -> (,) / {IO}
func print<+T: Display>(x: T) -> (,) / {IO}
func to_string<+T: Display>(x: T) -> String
func not(b: Bool) -> Bool

println and print render through Display and then perform write_line or write. not exchanges True and False.

Display

spec Display {
    func fmt(self: Self) -> String;
}

Impls cover i8, i32, i64, u8, u32, u64, f32, f64, String, Bool, char, File, the unit, and tuples and sums of up to eight components. A string's fmt is the string. Bool renders true and false. Sums render as ::0(…) through ::7(…).

Arithmetic and order

spec Add { func add(self: Self, other: Self) -> Self; }
spec Sub { func sub(self: Self, other: Self) -> Self; }
spec Mul { func mul(self: Self, other: Self) -> Self; }
spec Div { func div(self: Self, other: Self) -> Self; }
spec Rem { func rem(self: Self, other: Self) -> Self; }
spec Neg { func neg(self: Self) -> Self; }
spec Eq {
    func eq(self: Self, other: Self) -> Bool;
    func ne(self: Self, other: Self) -> Bool { <(<(self, other) | eq) | not }
}
spec Ord {
    func lt(self: Self, other: Self) -> Bool;
    func gt(self: Self, other: Self) -> Bool;
    func le(self: Self, other: Self) -> Bool;
    func ge(self: Self, other: Self) -> Bool;
}

Add, Sub, Mul, Div, Rem, Eq, and Ord are implemented for the six integer widths and for f32 and f64. Add is also implemented for String. Neg is implemented for the signed widths and the floats. Eq and Ord are also implemented for char, String, and Bool. Ord and Eq are separate specs.

Div and Rem by zero are runtime errors, as is an overflowing Add, Sub, Mul, or Neg. The call is <(a, b) | add.

func index(s: String, i: i64) -> char
func wrapping_mul(a: i64, b: i64) -> i64
func xor(a: i64, b: i64) -> i64
func shl(a: i64, b: i64) -> i64
func shr(a: i64, b: i64) -> i64
func not(b: Bool) -> Bool
func and(a: Bool, b: Bool) -> Bool
func or(a: Bool, b: Bool) -> Bool

index is the character at a zero-based position. An index outside the string is a runtime error. wrapping_mul is the product on the 64-bit pattern, and xor is exclusive or on that word. shl and shr move that pattern. The distance is the count's low six bits, so both are total, and shr fills from the left with zeros. not, and, and or are ordinary functions. and and or are strict: both arguments are values before the call. There is no !, &&, or ||.

Into

spec Into<+U> {
    func into(self: Self) -> U;
}

There is an impl for every pair among i8, i32, i64, u8, u32, u64, f32, and f64, and none from a type to itself. The expected type chooses the destination.

The number is kept when it is exact for the destination. Otherwise the conversion is an arithmetic overflow. An integer fits in a float only when the float is exactly that integer. A float fits in an integer width only when it is finite, integral, and inside that width. A float fits in f32 only when it is an exact f32. Every f32 fits in f64. A negative value does not fit an unsigned width. A u64 reaches as far as i64, because every integer is one signed word.

Float operations

spec Sqrt  { func sqrt(self: Self) -> Self; }
spec Abs   { func abs(self: Self) -> Self; }
spec Floor { func floor(self: Self) -> Self; }
spec Ceil  { func ceil(self: Self) -> Self; }
spec Sin   { func sin(self: Self) -> Self; }
spec Ln    { func ln(self: Self) -> Self; }
spec Pow   { func pow(self: Self, exp: Self) -> Self; }

Each is implemented for f32 and f64. sqrt of a negative number is an arithmetic overflow. -0.0 is in the domain. abs, floor, ceil, and sin are the IEEE operations. ln overflows on zero, a negative, and -∞. pow overflows when the base is negative and the exponent is finite but not an integer, and when the base is zero and the exponent is negative. A NaN argument of pow stays NaN. num::abs is the i64 function in the library, and it is a different operation.

Hash

spec Hash {
    func hash(self: Self) -> u64;
}

Impls cover the integer widths, char, Bool, and String. The result is non-negative. Equal values hash equal. The mix multiplies by the bit pattern of 0x9E3779B97F4A7C15 and clears the high bit, so a remainder of a hash is a slot. A String is FNV-1a over char_to_code.