A Subset of Gleam
The project's interpreter implements the subset of Gleam described on this page. Anything outside this subset is reported as an error instead of being evaluated.
The draw Function
Every drawing defines
fn draw(shape_slider: Int) -> List(Path) {
...
}
The shape_slider parameter is a whole number in [0, 100] taken from the
shape slider in the app, and the returned list of paths is what the animator
draws. A drawing may also define other top-level functions.
Values
The interpreter has integers, booleans, lists, functions, and the types that describe a drawing:
type Point {
Point(x: Int, y: Int)
}
type Shape {
Line(start: Point, end: Point)
}
type Path {
Path(shape: Shape, start: Int, end: Int, thickness: Int)
}
A Path names the Shape to draw, the start and end times between which
it is drawn (in tenths of a second), and a line thickness. A Point is a position
on the canvas: (0, 0) is the center, x and y run from -500 to 500, and positive y
is down. The thickness uses the same units, so a line looks thicker on a larger
canvas.
Built-in Functions
random(min: Int, max: Int) -> Int— a randomIntin[min, max).abs(n: Int) -> Int— the absolute value ofn.- The constructors of the record types above:
Point,Line, andPath. These are the only functions that accept labelled arguments, such asPoint(x: 0, y: 1). - Some
gleam/listfunctions:list.map,list.filter,list.append,list.length, andlist.flatten. They take the same arguments and behave the same way as ingleam/list, but noimportis needed.
Language Features
-
Functions — the
drawfunction, top-level helper functions, and anonymous functions defined within the body of other functions (e.g.,fn(x) { x * 2 }). As in Gleam, a function's parameters must have distinct names, as must the top-level functions and the variables in one pattern (discards such as_can repeat). -
Function calls — the call's target is any expression that evaluates to a function: a built-in (
random,list.map), a user-defined helper, alet-bound function, or another call's result (e.g.,f(x)(y)). -
Integers including literals (e.g.
4096), arithmetic operators (+,-,*,/,%) and comparison operators (<,>,<=,>=,==,!=). As in Gleam,/truncates toward zero,%is the remainder (with the sign of the left operand, so-7 % 3is-1), and both yield0when dividing by zero. -
Booleans including
True,False, and the operators&&and||. As in Gleam, each one only evaluates its right side if it's needed, so inFalse && f(x),fis not called.==and!=compare any two values, not just integers. -
Dot expressions — For example,
p.xandp.yfor the coordinates of aPointp. -
Variables bound by pattern arms in case expressions.
-
letbindings —let name = exprbinds the result ofexprtonamefor use in the rest of the block. -
caseexpressions to branch on aBoolorIntvalue or to destructure aList. -
Integer patterns — For example,
0 -> ...matches only the integer0. -
List patterns — For example,
[a, b](exactly two elements),[a](exactly one),[a, b, ..rest](two or more, bindingrestto a list of the others),[a, b, ..](two or more, ignoring the others), and[_, next, ..](ignore the first, bind the second). -
The wildcard
_to discard values in patterns. -
List prepending —
[a, b, ..rest]as an expression builds a new list by prepending elements to an existing listrest(the expression form of the..rest pattern, allowed only at the end of a list literal). -
Function captures —
add(1, _)as shorthand forfn(x) { add(1, x) }. -
The pipe operator —
x |> f(a)passesxas the first argument of the call, i.e.f(x, a), andx |> fcallsfwithxas its only argument. Pipes chain, sopaths |> list.filter(keep) |> list.map(thicken)reads as a left-to-right pipeline. -
usebindings —use x <- f(a)callsfwith one extra final argument: an anonymous function whose parameter isxand whose body is the rest of the block. Souse p <- list.map(points) Point(p.x, p.y + 10)is shorthand for
list.map(points, fn(p) { Point(p.x, p.y + 10) }). Multiple names (use a, b <- f) become multiple parameters, and ausecannot be the last line of a block, since the rest of the block is the function's body.
What Is Not Included
Other Gleam features are not available, such as string or float literals, tuples, and custom type definitions. Adding some of them is one possible extension of the project.
Evaluation is also bounded, so that a drawing cannot hang the browser: a program that recurses too deeply, does too much work in total (no matter how that work is spread across recursive branches), or builds a list that grows too long stops with an error rather than running forever or crashing the app.