Standard library overview¶
The candela standard library is a set of modules. The ones that give the
built-in types their methods, and the Option and Result enums, are the
prelude: every program has them with no import. The rest you import by path.
How std ships¶
The library ships as candela source. The toolchain installs a libs directory
beside the candela executable:
libs/std/holds one.cdlfile per module:assert.cdl,event.cdl,fs.cdl,hash.cdl,json.cdl,list.cdl,map.cdl,math.cdl,option.cdl,random.cdl,result.cdl,set.cdl,string.cdl,time.cdl.libs/std_src/holds the C sources and the compiled dynamic libraries for the four modules that call into native code:hash,math,random, andtime.
Set CANDELA_LIB_PATH to point at a different libs directory. The variable
names the directory that contains std/ and std_src/, and it overrides the
default location for library imports. A Rust host that embeds candela names it
with Engine::with_lib_dir instead; see
embedding. The prelude does not depend on it:
the compiler carries its own copy, so a program has the prelude wherever it
compiles.
Because the modules are ordinary source files, the compiler links the ones you
import into your program. A .cdlb artifact built from a program that imports
only pure-candela modules runs under candela-vm with no library directory
present. The hash, math, random, and time modules bind a dynamic
library, so a .cdlb that uses them records the binding recipe and needs the
libs directory when it runs; candela-vm finds it the way the compiler does,
through CANDELA_LIB_PATH or beside its own binary. See
artifacts.
In a browser¶
The WebAssembly build has no file system and loads no dynamic library. It
carries hash, math, random and time inside itself and runs their native
half on its own functions, so import "std/math"; works there as it
does on a desktop, with the same functions. A .cdlb built on a desktop that
imports them runs in the browser as well. The prelude is there too. The other
modules do not import in a browser.
Importing a module¶
A library import is a quoted path with no file extension. The resolver appends
.cdl and looks the file up in the shipped library directory, never next to
the importing file, so it works from any working directory:
An import binds the module under its name (import "std/json"; then
json::parse(text)), and a list of items brings those into the file's own
scope (import "std/json" { parse }; then parse(text)). The import form is
covered in full in modules.
A call to a name a module you have not imported declares, such as sqrt(x) or
fs::read(path), is a compile error whose help names the import it needs.
A library import's first segment is looked up among the packages the project
depends on before the library directory, so a dependency named std would
shadow the standard library. Nothing else about std is special: it is the
directory a library path falls back to.
The prelude¶
What a program reaches for on every other line needs no import:
- Every method on a string, a list, a map, an int, a float and a bool, whether
the language builds it in (
s.uppercase(),xs.push(x)) or a module defines it (s.capitalize(),xs.map(f),m.get(k),"42".parse<int>()). OptionandResult, withSome,None,Ok,Errand their methods.- The free built-ins:
print,input,exit,argv,typeand the conversions. They are listed in built-in functions.
fn main() {
let xs = [1, 2, 3, 4];
print(xs.map(fn(x) => x * 2));
print(xs.max().unwrap_or(0));
print({"a": 1}.get("b"));
}
The modules behind it are list, string, map, option and result.
Importing one of them still works, reaches the same module, and binds its name
like any import; its names stay reachable with no prefix. A method nothing calls is never compiled, so the prelude adds nothing to
a program that does not use it. A program that declares its own enum with a
Some variant, or its own Option, gets its own when it names it.
Everything else is imported: assert, fs, hash, json, math,
random, set and time.
The modules¶
| Module | What it gives you |
|---|---|
| assert | Assertions that raise an error when a check fails |
| builtins | The always-available functions and methods (no import) |
| event | Results from work running elsewhere, delivered to a function |
| fs | Read, write, copy and delete files, digest them, and list and make directories |
| hash | md5, sha1 and sha256 digests of a string, as hex |
| json | Parse a json string into candela values, and serialise back |
| list | Reductions, slicing, and higher-order methods on arrays |
| map | Map methods: get, which answers an Option, and is_empty |
| math | Trigonometry, logarithms, roots, rounding, and the constants |
| option | The Option enum: Some(x) or None, with methods |
| random | A seedable pseudo-random generator for ints and floats |
| result | The Result enum: Ok(v) or Err(e), with methods |
| set | Set<T>, a set of unique values, with union, intersection, difference, and symmetric difference |
| string | Substrings, characters, padding, capitalisation, line splitting, counting |
| time | The current unix time, and formatting a timestamp |
Errors¶
Standard library functions report failure the same way the rest of the language does: they raise an error that stops the run and prints a message naming the cause. Each page below states what its functions raise.
A raised error carries a short code as well as a message. The code is what a
catch binds and what catch "code" filters on; the message is what an uncaught
error prints. A throw is its own code, so throw "no such user"; is caught as
no such user. A try catches what a library function raises just as it
catches a throw written in the block. See
error handling for the mechanism and
the error catalogue for the codes.