The standard library
These pages are for writing programs. Building on the library, or replacing parts of it, is under Host developers.
The standard library is a language: the primitive types, the ops every application has, and the symbols that are sugar over them. It is what you get before your application adds anything of its own.
The pages beside this one are generated from the library itself, one per segment, each op with its real signature and an example that was run to produce the value shown. They cannot drift from the code, because they are read out of it at build time.
This page is the part that is not generated: the conventions all of them assume.
Reading a signature
Section titled “Reading a signature”Every entry leads with one, and it is the real one, printed from the op:
Filter(list: any[], predicate: (any) -> boolean) -> any[]
The name, then each input with its type, then what comes out. Those input names are the ones you use when you pass arguments by name rather than in order, which Operators and symbols covers. The rest of this page is the parts of a signature that are not obvious on sight.
Variadic inputs
Section titled “Variadic inputs”A signature ending in ... takes as many values as you give it:
output sum = Add(1, 2, 3, 4)
output any = Or($a, $b, $c)
output joined = Concat([1, 2], [3], [4, 5])Add(nodes...: number) means one input called nodes that swallows every argument. Six
ops work this way: And, Or, Xor, Add, Multiply and
Concat.
The symbol form of a variadic op takes two at a time, so 1 + 2 + 3 nests where
Add(1, 2, 3) does not. Same answer; the op form says “sum these” more directly.
An input marked ? may be left out: Join(parts~: string[], separator?: string) joins with
nothing between the parts when it gets no separator. One op has one so far. Leaving out any
other input is a missing_op_input warning, with the type’s default standing in.
An input marked ~ converts what it is given. parts~: string[] accepts a list of
anything, and each value in it becomes text before Join runs, so Join([1, 2], ", ")
is "1, 2" with no ToString and no warning. The conversion is the op’s, declared on that
one input and written under its signature; nothing else in the language converts on its own,
and Join is the only op in the library that does. A host can declare the mark on an op of
its own with convert: true, and a lambda can carry it on a parameter,
(t~: string) => Upper(t) (Naming a function).
The shape still has to match: Join(5) is a type error.
any in a signature
Section titled “ in a signature”An input typed any accepts any data value. Length(list: any[]) takes a list of
anything, Equals(a: any, b: any) compares anything with anything.
This is not the op being careless. It is the op saying it does not care, which is true:
Length counts without looking inside. What you lose is the check. Pass Length a
list of strings and nothing complains, because nothing needed to.
Where an op can be more precise, it is, which is the next convention.
Some output types depend on the inputs
Section titled “Some output types depend on the inputs”A signature is the general shape. Several ops narrow it for the particular call:
let numbers = [4, 8, 15]
output kept = Filter(numbers, n => n > 10)Filter’s signature says -> any[], but this call produces a number[],
because the list it was given was one. The same holds for Map (from its function’s
return type), Find (the element type), Reduce (from the starting value) and
If, which gives you the branch type when both branches agree and any when they
do not.
So the reference tells you what an op accepts, and the editor tells you what your call produced. When they differ, the editor is right.
Function-typed inputs
Section titled “Function-typed inputs”An input written (number) -> boolean wants a function. Six list ops have one, and
between them they are how iteration is expressed:
let items = [4, 8, 15, 16, 23]
output big = Filter(items, item => item > 10)There is nothing special about those ops. A function is a value in this language, so an op
taking one is an ordinary op, and you can hand it a lambda written in place or a name bound
earlier. What the signature does not show is that the parameter type is worked out for you: the
item above is a number because items is a list of numbers, so you rarely need to
annotate it.
Lambdas and lists covers writing them.
The segments
Section titled “The segments”The ops are grouped, and the grouping is real: it is a field on each op, which is what splits these pages up.
| Segment | What is in it |
|---|---|
| logic | And, Or, Xor, Not |
| comparison | Equals, NotEquals, LessThan, GreaterThan |
| control | If, Default, IsSet |
| array | Length, Concat, Includes, Average, Min, Max, Flatten |
| arithmetic | Add, Subtract, Multiply, Divide |
| list | Filter, Map, Reduce, Find, Some, Every |
| conversion | ToString, ToNumber, ToBool |
| string | Join, Upper, Lower, Trim, Contains, StartsWith, EndsWith |
Today an application takes the whole library or none of it. The segments are the seam along which that will change, so that an application which never touches a list need not carry the list ops, and these pages can tell you which segments your application has. Until then, if you are reading this inside somebody’s product, assume all of them and check the editor’s own lists for anything extra.
What your application adds
Section titled “What your application adds”Everything here is the floor, not the ceiling. An application embedding Dendrite registers its
own types and its own ops, and those behave exactly like these: same call syntax, same checking,
same reference format if it generates one. You cannot tell from a program whether Filter
came from the standard library or from the application, and that is the point.
what happens to a program that uses them.