Built-in functions¶
These functions and methods are part of the language. They need no import, and
they are available in a .cdlb artifact running under candela-vm with no
library directory installed. With the methods the standard library adds to the
same types, they make up the prelude.
A function your program declares or imports takes precedence over a built-in
function of the same name; see
functions. The built-in
methods work the other way round and keep precedence over an impl block, as
methods describes.
Everything here raises errors the same way the language does. A raised error
carries a short code, and that code is what a catch binds and what
catch "code" filters on; the longer message is what an uncaught error prints.
The codes appear against each function below. See
error handling and
the error catalogue.
Output and input¶
print¶
Writes each argument to standard output, one per line. Takes any number of
arguments of any type and returns nothing. A list prints as [1,2,3], a map as
{a:1,b:2}, a struct as Name {field:value}, and an enum value as its variant
name with any payload in brackets. A float keeps its decimal point wherever it
appears, so print(3.0) writes 3.0 and print([3.0]) writes [3.0], the
same text string gives. null, the value of a call that returns nothing, prints as
null. An infinity prints as inf and a not-a-number as NaN.
A function prints as <fn>, wherever it is held.
A stream that refuses the write costs the line, not the run. program | head -1
leaves standard output with no reader, and the prints that follow go nowhere
while the program runs to the end as it otherwise would.
input¶
Writes prompt to standard output without a trailing newline, reads one line
from standard input, and returns it as a string with the trailing newline (and a
carriage return, if present) removed. prompt is a string; with no argument the
prompt is empty.
Conversion¶
A conversion is the name of the type it converts to, called as a function.
Text that may not hold a value of the type is read with
s.parse<T>() instead, which answers None rather than
raising.
int¶
Converts a string or a float to an int. A float truncates towards zero, so
int(3.9) is 3 and int(-3.9) is -3. A string that does not parse as an integer
raises invalid_int.
float¶
Converts a string or an int to a float. A string that does not parse as a
floating-point number raises invalid_float.
string¶
Renders any value as a string. Ints and floats use their shortest exact decimal
form, bools become true or false, a string is returned unchanged, and
collections, structs, and enum values use their literal form. A struct reads as
Name {field:value}, the spelling print writes, wherever it appears: on its
own, inside a list or a map, and as an enum payload. A function reads <fn>:
there is no signature at run time to tell one function from another.
bool¶
Converts the string "true" or "false" to a bool. Any other string raises
invalid_bool. The argument has to be a string; there is no truthiness
conversion from other types.
type¶
Returns the static type of the expression as a string, resolved at compile time.
Every answer is spelled the way the same type is written in a program: int,
float, bool, string, null, any for a dynamic value, int[] for a list
of ints, {string: int} for a map, a|b for a union, the declared name for a
struct or an enum, and fn(int) -> int for a function. A struct or an enum that
takes type arguments carries them, as in Cell<int>. A list or a map names what
it holds, so a map of your own enum reads {string: Value}.
A function reads as its declaration does. A parameter with no annotation is
any, as is a return type the declaration leaves to the body, and a function
that hands nothing back is written without the arrow.
struct Point { x: int, y: int }
fn id(x: int) -> int { return x; }
fn greet(name: string) { print(name); }
fn add(a, b) { return a + b; }
fn main() {
print(type(Point { x: 1, y: 2 }));
print(type(id), type(greet), type(add));
}
The answer is the type the compiler inferred, not the tag the value carries when
the program runs, so a value the compiler could not pin reads any whatever it
turns out to hold. value is T answers at run time; see
dynamic values.
Sequences¶
range¶
Returns a list of ints from start (0 when omitted) up to but not including
end. Both arguments are ints. The result is empty when start is not less
than end.
Dynamic values¶
A value typed any (a json::parse result, an Option payload, a host
function return) carries its type at run time. value is T tests it, and
value as T hands it on typed as T or raises bad_downcast; both are
operators of the language rather than functions, described in
types.
as {any: any} gives a map with any keys and values, and as any[] a list of
any, because the entries of a dynamic collection are dynamic too. Such a
collection takes a push, an xs[i] = v or an m[k] = v of any type, in any
order, and an entry read back out is an any that needs its own downcast.
as {string: any} checks every key on the way, and gives the map a function
declaring string keys accepts.
import "std/json";
fn main() {
let m = json::parse("{\"n\": 7}") as {string: any};
print(m["n"] as int + 1);
m["name"] = "ada";
if m["name"] is string {
print("a name");
}
}
Parsing and serialising json is the json module.
Errors and process control¶
throw "kind"; raises an error. It is a statement of the language rather than
a function; see error handling.
exit¶
Stops the program. With no argument it ends the run normally; with an int code
the process exits with that status.
At the REPL a line calling exit
leaves the prompt with the status the call names, and the line is not kept in
the session: the prompt compiles the session in a process of its own, and a
kept line ending that process would stop every later run before it printed
anything. The argument is worked out by the session the line is typed into, so
exit(code) leaves with what code holds, and the status is the one the same
call leaves with in a file.
argv¶
Returns the command-line arguments that follow the script path, as a list of strings. Takes no arguments.
the_answer¶
Prints the answer to the Ultimate Question of Life, the Universe, and Everything, and returns 42.
File system¶
Files are read and written through the fs module, imported with
import "std/fs";.
String methods¶
Called on a string receiver.
| Method | Returns | Behaviour |
|---|---|---|
s.len() |
int | The number of characters |
s.uppercase() |
string | s with every character upper-cased |
s.lowercase() |
string | s with every character lower-cased |
s.starts_with(prefix) |
bool | True when s begins with the string prefix |
s.ends_with(suffix) |
bool | True when s ends with the string suffix |
s.contains(needle) |
bool | True when the string needle occurs in s |
s.index_of(needle) |
Option<int> |
Some with the position of the first occurrence of needle, or None |
s.replace(from, to) |
string | s with every occurrence of from replaced by to |
s.split(separator) |
string[] | s cut at each occurrence of the string separator; an empty separator answers the characters |
s.trim() |
string | s without leading or trailing whitespace |
s.trim_start() |
string | s without leading whitespace |
s.trim_end() |
string | s without trailing whitespace |
s.trim_chars(chars) |
string | s with any of the characters in chars stripped from both ends |
s.trim_start_chars(chars) |
string | The same, from the start only |
s.trim_end_chars(chars) |
string | The same, from the end only |
s.parse<T>() |
Option<T> |
Some with the int, float or bool the text holds, or None; "4" reads as a float as well as an int |
s.repeat(n) |
string | s joined to itself n times |
s.reverse() |
string | A new string with the characters in reverse order |
len, index_of, indexing, slicing, and split with an empty separator all count
characters; the empty separator answers the characters one at a time.
A character is a Unicode scalar value, so an accented letter or an emoji is one
position and arrives whole. The chars method in the string module
is that empty-separator split under a name that says so.
List methods¶
Called on an array receiver. push, remove, reverse, and sort change the
list in place and return nothing; the rest return a new value.
| Method | Returns | Behaviour |
|---|---|---|
arr.len() |
int | The number of elements |
arr.push(x) |
nothing | Appends x |
arr.remove(i) |
nothing | Removes the element at index i; raises index_out_of_bounds when i is outside the list |
arr.contains(x) |
bool | True when some element equals x |
arr.index_of(x) |
Option<int> |
Some with the index of the first element equal to x, or None |
arr.repeat(n) |
list | The elements of arr repeated n times |
arr.reverse() |
nothing | Reverses arr in place |
arr.sort() |
nothing | Sorts arr in place, ascending |
arr.join() |
string | The elements concatenated; the receiver has to be a list of strings |
arr.join(separator) |
string | The same, with separator between elements |
arr.split(x) |
list[] | arr cut into sub-lists at each element equal to x |
sort picks its ordering from the first element: ints, floats, and strings sort
ascending, and a list of any other element type is left unchanged. Strings sort
by their bytes, the order < gives them; see
operators.
The higher-order methods (map, filter, reduce, each, find, any,
all, sort_by), the reductions (sum, product) and the lookups (first,
last, min, max, which answer an Option) come from the
list module, which is part of the prelude. find takes a predicate;
the position of a value is index_of.
Map methods¶
Brackets read and write an entry. m[k] is the value stored under k, and
raises unknown_map_key when the key is absent. m[k] = v stores v under
k, adding the entry or replacing its value, and m[k] += 1 and the other
compound assignments read and write the entry in one statement.
The methods below are called on a map receiver. remove changes the map in
place and returns nothing. m.get(k), the lookup that answers an Option, is
in the map module.
| Method | Returns | Behaviour |
|---|---|---|
m.len() |
int | The number of entries |
m.remove(k) |
nothing | Drops the entry under k; a key the map does not hold changes nothing |
m.contains(k) |
bool | True when k is a key in m |
m.keys() |
list | The keys |
m.values() |
list | The values |
A map keeps its entries in the order they went in, so keys, values,
for k in m, printing, and json all walk it first-inserted first. Three rules
settle every case:
- Writing a key the map does not hold puts it at the end.
- Writing a key the map already holds replaces the value and leaves the entry where it is.
- Removing a key and writing it again puts it at the end; the entries after the removed one keep their order.
Two maps holding the same entries are equal whatever order they were built in.
keys and values walk the map the same way, so their results line up entry
for entry.
An empty map literal {} takes its key and value types from the first
m[k] = v, the way an empty list takes its element type from the first push. Only a
literal written empty works that way. A map whose entries are typed any, which
is what as {any: any} hands back, keeps taking entries of any type.
Number methods¶
| Method | Receiver | Returns | Behaviour |
|---|---|---|---|
x.abs() |
int or float | same as receiver | The magnitude of x |
x.sqrt() |
float | float | The square root of x |
x.round() |
float | float | x rounded to the nearest whole number, halves away from zero |
x.floor() |
float | float | The largest whole number not greater than x |
The math module covers the rest of the numeric surface, including
ceil, trunc, the trigonometric functions, and the logarithms.