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.
DisplayDebugErrorHashSharemapfoldfilteranyallcountfindfirstlastreversedconcattakedropabsabsfminmaxminfmaxfclampclampfapprox_eqdividessumsumfcontainsstarts_withends_withsplitjoinreplacewordsor_elseis_someparse_intparse_floatlowerupperequal_ignoring_casepad_startsortedmin_bymax_byzipenumerateflattenpositionincludesuniquechunkedhash_seedhash_combinehash_inthash_boolhash_floathash_strdebug_str
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.
fn show(self) -> str
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.
fn debug(self) -> str
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)
}
fn message(self) -> str
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.
fn hash(self) -> int
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.