The builtin library
FormScript gives you a library of functions, so most of what you need does not have to be written. There are 68 of them, and this page groups them by what they are for.
Converting
| The call | What it does |
|---|---|
toString(v) |
the value as text |
toNumber(v) |
the value as a number |
toBoolean(v) |
the value as true or false |
toDate(v) |
the value as a date |
FormScript does not convert implicitly, so these are how you convert on purpose.
Asking what a value is
| The call | What it does |
|---|---|
typeOf(v) |
the name of the type of the value |
isEmpty(v) |
is it absent or empty? |
isUnknown(v) |
is it unknown? |
isInapplicable(v) |
is it inapplicable? |
isAbsent(v) |
is it any kind of absence? |
isKnown(v) |
is it a real value? |
typeOf is how you ask a question about a type at runtime. It answers with the
name of the type of the value, not of the type the document declares: one of
string, integer, number, boolean, array, object, date, null or
inapplicable. A decimal with no fractional part is an integer, a date
literal is a string because a date is its own source text, and a value that
could not be evaluated has no name at all — the error value propagates.
Text
| The call | What it does |
|---|---|
len(s) |
the length in characters |
graphemeLen(s) |
the length in grapheme clusters — what a person sees |
lower(s) / upper(s) |
change the case |
trim(s) |
remove the space at both ends |
contains(s, t) |
does it contain it? |
startsWith(s, t) / endsWith(s, t) |
does it begin or end with it? |
replace(s, pattern, with) |
replace the matches |
substring(s, from, to) |
part of the text |
indexOf(s, t) |
where it is, or −1 |
split(s, separator) |
a list of pieces |
join(list, separator) |
a list, joined back together |
matches(s, pattern) |
does it match the pattern? |
slugify(s) |
a URL-safe form of the text |
len and graphemeLen are different on purpose. A flag emoji is one
character to a person and several to a computer; graphemeLen is the one that
matches what the reader sees.
slugify is NFKD, strips the accents, lowercases, and replaces every run of
characters that are not a–z or 0–9 with a single -.
Numbers
| The call | What it does |
|---|---|
abs(n) |
the absolute value |
ceil(n) / floor(n) / round(n) |
round up, down, or to the nearest |
min(a, b) / max(a, b) |
the smaller or the larger |
avg(list) |
the mean |
clamp(n, lo, hi) |
the value, held inside the range |
pmt(rate, periods, principal) |
the level payment |
pmt is the one that makes a loan form work:
field monthly { type = number value = pmt(0.05 / 12, term, amount) }
It is the payment that pays the principal off over the number of periods, at
the rate per period — which is why the rate is 0.05 / 12 and not 0.05.
Dates and times
| The call | What it does |
|---|---|
now() |
the moment of the current transaction |
today() |
the date of the current transaction |
dateAdd(d, n, unit) |
n units later |
dateDiff(a, b, unit) |
whole units from a to b |
year(d) / month(d) / day(d) / weekday(d) |
the parts of a date |
isBefore(a, b) / isAfter(a, b) |
ordering |
age(d) |
the age of a person born on that date |
The unit is one of "s", "m", "h", "d", "w", "mo", "y" — seconds,
minutes, hours, days, weeks, months, years.
Months are clamped rather than overflowed: dateAdd(2026-01-31, 1, "mo") is
2026-02-28, not the 3rd of March. The specification says so, so that two
runtimes cannot disagree about a birthday.
Lists
| The call | What it does |
|---|---|
first(list) / last(list) |
the ends |
sort(list) |
in order |
unique(list) |
without the duplicates |
flatten(list) |
one level deeper |
take(list, n) / skip(list, n) |
a part of the list |
minOf(list) / maxOf(list) |
the smallest or largest |
count(list) |
how many |
sum(list) |
the total |
position() |
which element am I, inside a repeat |
About the form itself
| The call | What it does |
|---|---|
filledFields() |
the paths of the fields that hold an answer |
isActive(path) |
is that field being asked? |
isOverridden(path) |
was that field's answer replaced? |
shadow(path) |
the value a field held before it stopped applying |
These four read the state of the form rather than a value you pass them, and they are what a save-and-resume feature is written with.
Text and money in a language
| The call | What it does |
|---|---|
t("key") |
the label for a key, in the current language |
formatDate(d, pattern) |
a date, written out |
formatMoney(n, currency) |
an amount of money, written out |
About absence
| The call | What it does |
|---|---|
coalesce(a, b, …) |
the first argument that is neither absent nor empty |
ifNull(a, b) |
b if a is absent or empty |
orDefault(a, b) |
b if a is absent |
iif(cond, a, b) |
a if cond is true, b otherwise |
These are in section 6, and they are the ones you will use most.
A rule about the list
The library is checked against the specification at build time. A builtin added to the specification without an implementation turns the build red, and a builtin implemented without being in the specification is a different kind of red. The list on this page is the whole list — there is nothing hidden behind an import.