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.
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.14Chaining methods reads left to right, like the pipelines of other languages:
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.
"hello".includes("ell") // => true
[1, 2, 3].includes(2) // => true
min(4, 9, 2) // => 2
prices.min() // => 2
[hours(1), minutes(30)].sum() // => PT1H30MA call that matches no overload is an error:
round("a") // error: NO_OVERLOADSpread arguments
...list expands a list into arguments:
max(...prices) // => 9
max(1, ...prices, 20) // => 20A 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.
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 ?? "") // => trueFunctions 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.
code.matches("^INV-[0-9]+$") // => true
"Hello".matches("(?i)^hello$") // => true
code.matches(pattern) // error: INVALID_ARGUMENTHost 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, ...).