Kite

prelude

The Kite prelude.

Compiled into every program before the user's own file. It is written in Kite, not in the compiler: everything here is expressible in the language, which is the point — a standard library that needed compiler support would be evidence the language was missing something.

It is deliberately small. Each addition is a name every program must live with, and a name taken is hard to give back.

Display

trait Display

How a type presents itself to a human.

io.print and \(x) interpolation both look for this, so implementing it once makes a type printable everywhere. It is deliberately not derived: how something reads is a design decision, and a mechanical one would be wrong more often than it was right — a Password that printed its field is the obvious case.

Debug

trait Debug

How a type presents itself to a programmer.

The counterpart to Display, and the reason both exist: Display is a design decision and Debug is a mechanical one. @derive(Debug) writes the body from the fields, which is right here for exactly the reason it would be wrong for Display — nobody reads a debug rendering expecting it to have been designed.

Error

trait Error

What a failure says about itself.

Declared here rather than in the compiler, for the same reason Display is: the language has no opinion about how a failure reads, and a program is free to declare its own trait if this one does not suit.

A type implementing this may be returned wherever an error is expected. The conversion happens at that point and is visible in the IR — nothing is implicit about it beyond not having to write the call.

pub enum LoadError {
    Missing(path: str)
}

impl Error for LoadError {
    fn message(self) -> str {
        return match self {
            Missing(path) => "no task file at \(path)",
        }
    }
}

fn load(path: str) -> ([Task], error) {
    return _, LoadError.Missing(path: path)
}

Hash

trait Hash

One integer standing for a value, for indexing and for deduplication.

Two values that are == must hash alike; two that are not may. The derived body honours that by walking exactly the fields == compares, in the same order.

It is not a security primitive. crypto is where a hash that has to resist an adversary comes from, and this one folds bytes with FNV-1a, which does not.

Share

trait Share

A value of this type may be moved to another task or isolate.

The one trait nobody implements: the compiler decides, structurally, and a type qualifies when it is deeply immutable. Because struct fields are immutable unless marked var, most types satisfy it without their author doing anything or knowing the trait exists — it only becomes visible when it is violated, as E0520.

It is declared here rather than in the compiler so that Share is an ordinary name a bound can mention, and so the rule it stands for is readable in the language it constrains.

map

pub fn map<T, U>(items: [T], f: fn(T) -> U) -> [U]

A new slice with f applied to every item. The result need not have the same element type: map(people, |p: Person| p.name) is [str].

fold

pub fn fold<T, A>(items: [T], initial: A, f: fn(A, T) -> A) -> A

Combine every item into one value, left to right.

filter

pub fn filter<T>(items: [T], test: fn(T) -> bool) -> [T]

The items test accepts, in order.

any

pub fn any<T>(items: [T], test: fn(T) -> bool) -> bool

Whether any item satisfies test. Stops at the first that does.

all

pub fn all<T>(items: [T], test: fn(T) -> bool) -> bool

Whether every item satisfies test. Vacuously true for an empty slice, which is the convention every language settles on because the alternative breaks the identity all(a + b) == all(a) and all(b).

count

pub fn count<T>(items: [T], test: fn(T) -> bool) -> int

How many items satisfy test.

find

pub fn find<T>(items: [T], test: fn(T) -> bool) -> Option<T>

The first item satisfying test, or nil.

first

pub fn first<T>(items: [T]) -> Option<T>

The first item, or nil for an empty slice.

last

pub fn last<T>(items: [T]) -> Option<T>

The last item, or nil for an empty slice.

reversed

pub fn reversed<T>(items: [T]) -> [T]

The items in the opposite order.

concat

pub fn concat<T>(items: [T], other: [T]) -> [T]

items, then other. Neither is modified: slices are values.

take

pub fn take<T>(items: [T], n: int) -> [T]

At most n items from the front. A negative n yields nothing rather than trapping, because take describes an amount and a negative amount is none.

drop

pub fn drop<T>(items: [T], n: int) -> [T]

Everything after the first n items.

abs

pub fn abs(x: int) -> int

The distance from zero.

absf

pub fn absf(x: float) -> float

min

pub fn min(a: int, b: int) -> int

max

pub fn max(a: int, b: int) -> int

minf

pub fn minf(a: float, b: float) -> float

maxf

pub fn maxf(a: float, b: float) -> float

clamp

pub fn clamp(x: int, low: int, high: int) -> int

x held within [low, high].

clampf

pub fn clampf(x: float, low: float, high: float) -> float

approx_eq

pub fn approx_eq(a: float, b: float, tolerance: float) -> bool

Floating-point equality within a tolerance. Comparing floats with == is almost never what is meant, which is why the compiler warns and points here.

divides

pub fn divides(x: int, by: int) -> bool

Whether x divides evenly by by.

sum

pub fn sum(items: [int]) -> int

The sum of a slice of numbers.

sumf

pub fn sumf(items: [float]) -> float

contains

pub fn contains(s: str, needle: str) -> bool

Whether needle appears anywhere in s.

starts_with

pub fn starts_with(s: str, prefix: str) -> bool

ends_with

pub fn ends_with(s: str, suffix: str) -> bool

split

pub fn split(s: str, sep: str) -> [str]

s cut at every occurrence of sep.

An empty separator would have no answer — every position is an occurrence — so it yields the whole string rather than looping.

join

pub fn join(pieces: [str], sep: str) -> str

The pieces joined with sep between them.

replace

pub fn replace(s: str, from: str, to: str) -> str

s with every occurrence of from replaced by to.

words

pub fn words(s: str) -> [str]

The words of s, with runs of spaces collapsed.

or_else

pub fn or_else<T>(value: Option<T>, fallback: T) -> T

The value, or fallback when there is none.

is_some

pub fn is_some<T>(value: Option<T>) -> bool

Whether an optional holds a value.

parse_int

pub fn parse_int(text: str) -> Option<int>

A whole number, or nil. Accepts a leading - and digits, and nothing else: no spaces, no separators, no trailing units. A caller who wants those trims first, which is a decision this cannot make for them.

parse_float

pub fn parse_float(text: str) -> Option<float>

A number with an optional fractional part, or nil. No exponent: a program that needs one is reading a format that has other rules too, and std/json is where those live.

lower

pub fn lower(text: str) -> str

upper

pub fn upper(text: str) -> str

equal_ignoring_case

pub fn equal_ignoring_case(left: str, right: str) -> bool

Whether two runs of text are the same ignoring ASCII case, which is what a header name or a flag is compared with.

pad_start

pub fn pad_start(text: str, width: int, with: str) -> str

text at least width long, padded on the left with with. Numbers in a column need this often enough to belong here rather than in std/fmt, which is about laying out a whole line.

sorted

pub fn sorted<T>(items: [T], less: fn(T, T) -> bool) -> [T]

items in the order less puts them, which is stable: two items less does not separate keep the order they were given in.

Merge sort rather than quicksort, because stability is worth more than the constant factor for the sizes application code sorts, and because a quicksort's worst case is a sorted input, which application code has a lot of.

min_by

pub fn min_by<T>(items: [T], less: fn(T, T) -> bool) -> Option<T>

The item less puts first, or nil for an empty slice.

max_by

pub fn max_by<T>(items: [T], less: fn(T, T) -> bool) -> Option<T>

zip

pub fn zip<A, B>(left: [A], right: [B]) -> [(A, B)]

Pairs, up to the length of the shorter side.

enumerate

pub fn enumerate<T>(items: [T]) -> [(int, T)]

Each item with the position it was at.

let names = ["ada", "grace"]
for (i, name) in enumerate(names) {
    io.print("\(i). \(name)")
}

A function rather than a method on [T], which is what §6.2 used to write. A slice can only take methods from the compiler — §8.2 has no extension methods, so that a receiver's methods are always found where its type is declared — and the three the compiler gives it are the three nothing else can be built from. This one can, so it is here, where it can be read.

flatten

pub fn flatten<T>(groups: [[T]]) -> [T]

One slice out of many.

position

pub fn position<T>(items: [T], test: fn(T) -> bool) -> int

The first position test accepts, or -1. A position rather than an optional, because -1 is what a caller compares against and an optional would need unwrapping before it could be used as an index anyway.

includes

pub fn includes<T>(items: [T], wanted: T) -> bool

Whether items holds something equal to wanted.

unique

pub fn unique<T>(items: [T]) -> [T]

items with later duplicates removed, keeping the first of each.

chunked

pub fn chunked<T>(items: [T], size: int) -> [[T]]

items in groups of at most size. The last group is whatever is left.

hash_seed

pub fn hash_seed() -> int

The FNV-1a offset basis.

FNV is chosen because it is four lines, needs no tables, and asks the two backends to agree about nothing but integer arithmetic.

The 32-bit parameters, in a 64-bit int, and that is not an oversight: Kite traps on integer overflow, so the 64-bit prime would kill the program on the first character. Staying inside 32 bits keeps every product well under the range, which is what lets the arithmetic be ordinary Kite instead of a wrapping operation the language deliberately does not have.

hash_combine

pub fn hash_combine(accumulated: int, next: int) -> int

Fold one more value into a hash.

hash_int

pub fn hash_int(value: int) -> int

hash_bool

pub fn hash_bool(value: bool) -> int

hash_float

pub fn hash_float(value: float) -> int

A float's hash goes through its rendered text.

That is not a shortcut. Rendering is the one thing about a float both backends already agree on exactly — it is shared with io.print and with interpolation — whereas reinterpreting the bits would need an operation Kite does not have and the two runtimes would have to be made to match.

hash_str

pub fn hash_str(text: str) -> int

FNV-1a over the string's code points.

debug_str

pub fn debug_str(text: str) -> str

A string as it would be written in source: quoted, with the escapes put back. What @derive(Debug) uses for a str field, so a value containing a comma or a brace cannot be mistaken for structure.