&&& buffers: shared memory with Rust
& use types that are all either empty or any
box console = []
box input = nil
box fetch_outbox = ""
box fetch_inbox = ()
box keys_down = []

& the very base: know something's type
fn type {
	"Returns a keyword representing the type of the value passed in."
	(x) -> base :type (x)
}

& & some helper type functions
fn coll? {
	"Returns true if a value is a collection: dict, list, tuple, or set."
	(coll as :dict) -> true
	(coll as :list) -> true
	(coll as :tuple) -> true
	& (coll as :set) -> true
	(_) -> false
}

fn indexed? {
	"Returns true if a value is indexed (can use `at`): list, tuple, or string."
	(coll as :list) -> true
	(coll as :tuple) -> true
	(coll as :string) -> true
	(_) -> false
}

& for now we don't need this: we don't have pkgs
& fn assoc? {
& 	"Returns true if a value is an associative collection: a dict or a pkg."
& 	(d as :dict) -> true
& 	(_) -> false
& }

& &&& nil: working with nothing

fn nil? {
	"Returns true if a value is nil."
	(nil) -> true
	(_) -> false
}

fn some? {
	"Returns true if a value is not nil."
	(nil) -> false
	(_) -> true
}

fn some {
	"Takes a possibly nil value and a default value. Returns the value if it's not nil, returns the default if it's nil."
	(nil, default) -> default
	(value, _) -> value 
}

& ...and if two things are the same
fn eq? {
	"Returns true if all arguments have the same value."
	(x) -> true
	(x, y) -> base :eq? (x, y)
	(x, y, ...zs) -> if eq? (x, y)
		then loop (y, zs) with {
			(a, [b]) -> and (eq? (a, x), eq? (b, x))
			(a, [b, ...cs]) -> if eq? (a, x)
				then recur (b, cs)
				else false
			}
		else false
}

& &&& true & false: boolean logic (part the first)
fn bool? {
	"Returns true if a value is of type :boolean."
	(false) -> true
	(true) -> true
	(_) -> false
}

fn true? {
	"Returns true if a value is boolean `true`. Useful to distinguish between `true` and anything else."
	(true) -> true
	(_) -> false
}

fn false? {
	"Returns `true` if a value is `false`, otherwise returns `false`. Useful to distinguish between `false` and `nil`."
	(false) -> true
	(_) -> false
}

fn bool {
	"Returns false if a value is nil or false, otherwise returns true."
	(nil) -> false
	(false) -> false
	(_) -> true
}

fn not {
	"Returns false if a value is truthy, true if a value is falsy."
	(nil) -> true
	(false) -> true
	(_) -> false
}

& & tuples: not a lot you can do with them functionally
fn tuple? {
	"Returns true if a value is a tuple."
	(tuple as :tuple) -> true
	(_) -> false
}

& &&& functions: getting things done
fn fn? {
	"Returns true if an argument is a function."
	(f as :fn) -> true
	(_) -> false
}

& what we need for some very basic list manipulation 
fn first {
  "Retrieves the first element of an ordered collection: tuple, list, or string. If the collection is empty, returns nil."
  ([]) -> nil
  (()) -> nil
  ("") -> nil
  (xs as :list) -> base :first (xs)
  (xs as :tuple) -> base :first (xs)
  (str as :string) -> base :first (str)
}

fn rest {
	"Returns all but the first element of a list or tuple, as a list."
	([]) -> []
	(()) -> ()
	(xs as :list) -> base :rest (xs)
	(xs as :tuple) -> base :rest (xs)
	(str as :string) -> base :rest (str)
}

fn last {
	"Returns the last element of an indexed value: list, tuple, or string."
	(xs as :list) -> base :last (xs)
	(xs as :tuple) -> base :last (xs)
	(str as :string) -> base :last (str)
}

fn inc {
	"Increments a number."
	(x as :number) -> base :inc (x)
}

fn dec {
	"Decrements a number."
	(x as :number) -> base :dec (x)
}

fn count {
	"Returns the number of elements in a collection (including string)."
	(xs as :list) -> base :count (xs)
	(xs as :tuple) -> base :count (xs)
	(xs as :dict) -> base :count (xs)
	(xs as :string) -> base :count (xs)
	& (xs as :set) -> base :count (xs)
}

fn empty? {
	"Returns true if something is empty. Otherwise returns false (including for things that can't logically be empty, like numbers)."
	([]) -> true
	(#{}) -> true
	& (s as :set) -> eq? (s, ${})
	(()) -> true
	("") -> true
	(_) -> false
}

fn any? {
	"Returns true if something is not empty, otherwise returns false (including for things that can't be logically full, like numbers)."
	([...]) -> true
	(#{...}) -> true
	& (s as :set) -> not (empty? (s))
	((...)) -> true
	(s as :string) -> not (empty? (s))
	(_) -> false
}

fn at {
	"Returns the element at index n of a list or tuple, or the byte at index n of a string. Zero-indexed: the first element is at index 0. Returns nil if nothing is found in a list or tuple; returns an empty string if nothing is found in a string."
	(i as :number) -> at (_, i)
	(xs as :list, i as :number) -> base :at (xs, i)
	(xs as :tuple, i as :number) -> base :at (xs, i)
	(str as :string, i as :number) -> base :at (str, i)
	(_) -> nil
}

fn second {
	"Returns the second element of a list or tuple."
	(xs) if indexed? (xs) -> at (xs, 1)
	(_) -> nil
}

fn list? {
	"Returns true if the value is a list."
	(l as :list) -> true
	(_) -> false
}

fn list {
	"Takes a value and returns it as a list. For atomic values, it simply wraps them in a list. For collections, conversions are as follows. A tuple->list conversion preservers order and length. Dicts return lists of `(key, value)`` tuples, but watch out: dicts are not ordered and may spit out their pairs in any order. If you wish to get a list of chars in a string, use `chars`."
	(x) -> base :list (x)
}

fn append {
	"Adds an element to a list."
	() -> []
	(xs as :list) -> xs
	(xs as :list, x) -> base :append (xs, x)
}

fn fold {
	"Folds a list."
	(f as :fn, []) -> []
	(f as :fn, xs as :list) -> fold (f, xs, f ())
	(f as :fn, [], root) -> []
	(f as :fn, xs as :list, root) -> loop (root, first (xs), rest (xs)) with {
		(prev, curr, []) -> f (prev, curr)
		(prev, curr, remaining) -> recur (
			f (prev, curr)
			first (remaining)
			rest (remaining)
		)
	}
}

fn foldr {
	"Folds a list, right-associatively."
	(f as :fn, []) -> []
	(f as :fn, xs as :list) -> foldr(f, xs, f ())
	(f as :fn, [], root) -> []
	(f as :fn, xs as :list, root) -> loop (root, first (xs), rest (xs)) with {
		(prev, curr, []) -> f (curr, prev)
		(prev, curr, remaining) -> recur (
			f (curr, prev)
			first (remaining)
			rest (remaining)
		)
	}
}

fn map {
	"Maps a function over a list: returns a new list with elements that are the result of applying the function to each element in the original list. E.g., `map ([1, 2, 3], inc) &=> [2, 3, 4]`. With one argument, returns a function that is a mapper over lists; with two, it executes the mapping function right away."
	(f as :fn) -> map (f, _)
	(kw as :keyword) -> map (kw, _)
	(f as :fn, xs) -> {
		fn mapper (prev, curr) -> append (prev, f (curr))
		fold (mapper, xs, [])
	}
	(kw as :keyword, xs) -> {
		fn mapper (prev, curr) -> append (prev, kw (curr))
		fold (mapper, xs, [])
	}
}

fn filter {
	"Takes a list and a predicate function, and returns a new list with only the items that produce truthy values when the function is called on them. E.g., `filter ([1, 2, 3, 4], odd?) &=> [1, 3]`."
	(p? as :fn) -> filter (p?, _)
	(p? as :fn, xs) -> {
		fn filterer (filtered, x) -> if p? (x)
			then append (filtered, x)
			else filtered
		fold (filterer, xs, [])
	}
}

fn keep {
	"Takes a list and returns a new list with any `nil` values omitted."
	(xs) -> filter (some?, xs)
}

fn concat {
	"Combines lists, strings, or sets."
	(x as :string) -> x
	(xs as :list) -> xs
	(x as :string, y as :string) -> "{x}{y}"
	(xs as :list, ys as :list) -> base :concat (xs, ys)
	& (xs as :set, ys as :set) -> base :concat (xs, ys)
	(xs, ys, ...zs) -> fold (concat, zs, concat (xs, ys))
}

fn contains? {
	"Returns true if a list contains a value."
	& (value, s as :set) -> bool (base :get	(s, value))
	(value, l as :list) -> loop (l) with {
		([]) -> false
		([...xs]) -> if eq? (first(xs), value)
			then true
			else recur (rest (xs))
	}
}

&&& boxes: mutable state and state changes
fn box? {
	"Returns true if a value is a box."
	(b as :box) -> true
	(_) -> false
}

fn unbox {
	"Returns the value that is stored in a box."
	(b as :box) -> base :unbox (b)
}

fn store! {
	"Stores a value in a box, replacing the value that was previously there. Returns the value."
	(b as :box, value) -> {
		base :store! (b, value)
		value
	}
}

fn update! {
	"Updates a box by applying a function to its value. Returns the new value."
	(b as :box, f as :fn) -> {
		let current = unbox (b)
		let new = f (current)
		store! (b, new)
	}
}

&&& strings: harder than they look!
fn string? {
	"Returns true if a value is a string."
	(x as :string) -> true
	(_) -> false
}

fn show {
	"Returns a text representation of a Ludus value as a string."
	(x) -> base :show (x)
}

fn string {
	"Converts a value to a string by using `show`. If it is a string, returns it unharmed. Use this to build up strings of different kinds of values."
	(x as :string) -> x
	(x) -> show (x)
	(x, ...xs) -> loop (string (x), xs) with {
		(out, [y]) -> concat (out, show (y))
		(out, [y, ...ys]) -> recur (concat (out, show (y)), ys)
	}
}

fn join {
	"Takes a list of strings, and joins them into a single string, interposing an optional separator."
	([]) -> ""
	([str as :string]) -> str
	(strs as :list) -> join (strs, "")
	([], separator as :string) -> ""
	([str as :string], separator as :string) -> str
	([str, ...strs], separator as :string) -> fold (
		fn (joined, to_join) -> concat (joined, separator, to_join)
		strs
		str
	)
}

fn split {
	"Takes a string, and turns it into a list of strings, breaking on the separator."
	(str as :string, splitter as :string) -> base :split (str, splitter)
}

fn trim {
	"Trims whitespace from a string. Takes an optional argument, `:left` or `:right`, to trim only on the left or right."
	(str as :string) -> base :trim (str)
	(str as :string, :left) -> base :triml (str)
	(str as :string, :right) -> base :trimr (str)
}

fn upcase {
	"Takes a string and returns it in all uppercase. Works only for ascii characters."
	(str as :string) -> base :upcase (str)
}

fn downcase {
	"Takes a string and returns it in all lowercase. Works only for ascii characters."
	(str as :string) -> base :downcase (str)
}

fn chars {
	"Takes a string and returns its characters as a list. Each member of the list corresponds to a utf-8 character."
	(str as :string) -> match base :chars (str) with {
		(:ok, chrs) -> chrs
		(:err, msg) -> panic! msg
	}
}

fn ws? {
	"Tells if a string is a whitespace character."
	(" ") -> true
	("\t") -> true
	("\n") -> true
	("\r") -> true
	(_) -> false
}

fn strip {
	"Removes punctuation from a string, removing all instances of ,.;:?!"
	("{x},{y}") -> strip ("{x}{y}")
	("{x}.{y}") -> strip ("{x}{y}")
	("{x};{y}") -> strip ("{x}{y}")
	("{x}:{y}") -> strip ("{x}{y}")
	("{x}?{y}") -> strip ("{x}{y}")
	("{x}!{y}") -> strip ("{x}{y}")
	(x) -> x
}

fn condenser (charlist, curr_char) -> if and (
		ws? (curr_char)
		do charlist > last > ws?)
	then charlist
	else append (charlist, curr_char)

fn condense {
	"Condenses the whitespace in a string. All whitespace will be replaced by spaces, and any repeated whitespace will be reduced to a single space."
	(str as :string) -> {
		let chrs = chars (str)
		let condensed = fold (condenser, chrs, [])
		join (condensed)
	}
}

fn words {
	"Takes a string and returns a list of the words in the string. Strips all whitespace."
	(str as :string) -> base :words (str)
}

fn sentence {
	"Takes a list of words and turns it into a sentence."
	(strs as :list) -> join (strs, " ")
}

fn to_number {
	"Takes a string that presumably contains a representation of a number, and tries to give you back the number represented. Returns a result tuple."
	(num as :string) -> base :number (num)
}

fn print! {
	"Sends a text representation of Ludus values to the console."
	(...args) -> {
		base :print! (args)
		let line = do args > map (string, _) > join (_, " ")
		update! (console, append (_, line))
		:ok
	}
}

fn report! {
	"Prints a value, then returns it."
	(x) -> {
		print! (x)
		x
	}
	(msg as :string, x) -> {
		print! (concat ("{msg} ", show (x)))
		x
	}
}

fn doc! {
	"Prints the documentation of a function to the console."
	(f as :fn) -> do f > base :doc > print!
	(_) -> nil
}

&&& numbers, basically: arithmetic and not much else, yet
& TODO: add nan?, 
fn number? {
	"Returns true if a value is a number."
	(x as :number) -> true
	(_) -> false
}

fn add {
	"Adds numbers or vectors."
	() -> 0
	(x as :number) -> x
	(x as :number, y as :number) -> base :add (x, y)
	(x, y, ...zs) -> fold (add, zs, base :add (x, y))
	& add vectors
	((x1, y1), (x2, y2)) -> (add (x1, x2), add (y1, y2))
}

fn sub {
	"Subtracts numbers or vectors."
	() -> 0
	(x as :number) -> x
	(x as :number, y as :number) -> base :sub (x, y)
	(x, y, ...zs) -> fold (sub, zs, base :sub (x, y))
	((x1, y1), (x2, y2)) -> (base :sub (x1, x2), base :sub (y1, y2))
}

fn mult {
	"Multiplies numbers or vectors."
	() -> 1
	(x as :number) -> x
	(x as :number, y as :number) -> base :mult (x, y)
	(x, y, ...zs) -> fold (mult, zs, mult (x, y))
	(scalar as :number, (x, y)) -> (mult (x, scalar), mult (y, scalar))
	((x, y), scalar as :number) -> mult (scalar, (x, y))
}

fn pow {
	"Raises a number to the power of another number."
	(x as :number, y as :number) -> base :pow (x, y)
}

fn div {
	"Divides numbers. Panics on division by zero."
	(x as :number) -> x
	(_, 0) -> panic! "Division by zero."
	(x as :number, y as :number) -> base :div (x, y)
	(x, y, ...zs) -> {
		let divisor = fold (mult, zs, y)
		div (x, divisor)
	}
}

fn div/0 {
	"Divides numbers. Returns 0 on division by zero."
	(x as :number) -> x
	(_, 0) -> 0
	(x as :number, y as :number) -> base :div (x, y)
	(x, y, ...zs) -> {
		let divisor = fold (mult, zs, y)
		div/0 (x, divisor)
	}
}

fn div/safe {
	"Divides a number. Returns a result tuple."
	(x as :number) -> (:ok, x)
	(_, 0) -> (:err, "Division by zero")
	(x, y) -> (:ok, div (x, y))
	(x, y, ...zs) -> {
		let divisor = fold (mult, zs, y)
		div/safe (x, divisor)
	}
}

fn inv {
	"Returns the inverse of a number: 1/n or `div (1, n)`. Panics on division by zero."
	(x as :number) -> div (1, x)
}

fn inv/0 {
	"Returns the inverse of a number: 1/n or `div/0 (1, n)`. Returns 0 on division by zero."
	(x as :number) -> div/0 (1, x)
}

fn inv/safe {
	"Returns the inverse of a number: 1/n or `div/safe (1, n)`. Returns a result tuple."
	(x as :number) -> div/safe (1, x)
}

fn neg {
	"Multiplies a number by -1, negating it."
	(n as :number) -> mult (n, -1)
}

fn zero? {
	"Returns true if a number is 0."
	(0) -> true
	(_) -> false
}

fn gt? {
	"Returns true if numbers are in decreasing order."
	(x as :number) -> true
	(x as :number, y as :number) -> base :gt? (x, y)
	(x, y, ...zs) -> loop (y, zs) with {
		(a, [b]) -> base :gt? (a, b)
		(a, [b, ...cs]) -> if base :gt? (a, b)
			then recur (b, cs)
			else false 
	}
}

fn gte? {
	"Returns true if numbers are in decreasing or flat order."
	(x as :number) -> true
	(x as :number, y as :number) -> base :gte? (x, y)
	(x, y, ...zs) -> loop (y, zs) with {
		(a, [b]) -> base :gte? (a, b)
		(a, [b, ...cs]) -> if base :gte? (a, b)
			then recur (b, cs)
			else false
	}
}

fn lt? {
	"Returns true if numbers are in increasing order."
	(x as :number) -> true
	(x as :number, y as :number) -> base :lt? (x, y)
	(x, y, ...zs) -> loop (y, zs) with {
		(a, [b]) -> base :lt? (a, b)
		(a, [b, ...cs]) -> if base :lt? (a, b)
			then recur (b, cs)
			else false
	}
}

fn lte? {
	"Returns true if numbers are in increasing or flat order."
	(x as :number) -> true
	(x as :number, y as :number) -> base :lte? (x, y)
	(x, y, ...zs) -> loop (y, zs) with {
		(a, [b]) -> base :lte? (a, b)
		(a, [b, ...cs]) -> if base :lte? (a, b)
			then recur (b, cs)
			else false
	}
}

fn between? {
	"Returns true if a number is in the range [lower, higher): greater than or equal to the lower number, less than the higher."
	(lower as :number, higher as :number, x as :number) -> and (
		gte? (x, lower)
		lt? (x, higher)
	)
}

fn neg? {
	"Returns true if a value is a negative number, otherwise returns false."
	(x as :number) if lt? (x, 0) -> true
	(_) -> false
}

fn pos? {
	"Returns true if a value is a positive number, otherwise returns false."
	(x as :number) if gt? (x, 0) -> true
	(_) -> false
}

fn abs {
	"Returns the absolute value of a number."
	(0) -> 0
	(n as :number) -> if neg? (n) then mult (-1, n) else n
}

&&& Trigonometry functions

& Ludus uses turns as its default unit to measure angles
& However, anything that takes an angle can also take a
& units argument, that's a keyword of :turns, :degrees, or :radians

let pi = base :pi

let tau = mult (2, pi)

fn turn/deg {
	"Converts an angle in turns to an angle in degrees."
	(a as :number) -> mult (a, 360)
}

fn deg/turn {
	"Converts an angle in degrees to an angle in turns."
	(a as :number) -> div (a, 360)
}

fn turn/rad {
	"Converts an angle in turns to an angle in radians."
	(a as :number) -> mult (a, tau)
}

fn rad/turn {
	"Converts an angle in radians to an angle in turns."
	(a as :number) -> div (a, tau)
}

fn deg/rad {
	"Converts an angle in degrees to an angle in radians."
	(a as :number) -> mult (tau, div (a, 360))
}

fn rad/deg {
	"Converts an angle in radians to an angle in degrees."
	(a as :number) -> mult (360, div (a, tau))
}

fn sin {
	"Returns the sine of an angle. Default angle measure is turns. An optional keyword argument specifies the units of the angle passed in."
	(a as :number) -> do a > turn/rad > base :sin
	(a as :number, :turns) -> do a > turn/rad > base :sin
	(a as :number, :degrees) -> do a > deg/rad > base :sin
	(a as :number, :radians) -> base :sin (a)
}

fn cos {
	"Returns the cosine of an angle. Default angle measure is turns. An optional keyword argument specifies the units of the angle passed in."
	(a as :number) -> do a > turn/rad > base :cos
	(a as :number, :turns) -> do a > turn/rad > base :cos
	(a as :number, :degrees) -> do a > deg/rad > base :cos
	(a as :number, :radians) -> base :cos (a)
}

fn tan {
	"Returns the sine of an angle. Default angle measure is turns. An optional keyword argument specifies the units of the angle passed in."
	(a as :number) -> do a > turn/rad > base :tan
	(a as :number, :turns) -> do a > turn/rad > base :tan
	(a as :number, :degrees) -> do a > deg/rad > base :tan
	(a as :number, :radians) -> base :tan (a)
}

fn rotate {
	"Rotates a vector by an angle. Default angle measure is turns. An optional keyword argument specifies the units of the angle passed in."
	((x, y), a) -> rotate ((x, y), a, :turns)
	((x, y), a, units as :keyword) -> (
		sub (mult (x, cos (a, units)), mult (y, sin (a, units)))
		add (mult (x, sin (a, units)), mult (y, cos (a, units)))
	)
}


fn atan/2 {
	"Returns an angle from a slope. Takes an optional keyword argument to specify units. Takes either two numbers or a vector tuple."
	(x as :number, y as :number) -> do base :atan_2 (x, y) > rad/turn
	(x, y, :turns) -> atan/2 (x, y)
	(x, y, :radians) -> base :atan_2 (x, y)
	(x, y, :degrees) -> do base :atan_2 (x, y) > rad/deg
	((x, y)) -> atan/2 (x, y)
	((x, y), units as :keyword) -> atan/2 (x, y, units)
}

fn angle {
	"Calculates the angle between two vectors."
	(v1, v2) -> sub (atan/2 (v2), atan/2 (v1))
}

fn mod {
	"Returns the modulus of x and y. Truncates towards negative infinity. Panics if y is 0."
	(x as :number, 0) -> panic! "Division by zero."
	(x as :number, y as :number) -> base :mod (x, y)
}

fn mod/0 {
	"Returns the modulus of x and y. Truncates towards negative infinity. Returns 0 if y is 0."
	(x as :number, 0) -> 0
	(x as :number, y as :number) -> base :mod (x, y)
}

fn mod/safe {
	"Returns the modulus of x and y in a result tuple, or an error if y is 0. Truncates towards negative infinity."
	(x as :number, 0) -> (:err, "Division by zero.")
	(x as :number, y as :number) -> (:ok, base :mod (x, y))
}

fn even? {
	"Returns true if a value is an even number, otherwise returns false."
	(x as :number) if eq? (0, mod (x, 2)) -> true
	(_) -> false
}

fn odd? {
	"Returns true if a value is an odd number, otherwise returns false."
	(x as :number) if eq? (1, mod (x, 2)) -> true
	(_) -> false
}

fn square {
	"Squares a number."
	(x as :number) -> mult (x, x)
}

fn sqrt {
	"Returns the square root of a number. Panics if the number is negative."
	(x as :number) if not (neg? (x)) -> base :sqrt (x) 
}

fn sqrt/safe {
	"Returns a result containing the square root of a number, or an error if the number is negative."
	(x as :number) -> if not (neg? (x))
		then (:ok, base :sqrt (x))
		else (:err, "sqrt of negative number")
}

fn sum_of_squares {
	"Returns the sum of squares of numbers."
	() -> 0
	(x as :number) -> square (x)
	(x as :number, y as :number) -> add (square (x), square (y))
	(x, y, ...zs) -> fold (
		fn (sum, z) -> add (sum, square (z))
		zs
		sum_of_squares (x, y))
}

fn dist {
	"Returns the distance from the origin to a point described by x and y, or by the vector (x, y)."
	(x as :number, y as :number) -> sqrt (sum_of_squares (x, y))
	((x, y)) -> dist (x, y)
}

fn heading/vector {
	"Takes a turtle heading, and returns a unit vector of that heading."
	(heading) -> {
		& 0 is 90º/0.25T, 0.25 is 180º/0.5T, 0.5 is 270º/0.75T, 0.75 is 0º/0T
		let a = add (neg (heading), 0.25)
		(cos (a), sin (a))
	}
}

fn floor {
	"Truncates a number towards negative infinity. With positive numbers, it returns the integer part. With negative numbers, returns the next more-negative integer."
	(n as :number) -> base :floor (n)
}

fn ceil {
	"Truncates a number towards positive infinity. With negative numbers, it returns the integer part. With positive numbers, returns the next more-positive integer."
	(n as :number) -> base :ceil (n)
}

fn round {
	"Rounds a number to the nearest integer."
	(n as :number) -> base :round (n)
}

fn range {
	"Returns the set of integers between start (inclusive) and end (exclusive) as a list: [start, end). With one argument, starts at 0. If end is less than start, returns an empty list."
	(end as :number) -> base :range (0, end)
	(start as :number, end as :number) -> base :range (start, end)
}

& additional list operations now that we have comparitors

fn slice {
	"Returns a slice of a list or a string, representing a sub-list or sub-string."
	(xs as :list, end as :number) -> slice (xs, 0, end) 
	(xs as :list, start as :number, end as :number) -> when {
		gte? (start, end) -> []
		gt? (end, count (xs)) -> slice (xs, start, count (xs))
		neg? (start) -> slice (xs, 0, end)
		true -> base :slice (xs, start, end)
	}
	(str as :string, end as :number) -> base :slice (str, 0, end)
	(str as :string, start as :number, end as :number) -> base :slice (str, start, end)
}

fn slice_n {
	"Returns a slice of a list or a string, representing a sub-list or sub-string."
	(xs as :list, n as :number) -> slice (xs, 0, n)
	(str as :string, n as :number) -> slice (str, 0, n)
	(xs as :list, start as :number, n as :number) -> slice (xs, start, add (start, n))
	(str as :string, start as :number, n as :number) -> slice (str, start, add (start, n))
}

fn butlast {
	"Returns a list, omitting the last element."
	(xs as :list) -> slice (xs, 0, dec (count (xs)))
}

fn indices_of {
	"Takes a list or string and returns a list of all the indices where the target appears. Returns an empty list if the target does not appear in the scrutinee."
		(scrutinee as :list, target) -> base :indices_of (scrutinee, target)
}

fn index_of {
	"Takes a list or string returns the first index at which the scrutinee appears. Returns `nil` if the scrutinee does not appear in the search target."
	(scrutinee as :list, target) -> base :index_of (scrutinee, target)
}

&&& keywords: funny names
fn keyword? {
	"Returns true if a value is a keyword, otherwise returns false."
	(kw as :keyword) -> true
	(_) -> false
}

fn key? {
	"Returns true if a value can be used as a key in a dict: if it's a string or a keyword."
	(kw as :keyword) -> true
	(str as :string) -> true
	(_) -> false
}

& TODO: determine if Ludus should have a `keyword` function that takes a string and returns a keyword. Too many panics, it has weird memory consequences, etc.

fn assoc {
	"Takes a dict, key, and value, and returns a new dict with the key set to value."
	() -> #{}
	(d as :dict) -> d
	(d as :dict, k as :keyword, val) -> base :assoc (d, k, val)
	(d as :dict, k as :string, val) -> base :assoc (d, k, val)
	(d as :dict, (k as :keyword, val)) -> base :assoc (d, k, val)
	(d as :dict, (k as :string, val)) -> base :assoc (d, k, val)
}

fn dissoc {
	"Takes a dict and a key, and returns a new dict with the key and associated value omitted."
	(d as :dict) -> d
	(d as :dict, k as :keyword) -> base :dissoc (d, k)
}

fn get {
	"Takes a key, dict, and optional default value; returns the value at key. If the value is not found, returns nil or the default value."
	(k as :keyword) -> get (k, _)
	(d as :dict, k as :keyword) -> base :get (d, k)
	(d as :dict, k as :keyword, default) -> match base :get (d, k) with {
		nil -> default
		val -> val
	} 
	(k as :string) -> get (k, _)
	(d as :dict, k as :string) -> base :get (d, k)
	(d as :dict, k as :string, default) -> match base :get (d, k) with {
		nil -> default
		val -> val
	}
}

fn update {
	"Takes a dict, key, and function, and returns a new dict with the key set to the result of applying the function to original value held at the key."
	(d as :dict) -> d
	(d as :dict, k as :keyword, updater as :fn) -> assoc (d, k, updater (get (d, k)))
}

fn keys {
	"Takes a dict and returns a list of keys in that dict."
	(d as :dict) -> do d > list > map (first, _)
}

fn values {
	"Takes a dict and returns a list of values in that dict."
	(d as :dict) -> do d > list > map (second, _)
}

fn has? {
	"Takes a key and a dict, and returns true if there is a non-`nil` value stored at the key."
	(k as :keyword) -> has? (_, k)
	(d as :dict, k as :keyword) -> do d > get (k) > some?
}

fn dict {
	"Takes a list or tuple of `(key, value)` tuples and returns it as a dict. Returns dicts unharmed."
	(d as :dict) -> d
	(l as :list) -> fold (assoc, l)
	(t as :tuple) -> do t > list > dict
}

fn dict? {
	"Returns true if a value is a dict."
	(d as :dict) -> true
	(_) -> false
}

fn each! {
	"Takes a list and applies a function, presumably with side effects, to each element in the list. Returns nil."
	(f! as :fn, []) -> nil
	(f! as :fn, [x]) -> { f! (x); nil }
	(f! as :fn, [x, ...xs]) -> { f! (x); each! (f!, xs) } 
}

fn random {
	"Returns a random something. With zero arguments, returns a random number between 0 (inclusive) and 1 (exclusive). With one argument, returns a random number between 0 and n. With two arguments, returns a random number between m and n. Alternately, given a collection (tuple, list, dict, set), it returns a random member of that collection."
	() -> base :random ()
	(n as :number) -> mult (n, random ())
	(m as :number, n as :number) -> add (m, random (sub (n, m)))
	(l as :list) -> {
		let i = do l > count > random > floor
		at (l, i)
	}
	(t as :tuple) -> {
		let i = do t > count > random > floor
		at (t, i)
	}
	(d as :dict) -> {
		let key = do d > keys > random 
		get (d, key)
	}
	& (s as :set) -> do s > list > random
}

fn random_int {
	"Returns a random integer. With one argument, returns a random integer between 0 and that number. With two arguments, returns a random integer between them."
	(n as :number) -> do n > random > floor
	(m as :number, n as :number) -> floor (random (m, n))
}


&&& Results, errors and other unhappy values

fn ok {
	"Takes a value and wraps it in an :ok result tuple."
	(value) -> (:ok, value)	
}

fn ok? {
	"Takes a value and returns true if it is an :ok result tuple."
	((:ok, _)) -> true
	(_) -> false
}

fn err {
	"Takes a value and wraps it in an :err result tuple, presumably as an error message."
	(msg) -> (:err, msg)
}

fn err? {
	"Takes a value and returns true if it is an :err result tuple."
	((:err, _)) -> true
	(_) -> false
}

fn unwrap! {
	"Takes a result tuple. If it's :ok, then returns the value. If it's not :ok, then it panics. If it's not a result tuple, it also panics."
	((:ok, value)) -> value
	((:err, msg)) -> panic! "Unwrapped :err! {msg}"
}

fn unwrap_or {
	"Takes a value that is a result tuple and a default value. If it's :ok, then it returns the value. If it's :err, returns the default value."
	((:ok, value), _) -> value
	((:err, _), default) -> default
}

fn assert! {
	"Asserts a condition: returns the value if the value is truthy, panics if the value is falsy. Takes an optional message."
	(value) -> if value 
		then value 
		else panic! "Assert failed: {value}"
	(msg, value) -> if value 
		then value 
		else panic! "Assert failed: {msg} with {value}"
}


&&& processes: doing more than one thing
fn self {
	"Returns the current process's pid, as a keyword."
	() -> base :process (:self)
}

fn send! {
	"Sends a message to the specified process and returns the message."
	(pid as :keyword, msg) -> {
		base :process (:send, pid, msg)
		msg
	}
}

fn link! {
	"Links this process to another process. When either one dies--panics or returns--both are shut down."
	(pid as :keyword) -> base :process (:link, pid)
}

fn spawn {
	"Spawns a process. Takes a 0-argument (nullary) function that will be executed as the new process. Returns a keyword process ID (pid) of the newly spawned process."
	(f as :fn) -> {
		let new_pid = base :process (:spawn, f)
		link! (new_pid)
		new_pid
	}
}

fn fledge {
	"Spawns a process and then immediately unlinks from it. Takes a 0-argument (nullary) function that will be executed as the new process. Returns a keyword process ID (pid) of the newly fledged process."
	(f as :fn) -> base :process (:spawn, f)
}

fn yield! {
	"Forces a process to yield, allowing other processes to execute."
	() -> base :process (:yield)
}

fn alive? {
	"Tells if the passed keyword is the id for a live process."
	(pid as :keyword) -> base :process (:alive, pid)
}

fn unlink! {
	"Unlinks this process from the other process."
	(pid as :keyword) -> base :process (:unlink, pid)
}

fn monitor! {
	"Subscribes this process to another process's exit signals. There are two possibilities: a panic or a return. Exit signals are in the form of `(:exit, pid, (:ok, value)/(:err, msg))`."
	(pid as :keyword) -> if alive? (pid)
		then base :process (:monitor, pid)
		else nil
}

fn flush {
	"Clears the current process's mailbox and returns all the messages."
	() -> base :process (:flush)
}

fn sleep! {
	"Puts the current process to sleep for at least the specified number of milliseconds."
	(ms as :number) -> base :process (:sleep, ms)
}

fn await {
	"Parks the current process until it receives an exit signal from the passed process. Returns the result of a successful execution or panics if the awaited process panics. If the other process is not alive, returns `nil`."
	(pid as :keyword) -> if monitor! (pid)
			then receive {
				(:exit, sender, (:ok, result)) if eq? (pid, sender) -> result
				(:exit, sender, (:err, msg)) if eq? (pid, sender) -> {
					let me = self ()
					panic! "Process {me} paniced because {pid} panicked: {msg}"
				}
			}
			else nil
	& (pids as :list) -> {
	& 	each! (monitor!, pids)
	& 	fold (
	& 		fn (results, pid) -> {
	& 			let result = await! (pid)
	& 			append (results, result)
	& 		}
	& 		pids
	& 		[]
	& 	)
	& }
}

fn hibernate! {
	"Ensures the current process will never return, allowing other processes to do their thing indefinitely. Does not unlink the process, so panics in linked processes will still bubble up."
	() -> receive { _ -> hibernate! () }
}

fn heed {
	"Parks the current process until it receives a reply, and returns whatever is replied. Causes a panic if it gets anything other than a `(:reply, result)` tuple."
	() -> receive {
		(:reply, result) -> result
	}
}

fn send_sync {
	"Sends the message to the specified process, and waits for a response in the form of a `(:reply, response)` tuple."
	(pid, msg) -> {
		send (pid, msg)
		receive {
			(:reply, res) -> res
		}
	}
}

& TODO: make this more robust, to handle multiple pending requests w/o data races
fn request_fetch! {
	(pid as :keyword, url as :string) -> {
		store! (fetch_outbox, url)
		request_fetch! (pid)
	} 
	(pid as :keyword) -> {
		if empty? (unbox (fetch_inbox))
			then {
				yield! ()
				request_fetch! (pid)
			}
			else {
				send (pid, (:reply, unbox (fetch_inbox)))
				store! (fetch_inbox, ())
			}
	}
}

fn fetch {
	"Requests the contents of the URL passed in. Returns a result tuple of (:ok, <contents>) or (:err, <status code>)."
	(url) -> {
		let pid = self ()
		spawn! (fn () -> request_fetch! (pid, url))
		receive {
			(:reply, (_, response)) -> response
		}
	}
}

fn input_reader! {
	(pid as :keyword) -> {
		if do input > unbox > not
			then {
				yield! ()
				input_reader! (pid)
			}
			else {
				send (pid, (:reply, unbox (input)))
				store! (input, nil)
			}
	}
}

fn read_input {
	"Waits until there is input in the input buffer, and returns it once there is."
	() -> {
		let pid = self ()
		spawn! (fn () -> input_reader! (pid))
		receive {
			(:reply, response) -> response
		}
	}
}

&&& Turtle & other graphics

& some basic colors
& these are the "basic" css colors
& https://developer.mozilla.org/en-US/docs/Web/CSS/named-color
let colors = #{
	:black (0, 0, 0, 255)
	:silver (192, 192, 192, 255)
	:gray (128, 128, 128, 255)
	:grey (128, 128, 128, 255)
	:white (255, 255, 255, 255)
	:maroon (128, 0, 0, 255)
	:red (255, 0, 0, 255)
	:purple (128, 0, 128, 255)
	:fuchsia (255, 0, 255, 255)
	:green (0, 128, 0, 255)
	:lime (0, 255, 0, 255)
	:olive (128, 128, 0, 255)
	:yellow (255, 255, 0, 255)
	:navy (0, 0, 128, 255)
	:blue (0, 0, 255, 255)
	:teal (0, 128, 128, 255)
	:aqua (0, 255, 25, 255)
}

let colours = colors

& the initial turtle state
let turtle_init = #{
	:position (0, 0) & let's call this the origin for now
	:heading 0 & this is straight up
	:pendown? true
	:pencolor :white
	:penwidth 1
	:visible? true
}

& turtle states: refs that get modified by calls
& turtle_commands is a list of commands, expressed as tuples
box turtle_commands = []
box turtle_state = turtle_init
box turtle_states = #{:turtle_0 turtle_init}
box command_id = 0

fn apply_command

fn add_command! (turtle_id, command) -> {
	let idx = unbox (command_id)
	update! (command_id, inc)
	update! (turtle_commands, append (_, (turtle_id, idx, command)))
	let prev = do turtle_states > unbox > turtle_id
	print!("previous state: {turtle_id}", prev)
	print!("applying command", command)
	let curr = apply_command (prev, command)
	update! (turtle_states, assoc (_, turtle_id, curr))
	:ok
}

fn forward! {
	"Moves the turtle forward by a number of steps. Alias: `fd!`"
	(steps as :number) -> add_command! (:turtle_0, (:forward, steps))
}

let fd! = forward!

fn back! {
	"Moves the turtle backward by a number of steps. Alias: `bk!`"
	(steps as :number) -> add_command! (:turtle_0, (:back, steps))
}

let bk! = back!

fn left! {
	"Rotates the turtle left, measured in turns. Alias: `lt!`"
	(turns as :number) -> add_command! (:turtle_0, (:left, turns))
}

let lt! = left!

fn right! {
	"Rotates the turtle right, measured in turns. Alias: `rt!`"
	(turns as :number) -> add_command! (:turtle_0, (:right, turns))
}

let rt! = right!

fn penup! {
	"Lifts the turtle's pen, stopping it from drawing. Alias: `pu!`"
	() -> add_command! (:turtle_0, (:penup))
}

let pu! = penup!

fn pendown! {
	"Lowers the turtle's pen, causing it to draw. Alias: `pd!`"
	() -> add_command! (:turtle_0, (:pendown))
}

let pd! = pendown!

fn pencolor! {
	"Changes the turtle's pen color. Takes a single grayscale value, an rgb tuple, or an rgba tuple. Alias: `pencolour!`, `pc!`"
	(color as :keyword) -> add_command! (:turtle_0, (:pencolor, color))
	(gray as :number) -> add_command! (:turtle_0, (:pencolor, (gray, gray, gray, 255)))
	((r as :number, g as :number, b as :number)) -> add_command! (:turtle_0, (:pencolor, (r, g, b, 255)))
	((r as :number, g as :number, b as :number, a as :number)) -> add_command! (:turtle_0, (:pencolor, (r, g, b, a)))
}

let pencolour! = pencolor!

let pc! = pencolor!

fn penwidth! {
	"Sets the width of the turtle's pen, measured in pixels. Alias: `pw!`"
	(width as :number) -> add_command! (:turtle_0, (:penwidth, width))
}

let pw! = penwidth!

fn background! {
	"Sets the background color behind the turtle and path. Alias: `bg!`"
	(color as :keyword) -> add_command! (:turtle_0, (:background, color))
	(gray as :number) -> add_command! (:turtle_0, (:background, (gray, gray, gray, 255)))
	((r as :number, g as :number, b as :number)) -> add_command! (:turtle_0, (:background, (r, g, b, 255)))
	((r as :number, g as :number, b as :number, a as :number)) -> add_command! (:turtle_0, (:background, (r, g, b, a)))
}

let bg! = background!

fn home! {
	"Sends the turtle home: to the centre of the screen, pointing up. If the pen is down, the turtle will draw a path to home."
	() -> add_command! (:turtle_0, (:home))
}

fn clear! {
	"Clears the canvas and sends the turtle home."
	() -> add_command! (:turtle_0, (:clear))
}

fn goto! {
	"Sends the turtle to (x, y) coordinates. If the pen is down, the turtle will draw a path to its new location."
	(x as :number, y as :number) -> add_command! (:turtle_0, (:goto, (x, y)))
	((x, y)) -> goto! (x, y)
}

fn setheading! {
	"Sets the turtle's heading. The angle is specified in turns, with 0 pointing up. Increasing values rotate the turtle counter-clockwise."
	(heading as :number) -> add_command! (:turtle_0, (:setheading, heading))
}

fn showturtle! {
	"If the turtle is hidden, shows the turtle. If the turtle is already visible, does nothing."
	() -> add_command! (:turtle_0, (:show))
}

fn hideturtle! {
	"If the turtle is visible, hides it. If the turtle is already hidden, does nothing."
	() -> add_command! (:turtle_0, (:hide))
}

fn loadstate! {
	"Sets the turtle state to a previously saved state."
	(state) -> {
		let #{position, heading, pendown?, pencolor, penwidth, visible?} = state
		add_command! (:turtle_0, (:loadstate, position, heading, visible?, pendown?, penwidth, pencolor))
	}
}

fn turtle_listener () -> {
	receive {
		(:forward!, steps as :number) -> add_command! (self (), (:forward, steps))
		(:back!, steps as :number) -> add_command! (self (), (:back, steps))
		(:left!, turns as :number) -> add_command! (self (), (:left, turns))
		(:right!, turns as :number) -> add_command! (self (), (:right, turns))
		(:penup!) -> add_command! (self (), (:penup))
		(:pendown!) -> add_command! (self (), (:pendown))
		(:pencolor!, color as :keyword) -> add_command! (self (), (:pencolor, color))
		(:pencolor!, gray as :number) -> add_command! (self (), (:pencolor, (gray, gray, gray, 255)))
		(:pencolor!, (r as :number, g as :number, b as :number)) -> add_command! (self (), (:pencolor, (r, g, b, 255)))
		(:pencolor!, (r as :number, g as :number, b as :number, a as :number)) -> add_command! (self (), (:pencolor, (r, g, b, a)))
		(:penwidth!, width as :number) -> add_command! (self (), (:penwidth, width))
		(:home!) -> add_command! (self (), (:home))
		(:goto!, x as :number, y as :number) -> add_command! (self (), (:goto, (x, y)))
		(:goto!, (x as :number, y as :number)) -> add_command! (self (), (:goto, (x, y)))
		(:show!) -> add_command! (self (), (:show))
		(:hide!) -> add_command! (self (), (:hide))
		(:loadstate!, state) -> {
			let #{position, heading, pendown?, pencolor, penwidth, visible?} = state
			add_command! (self (), (:loadstate, position, heading, visible?, pendown?, penwidth, pencolor))

		}
		(:pencolor, pid) -> send (pid, (:reply, do turtle_states > unbox > self () > :pencolor))
		(:position, pid) -> send (pid, (:reply, do turtle_states > unbox > self () > :position))
		(:penwidth, pid) -> send (pid, (:reply, do turtle_states > unbox > self () > :penwidth))
		(:heading, pid) -> send (pid, (:reply, do turtle_states > unbox > self () > :heading))
		does_not_understand -> {
			let pid = self ()
			panic! "{pid} does not understand message: {does_not_understand}"
		}
	}
	turtle_listener ()
}

fn spawn_turtle {
	"Spawns a new turtle in a new process. Methods on the turtle process mirror those of turtle graphics functions in prelude. Returns the pid of the new turtle."
	() -> {
		let pid = spawn! (fn () -> turtle_listener ())
		update! (turtle_states, assoc (_, pid, turtle_init))
		pid
	}
}

fn apply_command {
	"Takes a turtle state and a command and calculates a new state."
	(state, command) -> {
		match command with {
			(:goto, (x, y)) -> assoc (state, :position, (x, y))
			(:home) -> do state > 
				assoc (_, :position, (0, 0)) >
				assoc (_, :heading, 0)
			& (:clear) -> do state >
			& 	assoc (state, :position, (0, 0)) >
			& 	assoc (_, :heading, 0)
			(:right, turns) -> update (state, :heading, add (_, turns))
			(:left, turns) -> update (state, :heading, sub (_, turns))
			(:forward, steps) -> {
				let #{heading, position, ...} = state
				let unit = heading/vector (heading)
				let vect = mult (steps, unit)
				update (state, :position, add (vect, _))
			}
			(:back, steps) -> {
				let #{heading, position, ...} = state
				let unit = heading/vector (heading)
				let vect = mult (steps, unit)
				update (state, :position, sub (_, vect))
			}
			(:penup) -> assoc (state, :pendown?, false)
			(:pendown) -> assoc (state, :pendown?, true)
			(:penwidth, pixels) -> assoc (state, :penwidth, pixels)
			(:pencolor, color) -> assoc (state, :pencolor, color)
			(:setheading, heading) -> assoc (state, :heading, heading)
			(:loadstate, position, heading, visible?, pendown?, penwidth, pencolor) -> #{position, heading, visible?, pendown?, penwidth, pencolor}
			(:show) -> assoc (state, :visible?, true)
			(:hide) -> assoc (state, :visible?, false)
			(:background, _) -> state
		}}
}

fn position {
	"Returns the turtle's current position as an `(x, y)` vector tuple."
	() -> do turtle_states > unbox > :turtle_0 > :position
}

fn heading {
	"Returns the turtle's current heading."
	() -> do turtle_states > unbox > :turtle_0 > :heading
}

fn pendown? {
	"Returns the turtle's pen state: true if the pen is down."
	() -> do turtle_states > unbox > :turtle_0 > :pendown?
}

fn pencolor {
	"Returns the turtle's pen color as an (r, g, b, a) tuple or keyword. Alias: `pencolour`."
	() -> do turtle_states > unbox > :turtle_0 > :pencolor
}

let pencolour = pencolor

fn penwidth {
	"Returns the turtle's pen width in pixels."
	() -> do turtle_states > unbox > :turtle_0 > :penwidth
}


&&& fake some lispisms with tuples
fn cons {
	"Old-timey lisp `cons`. `Cons`tructs a tuple out of two arguments."
	(x, y) -> (x, y)
}

fn car {
	"Old-timey lisp `car`. Stands for 'contents of the address register.' Returns the first element in a `cons`ed pair (or any two-tuple)."
	((x, _)) -> x
}

fn cdr {
	"Old-timey list `cdr`. Stands for 'contents of the decrement register.' Returns the second element in a `cons`ed pair, usually representing the rest of the list."
	((_, x)) -> x
}

fn llist {
	"Makes an old-timey linked list of its arguments, of LISt Processor fame."
	(...xs) -> foldr (cons, xs, nil)
}

&&& keyboard input
fn key_down? {
	"Returns true ie the key is currently pressed. Keys are indicated by strings. For non-alphanumeric keys, consult the documentation to get key codes."
	(key as :string) -> do keys_down > unbox > contains? (key, _)
}

#{
	abs & math
	add & math
	alive? & processes
	angle & math
	any? & types and values
	append & lists
	assert! & errors
	assoc & dicts
	at & tuples, strings, lists
	atan/2 & math
	await & processes
	back! & turtle graphics
	background! & turtle graphics
	between? & math
	bg! & turtle graphics
	bk! & turtle graphics
	bool & bools
	bool? & bools
	box? & boxes
	butlast & list, tuple, string
	car & llist
	cdr & llist
	ceil & math
	chars & strings
	clear! & turtle graphics
	coll? & dicts, tuples, lists
	colors & turtle graphics
	colours & turtle graphics
	concat & list string
	cons & llist
	console & buffers
	contains? & list, tuple, string
	cos & math
	count & list, tuple, string
	dec & math
	deg/rad &math
	deg/turn &math
	dict & dicts
	dict? & dicts
	dissoc & dicts
	dist & math
	div & math
	div/0 & math
	div/safe & math
	doc! & io
	downcase & string
	each! & list
	empty? & list string dict tuple
	eq? & values
	err & result
	err? & result
	even? & math
	false? & bool
	fd! & turtles
	fetch & 
	fetch_inbox
	fetch_outbox
	filter
	first
	floor
	flush
	fn?
	fold
	foldr
	forward!
	get
	goto!
	gt?
	gte?
	has?
	heading
	heading/vector
	heed
	hibernate!
	hideturtle!
	home!
	inc
	index_of
	indexed?
	indices_of
	input
	inv
	inv/0
	inv/safe
	join
	keep
	key_down?
	keys
	keys_down
	keyword?
	last
	left!
	link!
	list
	list?
	llist
	loadstate!
	lt!
	lt?
	lte?
	map
	mod
	mod/0
	mod/safe
	monitor!
	mult
	neg
	neg?
	nil?
	not
	odd?
	ok
	ok?
	pc!
	pd!
	pencolor
	pencolor!
	pencolour!
	pendown!
	pendown?
	penup!
	penwidth
	penwidth!
	pi
	pos?
	position
	pow
	print!
	pu!
	pw!
	rad/deg
	rad/turn
	random
	random_int
	range
	read_input
	report!
	rest
	right!
	rotate
	round
	rt!
	second
	self
	send!
	sentence
	setheading!
	show
	showturtle!
	sin
	sleep!
	slice
	slice_n
	some
	some?
	spawn 
	spawn_turtle
	split
	sqrt
	sqrt/safe
	square
	store!
	string
	string?
	strip
	sub
	tan
	tau
	to_number
	trim
	true?
	tuple?
	turn/deg
	turn/rad
	turtle_commands
	turtle_init
	turtle_state
	type
	unbox
	unlink!
	unwrap!
	unwrap_or
	upcase
	update
	update!
	values
	words
	ws?
	yield!
	zero?
}
