9. Strings and the standard library

The standard library comes in two layers. A few high-frequency names (println, map/filter/fold, len, to_string, …) live in the prelude and are available everywhere; everything else lives in a module, brought in with use std/x and called qualified as x.fn(...) — strings are in std/str, and there are also std/list, std/map, std/set, std/bytes, std/io and std/cursor. A hot name can be imported selectively (use std/str.{trim}).

String functions work in code points. str.split separates on a literal, not a regex; join is its inverse:

use std/str

pub fn main() -> Unit !io = {
  let parts = str.split("a,b,c", ",")
  println(to_string(len(parts)))
  println(join(parts, " - "))
}
Open in playground
3
a - b - c

There are three ways to write a string, and their blind spots complement each other. Double quotes "..." support escapes and $ interpolation; triple quotes """ span lines, strip the common indent and need no escaping for quotes (interpolation still applies); and backticks `...` are a raw string — no escapes, no interpolation, may span lines, so a regex, a code sample or a fragment of HTML is worth exactly what it looks like (the one restriction: the content may not contain a backtick):

pub fn main() -> Unit !io = {
  println(`"quotes" and $dollar and \n stay literal`)
}
Open in playground
"quotes" and $dollar and \n stay literal

parse_int turns a string into an Option[Int] — failure is None, not an exception:

fn parseOr(s: String, fallback: Int) -> Int =
  match parse_int(s) {
    Some(n) -> n
    None -> fallback
  }

pub fn main() -> Unit !io = {
  println(to_string(parseOr("42", 0)))
  println(to_string(parseOr("oops", -1)))
}
Open in playground
42
-1