Skip to content

Functions and Calls ​

One namespace, two call forms ​

There is one function namespace, and f(x, a) and x.f(a) are the same call. The method form passes the receiver as the first argument. It never runs anything stored in the value: f always resolves to a built-in or a host function.

bonsai
toUpperCase(user.name) // => "ADA"
user.name.toUpperCase() // => "ADA"
sum(prices) // => 15
prices.sum() // => 15
round(3.14159, 2) // => 3.14
(3.14159).round(2) // => 3.14

Chaining methods reads left to right, like the pipelines of other languages:

bonsai
items.map(.price * .qty).sum().toFixed(2) // => "18.00"

length is a property, not a function; has and try are special forms. The full list of built-ins is in Built-in Functions.

Overloads ​

Built-in functions are overloaded on parameter types. includes works on text and on lists; min takes a list or several numbers; sum adds numbers or durations. When argument types are known, the overload is chosen by the checker; otherwise it is chosen at run time from the kinds of the arguments.

bonsai
"hello".includes("ell") // => true
[1, 2, 3].includes(2) // => true
min(4, 9, 2) // => 2
prices.min() // => 2
[hours(1), minutes(30)].sum() // => PT1H30M

A call that matches no overload is an error:

bonsai
round("a") // error: NO_OVERLOAD

Spread arguments ​

...list expands a list into arguments:

bonsai
max(...prices) // => 9
max(1, ...prices, 20) // => 20

A spread argument cannot be used with a function that takes a lambda.

How many arguments a spread supplies is known only when the call runs, but its types are checked up front: every parameter the spread may fill must accept the list's element type. A mixed list such as pad(...["a", 2]), whose element type is string | number, is a NO_OVERLOAD check error even though pad("a", 2) would run. Pass such arguments one by one.

null arguments ​

A null argument selects an overload whose parameter accepts null. If none does and the null is the first argument (the receiver), the call is a NULL_RECEIVER error in either call form, or null when the call uses ?.. A null in any later position is a NO_OVERLOAD error, and ?. does not change that: it only skips a call on a null receiver.

bonsai
user.nickname.toUpperCase() // error: NULL_RECEIVER
toUpperCase(user.nickname) // error: NULL_RECEIVER
user.nickname?.toUpperCase() // => null
(user.nickname ?? "").toUpperCase() // => ""
isEmpty(user.nickname) // => true
user.name?.startsWith(user.nickname) // error: NO_OVERLOAD
user.name.startsWith(user.nickname ?? "") // => true

Functions such as isEmpty, toString, and type accept null, and the calendar functions accept null as the time zone (meaning UTC).

Regular expressions ​

matches(text, pattern) uses JavaScript regular expression syntax, as with the u flag, restricted to what can run in time linear in the pattern and the text, so a pattern a user writes cannot hang evaluation. A pattern using any of these is an INVALID_ARGUMENT error:

  • backreferences (\1, \k<name>) and lookaround ((?=, (?!, (?<=, (?<!);
  • named groups ((?<name>...));
  • Unicode property escapes (\p{...}, \P{...}) and control escapes (\cX);
  • inline flags other than a leading (?i), and flag groups such as (?i:...);
  • a quantifier on an assertion ((?:^)*, \b+);
  • a repeat count above 1000, groups nested more than 100 deep, or a pattern that compiles to more than 5000 instructions.

A leading (?i) makes the match ignore ASCII case (other letters match exactly). A backslash before ASCII punctuation that needs no escape (\-, \!) matches that character; before any other character it is an error. A pattern longer than maxPatternLength (default 4,096) is a PATTERN_LIMIT error, which try() does not catch.

bonsai
code.matches("^INV-[0-9]+$") // => true
"Hello".matches("(?i)^hello$") // => true
code.matches(pattern) // error: INVALID_ARGUMENT

Host functions ​

The host can add functions with typed parameters. They are called exactly like built-ins, in either form. A host function with the same name as a built-in replaces the built-in for that environment, so built-ins added in future releases never change the meaning of an existing host's expressions. See Host Functions.

Evaluation order ​

Operands, arguments, and lambda invocations are evaluated left to right, one at a time, including in async mode. Higher-order built-ins call their lambda in list order and stop as soon as the result is known (some, every, find, ...).

Released under the MIT License.