Surf
Standard library v0
The methods of the built-in types, the built-in names and the operators.This page as MarkdownStatuscurrent on 2026-11-06, for version 0. The table of methods in crates/surf-check/src/infer.rs is the source of this page.
Related documentslanguage spec (sections 4, 6 and 8), getting started, limits of version 0
The standard library of version 0 is small: the methods of the built-in types, Int.parse and Float.parse, the built-in names, and the operators. A file uses them without an import. Each method gives the same result on every target. tests/compare.sh compares the results of the native target, the interpreter and the web target.A method that changes its value needs a place, for example a state or a let variable (spec 5.2). A call of such a method is a statement of its own: todos.push(t) is valid, but const n = todos.push(t) is a compile error.Int is an integer from −(2^53 − 1) to 2^53 − 1 on all targets. Float is an IEEE 754 binary64 number.Read a number from a String:Methods that change the list. Each is a statement of its own:Changes:The theme tokens, for example $accent and $text-secondary, are also built-in names. The layout spec, section 2.1, lists them.The module Locale formats values for a person in the locale of the app (ADR-0132, spec 14). A locale is a BCP 47 tag, for example en, de or pt-BR.surf.toml lists the locales of the project: locales = ["en", "de"]. At the start, the app finds the first language of the system that fits an item of the list, with the lookup of RFC 4647: de-CH fits de (spec 14). Else it takes the first item of the list. If a project uses Locale and its surf.toml has no key locales, or an empty list, the compiler gives an error. The message gives an example line, for example locales = ["en"].
Method
Result
Rule
i.toFloat()
Float
The same value as a Float
f.toInt()
Int
Removes the fraction: 2.7 gives 2, -2.7 gives -2
f.round()
Int
The nearest integer. A value halfway between two integers goes away from zero: 2.5 gives 3, -2.5 gives -3
f.floor()
Int
The nearest integer that is not more than f
f.ceil()
Int
The nearest integer that is not less than f
Function
Result
Rule
Int.parse(s)
Int?
An optional + or -, then the digits 0 to 9: Int.parse("-042") is -42. Other text, and a value outside the range, give none
Float.parse(s)
Float?
An optional sign, digits with an optional . and fraction, and an optional exponent: Float.parse("-.5e2") is -50.0. Other text, and a value that is too large, give none
- Both functions accept no white space: Int.parse(" 4") is none. Use Int.parse(draft.trim()) for the text of an Input.
- The four conversions from Float give the runtime error "the Float is NaN or outside the Int range" for NaN, and for a result outside the Int range.
- An Int result outside the range is the runtime error "the result is outside the integer range".
- a / b with two Int values removes the fraction. a % b has the sign of a. Division by zero is a runtime error for Int, and gives infinity or NaN for Float.
- An operation cannot mix Int and Float. Write n.toFloat() * 1.5.
- "{x}" writes a Float with the shortest text that gives the same value again: 0.1 + 0.2 gives 0.30000000000000004, and 2.0 gives 2.
Method
Result
Rule
n.abs(), f.abs()
Int, Float
The value without its sign. A negative literal needs parentheses: (-3).abs()
a.min(b), a.max(b)
the type of a
The smaller or the larger value. For Float, NaN when a value is NaN
x.clamp(lo, hi)
the type of x
lo.max(x.min(hi)): x kept between lo and hi. When lo is above hi, the result is lo
f.sqrt(), f.sin(), f.cos(), f.tan(), f.exp(), f.log()
Float
log is the natural logarithm. The angles are in radians
f.pow(e), y.atan2(x)
Float
f to the power e, and the angle of the point (x, y)
Float.pi
Float
π
f.format(decimals)
String
f with 0 to 15 digits after the point. The value times 10 to the power of decimals rounds half away from zero, as round does: 0.125.format(2) is "0.13", 1.005.format(2) is "1.00". A value that is too large at that scale, NaN and the infinities give the text of "{f}"
- sqrt, sin, cos, tan, exp, log, pow and atan2 use the math library of the target. The results can differ in the last bit between targets, so show them with format.
- pow follows IEEE 754 on each target: 1.0.pow(e) is 1 for each e, also NaN, and (-1.0).pow(e) is 1 for an infinite e.
Method
Result
Rule
s.count()
Int
The number of scalar values: "aé😀".count() is 3
s.isEmpty()
Bool
true for ""
s.contains(t)
Bool
true when t is in s
s.startsWith(t), s.endsWith(t)
Bool
s.slice(from, to)
String
The scalar values from from to to, without to. The method keeps each bound between 0 and s.count(), and to is at least from: "abc".slice(-1, 9) is "abc", "abc".slice(2, 1) is ""
s.chars()
List<String>
One string for each scalar value
s.split(sep)
List<String>
The parts between the separators: "a,b,".split(",") is ["a", "b", ""]. An empty separator gives the same list as chars()
s.trim()
String
Without the white space at the start and at the end. White space is each character with the property White_Space of Unicode, for example U+0085 and U+3000, and not U+FEFF
s.upper(), s.lower()
String
The Unicode default case mapping, the same for every locale
s.replace(old, new)
String
Each old becomes new. An empty old changes nothing
s.indexOf(t)
Int?
The index of the first t in scalar values, or none
s.hash()
Int
The hash of the trait Hashable (spec 7.8): the same number on each target. A later version can give another number
- a + b joins two strings. String has an impl of the operator trait Add (spec 7.8, ADR-0134). A record with an impl of an operator trait can use its operator too, for example a + b of two Vector values with an impl of Add.
- "{n} items" puts values into a string. {...} takes an Int, a Float, a Bool or a String. A type with an impl of the trait Text can go into {...} too (spec 5.4, ADR-0134).
- == compares the scalar values. <, <=, > and >= compare scalar value by scalar value on the native targets and in the interpreter. The web compares by UTF-16 code units, as JavaScript does (spec 4.4, ADR-0113). The two orders differ only for a few characters, for example most emoji. For the order of a language, use Locale.compare (section 6a) with sortWith (section 3).
Method
Result
Rule
xs[i]
T
The element at i. An index outside the list is the runtime error "the index is outside the list"
xs.get(i)
T?
The element at i, or none for an index outside the list, also for a negative index
xs.first(), xs.last()
T?
none for an empty list
xs.count()
Int
xs.isEmpty()
Bool
xs.contains(v)
Bool
true when an element is equal to v (==, spec 5.1)
xs.slice(from, to)
List<T>
The elements from from to to, without to, with the bounds rule of String.slice
xs.filter(x => cond)
List<T>
The elements for which cond is true, in their order
xs.flatMap(x => list)
List<U>
The lists of the elements, as one list (ADR-0052)
xs.reversed()
List<T>
A new list in the other order
xs.concat(ys), xs + ys
List<T>
A new list: the elements of xs, then of ys
xs.unique()
List<T>
The first of each value, in the order of the list. For Int, String and Bool
xs.find(x => cond)
T?
The first element for which cond is true
xs.map(x => value)
List<U>
One value for each element
xs.any(x => cond), xs.all(x => cond)
Bool
true when cond is true for one element, or for all elements. An empty list gives false and true
xs.indexOf(v)
Int?
The index of the first element that is equal to v, or none
xs.sum()
Int, Float
The sum of a List<Int> or a List<Float>. An Int sum outside the range is a runtime error
xs.join(sep)
String
The strings of a List<String> with sep between them
Method
Rule
xs.push(v)
Adds v at the end
xs.insert(i, v)
Puts v at index i. i can be from 0 to xs.count(). Another index is a runtime error
xs.removeAt(i)
Removes the element at i. An index outside the list is a runtime error
xs.remove(v)
Removes the first element that is equal to v. Without such an element, the list does not change
xs.clear()
Removes all elements
xs.sort()
Orders the list by <. The type of the elements must have the trait Comparable (spec 7.8, ADR-0134): Int, Int64, Float, String, or a type with an impl of Comparable
xs.sortBy(x => key)
Orders the list by a key, for example todos.sortBy(t => t.title). The type of the key must have the trait Comparable
xs.sortWith((a, b) => order)
Orders the list with a compare function that gives an Ordering (ADR-0132). .less puts a before b. For example, names.sortWith((a, b) => Locale.compare(a, b)) sorts in the order of the locale
- The three sorts are stable: equal elements keep their order. For sortWith, two elements are equal when the compare function gives .equal.
- Each runtime uses the same stable merge sort for the three sorts, also the web (ADR-0132). So each target gives the same order, also when the order is not consistent. An example is a compare function or an impl of Comparable that gives .less for (a, b) and also for (b, a). Spec 4.5 gives the exact form of the merge sort.
- sortBy gets the key of each element one time, before the sort starts. sortWith calls the compare function for each comparison of the merge sort.
- sortWith is for an order that no key gives, for example the order of a language (ADR-0132).
- The lambda of filter, find, map and sortBy has one parameter and a value as its body. The lambda of sortWith has two parameters and a value as its body. Each runs before the call returns, so it can read the parameters, the local values and the reactive values around it.
- contains, indexOf and remove compare with ==. A list of dyn values cannot use them, because a dyn value has no == (spec 5.1, ADR-0135).
- xs[i] = v and xs[i].done = true write one element.
- for x in xs { ... } and for x, i in xs { ... } go through the list as it was when the loop started (spec 6.1).
Method
Result
Rule
m[k]
V?
The value of k, or none
m.count()
Int
The number of keys
m.isEmpty()
Bool
m.contains(k)
Bool
true when m has the key k
m.keys()
List<K>
The keys, in the order in which they were first added
m.values()
List<V>
The values, in the order of their keys
Statement
Rule
m[k] = v
Adds or replaces the value of k. A new key goes to the end. A replaced value keeps the position of its key
m.remove(k)
Removes the key k and its value
m.clear()
Removes all keys
- for k, v in m { ... } goes through the keys in their order.
- Two maps are equal when they have the same keys with equal values. The order of the keys does not count.
Form
Result
Rule
text.bytes()
Bytes
The UTF-8 bytes of a String
Bytes.from(list)
Bytes
The bytes of a List<Int>. A number outside 0 to 255 is a runtime error
b.count()
Int
The number of bytes
b.isEmpty()
Bool
True when there are no bytes
b.get(i)
Int?
The byte at the index i, or none
b.slice(from, to)
Bytes
The bytes from from to to, with the bounds of xs.slice
b.text()
String
The UTF-8 text. An invalid sequence becomes U+FFFD
b.toList()
List<Int>
The numbers
Form
Rule
none
The empty value of each type T?
a ?? b
a when it has a value, else b
a?.b
none when a is none, else a.b
if let x = a { ... }
Runs the block when a has a value. x is the value
#if let x = a { ... } else { ... }
The same in markup. else if let continues the block
a.toList()
A list with the value of a, or an empty list
Result<T, E>
A built-in enum with the cases ok(T) and err(E). Use match to read it
r?
In a function that gives Result<_, E>: returns the err value at once, or gives the ok value
Name
Type
Rule
sleep(ms)
async fn (Int) -> Void
A task that ends after ms milliseconds. Use it with await (spec 8)
interval(ms)
async fn (Int) -> Void
A task that ends at the next multiple of ms on the clock of the app. A loop with await interval(1000) ticks each second and does not drift. Use it in place of a timer (ADR-0026)
panic(message)
(String) -> Void
Stops the current batch with a runtime error. A path that ends in panic needs no return
Int64.from(n), Int64.parse(text)
Int64, Int64?
A whole number with 64 bits (ADR-0084). +, -, *, /, % and compares work on two Int64 values. x.toInt() gives an Int
Time.now()
Int
The milliseconds since 1970-01-01 UTC
Time.utc(ms), Time.local(ms)
TimeParts
The date of ms in UTC or in the time zone of the machine: year, month, day, hour, minute, second, millisecond and weekday (ADR-0079). weekday is 1 for Monday
Time.iso(ms)
String
The ISO 8601 text, for example 2026-11-01T12:00:00.000Z. A year outside 0 to 9999 has a sign and six digits: +010000-01-01T00:00:00.000Z
Time.parse(text)
Int?
The milliseconds of an ISO 8601 date. A text with no zone is in UTC. none for another text
Theme.designWidth(), Theme.designHeight()
Float
scale.design of style.toml, at compile time
Theme.minScale(), Theme.maxScale()
Float
scale.min and scale.max of style.toml, at compile time
Http.event(url)
Task<Result<String, String>>
The next message of a stream of server-sent events (ADR-0077). An err ends the stream, and the next call opens it again
File.pick(accept)
Task<PickedFile?>
The file dialog of the platform (ADR-0078). A PickedFile has name and bytes. none when the user closes it
Storage.get(key)
String?
The text of key in the store of the app (ADR-0074). none on a server
Storage.set(key, value), Storage.delete(key)
nothing
Writes or deletes a value. A store that refuses a write has no effect
Theme.setScale(factor)
nothing
Scales the whole UI, from 0.5 to 3 (ADR-0085)
Theme.setToken(name, value)
nothing
Changes a token of the theme while the app runs (ADR-0082, ADR-0130). The signature is Theme.setToken<T: Token>(name: String, value: T). Token is a built-in trait for Color and Float (ADR-0134). A Color changes a color in each variant. A Float changes a length in logical px. With a literal name, only that length token can change. With a computed name, each length token can change. An app that never gives a length has no run-time code for lengths (layout spec, section 2.1). With a literal name, a value of the wrong kind is a compile error, for example a Float for the color token accent. With a computed name, a value of the wrong kind has no effect, as an unknown name has. A length that is not finite has no effect
Theme.setMode(mode)
nothing
Chooses the theme: .system, .light, .dark, .contrastLight or .contrastDark of the built-in enum ThemeMode (ADR-0072, ADR-0103). The app stores the choice.
Focus.to(id)
nothing
Gives the keyboard focus to the element with this id, at the end of the batch (ADR-0102)
Focus.newId()
String
An id that no other call gives, for id, controls and labelledBy (ADR-0102)
Json.embed("../x.json")
The expected type
The value of a JSON file at compile time. The path starts at the folder of the sources, and it can leave that folder. A file that does not fit the type is a compile error. A key with no field is ignored.
Random.float()
Float
A random number from 0 up to, but without, 1. It is not for secrets
Random.int(lo, hi)
Int
A random Int from lo up to, but without, hi. lo when hi is not above lo
Color.parse(text)
Color?
A color from text at run time: #rgb, #rrggbb, #rrggbbaa, rgb(r g b / a) or transparent (ADR-0050). none for another text
Color.rgb(r, g, b, a)
Color
The color rgb(r g b / a) of three Int channels from 0 to 255 and a Float alpha from 0 to 1 (ADR-0160). A value outside its range takes the nearest end, as in CSS
Color.mix(a, b, t)
Color
The color at t from a (0.0) to b (1.0): color-mix(in srgb, a, b t%) of CSS (ADR-0160). The channels mix with their alpha, as in a transition, so a mix with transparent has no gray. A token stays a token, so the mix follows the theme. t outside 0 to 1 takes the nearest end
Icon.parse(text)
Result<Icon, String>
The icon of SVG text that the app gets while it runs, for example from the network. The rules are those of an imported file (ADR-0030). The error has the line. Only an app that calls it links the parser
Http.get(url)
Task<Result<HttpResponse, String>>
A built-in async fn: a GET request (ADR-0037). err means that no response came. A status of 404 is ok
Http.send(request)
Task<Result<HttpResponse, String>>
The same for an HttpRequest: url, method (default "GET"), headers and body. A request ends after 30 seconds, and with its component
Json.decode(text)
Result<T, String>
The value of the expected type T (ADR-0038): const feed: Feed = Json.decode(text)?. The error has a path. A key that is not a field does not matter. A field of a type T? can be missing
Json.encode(value)
String
The JSON text of a value, in the format of JSON.stringify
scalar.text()
String
The text of a Scalar (ADR-0045): the text itself, a number as in {x} of a Float, or true or false
Window.width, Window.height
Float
The size of the window in logical px (ADR-0106). A read has the dependencies of a state. Only a component can read it. A server gives 0
Platform.os
Os
The target: .macos, .windows, .linux, .android, .ios or .web. It is a constant of the build, so the compiler removes the code of the other targets in if Platform.os == .web { ... }
Function
Result
Rule
Locale.number(x, style: NumberStyle = .decimal)
String
An Int or a Float with the decimal separator and the grouping of the locale: Locale.number(1234.5) is "1,234.5" in en and "1.234,5" in de
Locale.date(ms, style: DateStyle = .medium)
String
The date of ms in the time zone of the machine, with the names of months and weekdays of the locale
Locale.time(ms, style: DateStyle = .medium)
String
The time of day of ms in the time zone of the machine
Locale.relative(ms)
String
The text of a difference of ms milliseconds: Locale.relative(-3 * 86_400_000) is "3 days ago" in en. A negative value is in the past
Locale.list(items)
String
The strings of a List<String> with the words of the locale: ["a", "b", "c"] gives "a, b, and c" in en and "a, b und c" in de
Locale.plural(n)
PluralCategory
The plural category of an Int or a Float in the locale
Locale.compare(a, b)
Ordering
The order of two strings in the rules of the locale. .less when a comes before b. xs.sortWith((a, b) => Locale.compare(a, b)) sorts a list in this order (section 3)
Locale.set(tag)
nothing
Changes the locale while the app runs. A tag that fits no item of the list gives the first item. The change is not saved. A derived value and a markup expression cannot call it, because it is a write
Locale.current
String
The tag of the current locale. It is always an item of the list
- NumberStyle has the cases decimal, percent and currency(code: String), with a currency code of ISO 4217: style: .currency(code: "EUR"). A percent shows x times 100.
- A decimal number has at most 3 digits after the separator, and a percent has none. A currency has the digits of the currency. The value rounds half away from zero, as round does.
- DateStyle has the cases short, medium and long. The locale gives the pattern of each length.
- Locale.relative uses the largest unit in which the amount is at least 1. The units are year (365 days), month (30 days), week, day, hour and minute. Else it uses seconds. The amount drops its fraction.
- PluralCategory has the categories of CLDR: zero, one, two, few, many and other. English uses only one and other.
- A read of Locale.current and a call of a function of Locale have the dependencies of a state. After Locale.set, each derived value, effect and text that uses a format updates.
- Locale is valid in each function, also in a function of a .surfs file: fn due(t: Todo) -> String = Locale.date(t.due);. The checker follows each call, so a component that calls such a function updates after Locale.set too (spec 10.2).
- A module constant cannot read Locale, also through a call. This is a compile error. Use a function, for example fn title() -> String (spec 7.3).
- A call of a function prop reads Locale when a function value of the type of the prop in the app reads Locale (spec 10.2).
- Locale.set does not save the locale, as Theme.setToken does not save a token (ADR-0082). An app can save the choice with Storage.
- On a SurfWave server, each request takes the first tag of its Accept-Language header that fits an item of the list, with the same lookup. The tags have the order of their weights. Else it takes the first item of the list. The page hydrates with the same locale. After Locale.set, the header x-wave-locale replaces Accept-Language (SurfWave, section 5).
- The web uses Intl of the browser. The native targets and the interpreter use the ICU of the system (ADR-0132). The two can have different versions of the data, so a few results can differ (see native differences).
- Locale.plural rounds a Float to 3 digits after the separator first, as Locale.number does: Locale.plural(1.0001) is .one in en.
- An app that uses Locale loads ICU when it starts. A system without ICU stops the app at its start with an error that names the package of ICU. The .deb of such an app depends on ICU, and its MSIX package needs Windows 10 version 1903 (ADR-0086, ADR-0141).
Task
Way in version 0
A repeated action, as setInterval does
A loop with await interval(ms) in an async fn, started in on mount. The runtime stops it with its component