These functions are always available, with no import. Files and processes report
failure as Result or Option values, never by crashing. A few built-ins change
the world outside the program, and those can’t be called inside a par body
(E0503).
| Function | Result | |
|---|---|---|
print(a, b, ..., sep=" ", end="\n") |
write to stdout | |
eprint(a, b, ..., sep=" ", end="\n") |
write to stderr | |
len(x) |
int |
array, string (bytes) or map |
abs(x) |
same as x |
any number |
min(a, b), max(a, b) |
same as a |
two numbers of the same type |
sqrt, sin, cos, tan, exp, log |
float | one float |
floor, ceil, round |
float | one float |
pow(x, y) |
float |
two floats |
sum, product, min, max, any, all |
over a generator: sum(x for x in xs) |
|
range(stop), range(start, stop), range(start, stop, step) |
in for and comprehensions only |
|
enumerate(xs) |
[(int, T)] |
|
int(x), float(x), str(x), i32(x), u8(x), … |
conversions | |
ord(c), chr(n) |
int, str |
code points |
clock() |
float |
seconds, for timing |
args() |
[str] |
command-line arguments |
exit(code) |
stop with an exit code | |
panic(msg) |
stop with a message | |
embed("file") |
str |
a file’s text, read at compile time |
read_file(path) |
Result[str, str] |
|
write_file(path, text) |
Result[(), str] |
|
file_exists(path) |
bool |
|
getenv(name) |
Option[str] |
|
run(cmd) |
Result[int, str] |
run a program and wait for it |
The os module has more for files, directories and processes, and world is for programs that run as many processes.
Printing
Section titled “Printing”print writes its arguments separated by sep (default " ") and followed by
end (default "\n"). eprint does the same on stderr.
fn main(): print("a", 1, 2.5, true) print([1, 2], ("x", 3), {"k": "v"}, Some("s")) print("no newline", end="") print(" | then", 1, 2, sep="-") print() eprint("this goes to stderr")a 1 2.5 true[1, 2] ("x", 3) {"k": "v"} Some("s")no newline | then-1-2
this goes to stderr- A string argument prints as its text. Strings inside arrays, tuples, maps,
structs and enums print quoted, so
["a"]is distinguishable from[a]. - Every value prints except
()on its own (Ok(())prints fine). Structs print asPoint(x=1, y=2)and enum variants asShape.Circle(radius=1.0). print()with no arguments prints an empty line.sepandendmust be strings and are the only keyword arguments.
Numbers
Section titled “Numbers”fn main(): print(abs(-3), abs(-2.5), min(3, 7), max(2.5, 1.0)) print(sqrt(2.0), pow(2.0, 10.0), exp(1.0), log(10.0)) print(sin(0.0), cos(0.0), tan(0.0)) print(floor(-1.5), ceil(1.2), round(2.5), round(-2.5), int(round(2.6))) print(sqrt(-1.0), log(0.0))3 2.5 3 2.51.4142135623730951 1024.0 2.718281828459045 2.3025850929940460.0 1.0 0.0-2.0 2.0 3.0 -3.0 3nan -infabsworks on any number and returns the same type.absof the most negative integer overflows and panics.min(a, b)andmax(a, b)need two numbers of the same type:min(1, 2.0)is error E0301. A literal adapts to the other argument (min(x, 3)with ani32xis fine). They don’t compare strings.sqrt,sin,cos,tan,exp,log(natural log),floor,ceilandroundtake afloatorf32and return the same type. An integer literal is read as a float (sqrt(4)is2.0); an integer variable needsfloat(n).pow(x, y)takes and returnsfloat(f64).floor,ceilandroundreturn floats. Convert withint(...)for an integer.roundrounds halves away from zero (round(2.5)is3.0), not to even as Python does.- Math on floats follows IEEE 754:
sqrt(-1.0)isnanand0.0 / 0.0isnan, with no panic. Integer division by zero panics.
len, abs, min and max can also appear in requires, ensures and other
contracts.
Reductions
Section titled “Reductions”sum, product, min, max, any and all with one generator argument fold a
sequence without building an array: sum(x * x for x in xs),
any(w == "" for w in words). min and max of an empty sequence panic. See
arrays.
Ranges and enumerate
Section titled “Ranges and enumerate”fn main(): print([i for i in range(5)], [i for i in range(2, 5)], [i for i in range(10, 0, -3)]) print([i for i in 0..4], [i for i in 0..=4], [i for i in 5..2]) for i, w in enumerate(["a", "b"]): print(i, w) print(enumerate(["x", "y"]))[0, 1, 2, 3, 4] [2, 3, 4] [10, 7, 4, 1][0, 1, 2, 3] [0, 1, 2, 3, 4] []0 a1 b[(0, "x"), (1, "y")]a..bcounts fromaup tob - 1,a..=bincludesb.range(...)works as in Python, including a negative step; a step of 0 panics.- Ranges exist only as the iterable of a
forloop or comprehension.let r = range(0, 3)is error E0312; write[i for i in 0..3]for an array. enumerate(xs)returns an array of(index, element)tuples.
Conversions
Section titled “Conversions”There are no implicit conversions. Call the type as a function:
fn main(): let n = 7 let x = 2.9 print(float(n) / 2.0, int(x), int(-x), int(true)) print(str(n) + "!", str([1, 2]), int("42") + 1, float("2.5")) let small: u8 = u8(200) print(i32(n) + i32(1), small)3.5 2 -2 17! [1, 2] 43 2.58 200int(x)of a float truncates toward zero. A float that doesn’t fit, ornan, panics.i32(n),u8(n)and the other sized types check the range and panic if the value doesn’t fit:integer conversion out of range for u8.int(true)is 1. There is nobool(...): writen != 0.str(x)works for any printable value.int(s)andfloat(s)parse a string and panic on bad input. See strings.ord(c)is the code point of a one-character string andchr(n)the string for a code point.
The program: args, exit, panic, clock
Section titled “The program: args, exit, panic, clock”fn main() -> int: let a = args() if len(a) == 0: eprint("usage: greet <name>...") return 2 for name in a: print(f"hello, {name}") return 0brine run greet.br -- ann "bob smith"hello, annhello, bob smithargs()is the command-line arguments, without the program’s own name. Withbrine run, they go after--.mainmay return nothing (exit code 0), anint(the exit code), or aResult[(), str](anErr(e)printserror: eand exits with 1). See functions.exit(code)stops the program at once with that exit code.panic(msg)stops the program with a message and the source location, and exit code 101. Use it for bugs, not for expected failures (return aResultfor those; see error handling).clock()is a monotonic time in seconds as afloat. Subtract two readings to time something:
fn work(n: int) -> int: var total = 0 for i in 0..n: total += i % 7 return total
fn main(): let t0 = clock() let r = work(10_000_000) let dt = clock() - t0 print(r, f"{dt:.3f} s")29999994 0.013 s(The time differs from run to run.)
fn main(): let n = 3 panic(f"bad value: {n}")panic: bad value: 3 --> main.br:3:5embed("path") is a str holding a file’s text, read when the program is
compiled, so the program doesn’t need the file at run time. Use it for help
text, templates and test data:
const HELP = embed("help.txt")
fn main(): print(HELP, end="")- The path must be a string literal, relative to the directory of the
.brfile that contains the call. - The file must exist when you compile and be UTF-8 text.
\r\nline endings become\n. - It works in
consts, as above, and anywhere an expression can go. - A missing file or a non-literal path is error E0707:
error[E0707]: cannot embed `nope.txt` --> main.br:2:17 |2 | print(embed("nope.txt")) | ^^^^^^^^^^ no file at `nope.txt` | = help: the path is relative to the directory of the file that contains the `embed` = run `brine explain E0707` for more about this errorerror: could not compile due to 1 errorFiles, environment and processes
Section titled “Files, environment and processes”fn main(): match read_file("missing.txt"): case Ok(text): print(len(text.lines()), "lines") case Err(e): print("error:", e) print(write_file("out.txt", "hi\n"), file_exists("out.txt"), read_file("out.txt")) print(getenv("NO_SUCH_VARIABLE"), getenv("PATH").is_some()) print(run(["no-such-program"]))error: cannot read missing.txt: No such file or directoryOk(()) true Ok("hi\n")None trueErr("cannot run no-such-program: program not found")read_file(path)returns the whole file asOk(text), orErr(message). The file must be UTF-8 text; otherwise it’s anErr(cannot read b.bin: the file is not valid UTF-8 text).write_file(path, text)creates or replaces the file.file_exists(path)istruefor an existing file or directory.getenv(name)isSome(value)orNone.run(cmd)startscmd[0](searched onPATH) with the rest as arguments, waits for it, and returnsOk(exit code). The program shares the terminal: its output goes straight to yours. It’sErronly if the program can’t start. To capture output, run it in the background or pass environment variables, useos.spawn.
With ? these read naturally in a function that returns Result:
fn line_count(path: str) -> Result[int, str]: let text = read_file(path)? return Ok(len(text.lines()))
fn main() -> Result[(), str]: write_file("notes.txt", "one\ntwo\nthree\n")? print(line_count("notes.txt")?) return Ok(())3Inside par
Section titled “Inside par”par bodies run in an unpredictable order, so built-ins whose effects would
depend on that order are compile errors there:
Allowed in par |
Not allowed in par (E0503) |
|---|---|
print, eprint (lines from different iterations may come in any order), panic, read_file, file_exists, getenv, clock, args, and everything else on this page |
write_file, run, exit |
fn main(): par for i in 0..4: let r = write_file(f"out{i}.txt", "hi")error[E0503]: `write_file` can't be called inside a `par` body --> main.br:3:17 |3 | let r = write_file(f"out{i}.txt", "hi") | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not allowed here | = help: its effects would happen in an unpredictable order; do it before or after the `par` = run `brine explain E0503` for more about this errorerror: could not compile due to 1 errorCompute the results in parallel, then write them afterwards. Some os functions
have the same rule; see the os module.
Related: strings, arrays, the os module, C interop.