Files
functional-programming-snake/course/reference.md
T
Lucas WintherandClaude Opus 5.5 a23fd07906 docs: README, mentor mode and reference
README front page (module map, checkpoint policy with per-track file map,
maintainer tools), MENTOR.md Socratic mentor instructions with AGENTS.md /
CLAUDE.md pointers, and course/reference.md listing every API side by side.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-29 01:17:35 +01:00

18 KiB

Reference: the course's API in three languages

This page lists every type, value and function you write (or are given) in the course, side by side in JavaScript, Scala and F#. The names come straight from the stub files in js/src/, scala/src/ and fsharp/src/; when this page and a stub's doc comment disagree, the stub wins (and please report it).

Use it to look up a spelling or an argument order. It does not explain why; the modules do that.

How the three tracks differ

Topic JavaScript Scala F#
Style of calls standalone functions, data first: map(io, f) methods on the value, io.map(f), plus functions on the companion object: IO.pure(a) module functions, data last, curried: IO.map f io, so io |> IO.map f works
Where a function lives named export of a file: import { map } from './io.js' an object (Rules.turn) or a method of the type (r.flatMap(f)) a module (Rules.turn); open Snake.Model to drop the prefix
flatMap flatMap flatMap bind
filterMap on streams filterMap (function returns a value or null) filterMap (function returns an Option) choose (function returns an option, like List.choose)
Field names camelCase: nextDir camelCase: nextDir PascalCase: NextDir
Sum types strings ('Up') or tagged objects ({tag: 'Turn', dir}) enum: Direction.Up, Action.Turn(dir) discriminated unions: Up, Turn dir
"Nothing here" null None / Some(x) None / Some x
Seeds and random values number (an integer in [0, 2^32)) Long (in [0, 2^32)) uint32
Time in ms number Long int64
"No result" undefined () (Unit) () (unit)
Unimplemented stub todo('m03') ??? failwith "TODO m03"

F#'s pure triggers warning FS0046 ("reserved for future use"); the project files silence it, so the name is fine.

Module 1 — util

Concept JavaScript (js/src/util.js) Scala (scala/src/Util.scala) F# (fsharp/src/Util.fs)
numbers 0..n-1 range(n) Util.range(n: Int): List[Int] Util.range (n: int) : int list
all but the last dropLast(xs) Util.dropLast[A](xs: List[A]): List[A] Util.dropLast (xs: 'a list) : 'a list
membership with a custom equality contains(xs, p, eq), true if some eq(x, p) Util.contains[A](xs: List[A], p: A, eq: (A, A) => Boolean): Boolean Util.contains (xs: 'a list) (p: 'a) (eq: 'a -> 'a -> bool) : bool
left-to-right application pipe(x, ...fns) built in: f andThen g, or x.pipe(f) with import scala.util.chaining.* built in: x |> f, f >> g
right-to-left composition compose(...fns) built in: g compose f built in: g << f
deep freeze deepFreeze(obj) (returns the same object) not needed: data is immutable not needed: data is immutable

Module 2 — model

Concept JavaScript (js/src/model.js) Scala (scala/src/Model.scala) F# (fsharp/src/Model.fs)
a cell {x, y}, built with point(x, y) final case class Point(x: Int, y: Int) type Point = { X: int; Y: int }
point equality pointEquals(a, b) == (case classes compare structurally) = (records compare structurally)
direction 'Up', 'Down', 'Left', 'Right' enum Direction { Up, Down, Left, Right }: Direction.Up type Direction = Up | Down | Left | Right
unit step delta(dir) Model.delta(dir: Direction): Point Model.delta (dir: Direction) : Point
reverse direction opposite(dir) Model.opposite(dir: Direction): Direction Model.opposite (dir: Direction) : Direction
status 'Playing', 'Paused', 'GameOver', 'Won' enum Status: Status.Playing, ... type Status = Playing | Paused | GameOver | Won
actions Action.Tick, Action.Turn(dir), Action.TogglePause, Action.Restart, Action.Frame; values {tag: 'Tick'}, {tag: 'Turn', dir}, ... enum Action: Action.Tick, Action.Turn(dir), Action.TogglePause, Action.Restart, Action.Frame type Action = Tick | Turn of Direction | TogglePause | Restart | Frame
game state {width, height, snake, dir, nextDir, apple, seed, score, status, frame} GameState(width, height, snake: List[Point], dir, nextDir, apple: Option[Point], seed: Long, score, status, frame) { Width; Height; Snake: Point list; Dir; NextDir; Apple: Point option; Seed: uint32; Score; Status; Frame }
fresh game initialState(seed) Model.initialState(seed: Long): GameState Model.initialState (seed: uint32) : GameState

The snake is a list of points, head first. apple is null / None when there is no apple.

Module 3 — rules

Concept JavaScript (js/src/rules.js) Scala (scala/src/Rules.scala) F# (fsharp/src/Rules.fs)
one step from a cell move(p, dir) Rules.move(p: Point, dir: Direction): Point Rules.move (p: Point) (dir: Direction) : Point
on the board? inBounds(state, p) Rules.inBounds(state: GameState, p: Point): Boolean Rules.inBounds (state: GameState) (p: Point) : bool
ask to turn turn(state, dir) Rules.turn(state: GameState, dir: Direction): GameState Rules.turn (state: GameState) (dir: Direction) : GameState
one step of the game advance(state) Rules.advance(state: GameState): GameState Rules.advance (state: GameState) : GameState

Module 4 — random and spawn

Concept JavaScript (js/src/random.js) Scala (scala/src/Random.scala) F# (fsharp/src/Random.fs)
the type a function seed => [value, nextSeed] final case class Random[A](run: Long => (A, Long)) type Random<'a> = Random of (uint32 -> 'a * uint32)
run it run(r, seed) → [a, seed2] r.run(seed) → (a, seed2) (the field) Random.run seed r → (a, seed2)
constant pure(a) Random.pure(a) Random.pure a
transform the value map(r, f) r.map(f) Random.map f r
chain flatMap(r, f) r.flatMap(f) Random.bind f r
the generator (mulberry32) nextUint32 (itself a Random: call nextUint32(seed)) Random.nextUint32: Random[Long] Random.nextUint32 : Random<uint32>
a number in [0, n) intBelow(n) Random.intBelow(n: Int): Random[Int] Random.intBelow (n: int) : Random<int>

In F#, do every * and + of the generator through uint64 helpers so it also works in the browser (Module 4, "F# in the browser").

Concept JavaScript (js/src/spawn.js) Scala (scala/src/Spawn.scala) F# (fsharp/src/Spawn.fs)
empty cells, row-major freeCells(state) Spawn.freeCells(state): List[Point] Spawn.freeCells (state: GameState) : Point list
place an apple spawnApple(state) Spawn.spawnApple(state): GameState Spawn.spawnApple (state: GameState) : GameState
advance, then spawn tick(state) Spawn.tick(state): GameState Spawn.tick (state: GameState) : GameState
fresh game with an apple newGame(seed) Spawn.newGame(seed: Long): GameState Spawn.newGame (seed: uint32) : GameState

Module 5 — game (and its Module 10 additions)

Concept JavaScript (js/src/game.js) Scala (scala/src/Game.scala) F# (fsharp/src/Game.fs)
the update function update(state, action) Game.update(state: GameState, action: Action): GameState Game.update (state: GameState) (action: Action) : GameState
final state of a game replay(state, actions) Game.replay(state, actions: List[Action]): GameState Game.replay (state: GameState) (actions: Action list) : GameState
every state of a game history(state, actions) (length actions.length + 1) Game.history(state, actions): List[GameState] Game.history (state: GameState) (actions: Action list) : GameState list
frames per step (m10) movesEvery(score) Game.movesEvery(score: Int): Int Game.movesEvery (score: int) : int

Module 6 — view

Concept JavaScript (js/src/view.js) Scala (scala/src/View.scala) F# (fsharp/src/View.fs)
pixels per cell CELL (20) View.CELL: Int View.CELL
a rectangle {tag: 'Rect', x, y, w, h, color} Shape.Rect(x, y, w, h, color) Rect(x, y, w, h, color) (union case of Shape)
a text {tag: 'Text', x, y, text, color, size, align} Shape.Text(x, y, text, color, size, align) Text(x, y, text, color, size, align)
a picture an array of shapes type Scene = List[Shape] type Scene = Shape list
state to picture view(state) View.view(state: GameState): Scene View.view (state: GameState) : Scene

Module 7 — IO

Concept JavaScript (js/src/io.js) Scala (scala/src/IO.scala) F# (fsharp/src/IO.fs)
operations {tag: 'Draw', shape}, {tag: 'RandomSeed'} enum Op: Op.Draw(shape), Op.RandomSeed type Op = Draw of Shape | RandomSeed
answers to operations plain values: undefined for a draw, a number for a seed enum OpResult: OpResult.Done, OpResult.Seed(value: Long) type OpResult = Done | Seed of uint32
the IO type {tag: 'Pure', value} or {tag: 'Effect', op, k} enum IO[+A]: IO.Pure(a), IO.Effect(op, k: OpResult => IO[A]) type IO<'a> = Pure of 'a | Effect of Op * (OpResult -> IO<'a>)
finished program pure(a) IO.pure(a) IO.pure a
one operation effect(op) IO.effect(op): IO[OpResult] IO.effect op : IO<OpResult>
transform the result map(io, f) io.map(f) IO.map f io
chain flatMap(io, f) io.flatMap(f) IO.bind f io
run in order sequence(ios) IO.sequence(ios: List[IO[Unit]]): IO[Unit] IO.sequence (ios: IO<unit> list) : IO<unit>
draw one shape draw(shape) IO.draw(shape): IO[Unit] IO.draw shape : IO<unit>
ask for a seed randomSeed (a value) IO.randomSeed: IO[Long] (a value) IO.randomSeed () : IO<uint32> (a function of unit)
draw a scene drawScene(scene) IO.drawScene(scene): IO[Unit] IO.drawScene scene : IO<unit>
pure test interpreter trace(io, seed) → {result, ops} IO.trace(io, seed): Traced[A], Traced(result, ops) IO.trace seed io : Traced<'a>, { Result; Ops }

Module 8 — streams

Concept JavaScript (js/src/stream.js) Scala (scala/src/Stream.scala) F# (fsharp/src/Stream.fs)
inputs plain objects {tag: 'Time', t}, {tag: 'KeyDown', key}, {tag: 'KeyUp', key} enum Event: Event.Time(t: Long), Event.KeyDown(key), Event.KeyUp(key) type Event = Time of int64 | KeyDown of string | KeyUp of string
the stream type { step: input => [outputs, next] } final case class Stream[A](step: Event => (List[A], Stream[A])) type Stream<'a> = Stream of (Event -> 'a list * Stream<'a>)
feed one input s.step(input) s.step(event) Stream.step e s
key presses / releases keyDowns, keyUps Stream.keyDowns, Stream.keyUps Stream.keyDowns, Stream.keyUps
a clock interval(ms) Stream.interval(ms: Long): Stream[Long] Stream.interval (ms: int64) : Stream<int64>
transform outputs map(s, f) s.map(f) Stream.map f s
keep some outputs filter(s, p) s.filter(p) Stream.filter p s
transform and drop filterMap(s, f) (drop null) s.filterMap(f) (drop None) Stream.choose f s (drop None)
combine two streams merge(a, b) a.merge(b) Stream.merge a b
running fold scan(s, f, z) s.scan(z)(f) Stream.scan f z s
emit a first value startWith(s, v) s.startWith(v) Stream.startWith v s
run on a list of inputs run(s, inputs) s.run(events) Stream.run events s

Module 9 — input, app, runtime (and Module 10 additions)

Concept JavaScript Scala F#
key to action keyToAction(key) → action or null (input.js) Input.keyToAction(key: String): Option[Action] Input.keyToAction (key: string) : Action option
ms per step TICK_MS = 150 (app.js) App.TICK_MS: Long App.TICK_MS (150L)
ms per frame (m10) FRAME_MS = 25 App.FRAME_MS: Long App.FRAME_MS (25L)
actions, classic clock classicActions App.classicActions: Stream[Action] App.classicActions : Stream<Action>
actions, frame clock (m10) speedActions App.speedActions: Stream[Action] App.speedActions : Stream<Action>
states over time states(seed, actions) App.states(seed: Long, actions): Stream[GameState] App.states (seed: uint32) (actions: Stream<Action>) : Stream<GameState>
draw one state render(state) App.render(state): IO[Unit] App.render (state: GameState) : IO<unit>
the whole program main (a value) App.main: IO[Stream[IO[Unit]]] (a value) App.main () : IO<Stream<IO<unit>>> (a function of unit)
the outside world Platform object: fillRect(x, y, w, h, color), fillText(x, y, text, color, size, align), randomSeed(), startClock(onTime), onKeyDown(cb), onKeyUp(cb) trait Platform with the same method names; randomSeed(): Long, startClock(onTime: Long => Unit) type Platform record of functions: FillRect, FillText, RandomSeed: unit -> uint32, StartClock: (int64 -> unit) -> unit, OnKeyDown, OnKeyUp
interpret an IO runIO(io, platform) (runtime.js) Runtime.runIO(io, p) Runtime.runIO p io
run the program runApp(main, platform) Runtime.runApp(main, p) Runtime.runApp p main
browser platform browserPlatform(canvas, win) (browser.js, you write it) BrowserPlatform(canvas) (scala/web/, given) BrowserPlatform.create canvas, BrowserPlatform.gameCanvas () (fsharp/web/, given)
entry point js/src/main.js (given): runApp(main, browserPlatform(...)) scala/web/Main.scala (given): SnakeMain fsharp/web/SnakeMain.fs (given)

Module 11 — Pong

JS files are in js/src/pong/, Scala files in scala/src/pong/ (objects PongModel, PongRules, PongView, PongApp in package snake.pong), F# files in fsharp/src/Pong/ (modules Snake.Pong.Model, Snake.Pong.Rules, Snake.Pong.View, Snake.Pong.App). In F#, the Pong unions are marked [<RequireQualifiedAccess>], so you always write PongStatus.Playing, Paddle.Up, PongAction.Frame; that keeps them apart from the snake's Playing, Up and Frame.

Concept JavaScript Scala F#
constants W, H, PCELL, PADDLE, PLAYER_X, CPU_X, WIN (model.js) PongModel.W, ... PongModel.WIN Pong.Model.W, ... Pong.Model.WIN
velocity vel: {dx, dy} Velocity(dx, dy) { Dx: int; Dy: int } (type Velocity)
held keys held: {up, down} Held(up, down) { Up: bool; Down: bool } (type Held)
score score: {player, cpu} Score(player, cpu) { Player: int; Cpu: int } (type Score)
status 'Playing', 'Over' enum PongStatus: PongStatus.Playing, PongStatus.Over PongStatus.Playing, PongStatus.Over
paddle keys 'Up', 'Down' enum Paddle: Paddle.Up, Paddle.Down Paddle.Up, Paddle.Down
match state {ball: {x, y}, vel, player, cpu, held, score, status, seed, frame} PongState(ball: Point, vel, player, cpu, held, score, status, seed: Long, frame) { Ball: Point; Vel; Player; Cpu; Held; Score; Status; Seed: uint32; Frame }
actions PongAction.Press(key), PongAction.Release(key), PongAction.Frame, PongAction.Restart; values {tag: 'Press', key}, ... enum PongAction: Press(key: Paddle), Release(key: Paddle), Frame, Restart PongAction.Press of Paddle, PongAction.Release of Paddle, PongAction.Frame, PongAction.Restart
fresh match initialPong(seed) (model.js) PongModel.initialPong(seed: Long): PongState Pong.Model.initialPong (seed: uint32) : PongState
keep in range clamp(lo, hi, x) (rules.js) PongRules.clamp(lo: Int, hi: Int, x: Int): Int Pong.Rules.clamp (lo: int) (hi: int) (x: int) : int
the update function pongUpdate(state, action) PongRules.pongUpdate(state: PongState, action: PongAction): PongState Pong.Rules.pongUpdate (state: PongState) (action: PongAction) : PongState
state to picture pongView(state) (view.js) PongView.pongView(state: PongState): Scene Pong.View.pongView (state: PongState) : Scene
ms per frame FRAME_MS_PONG = 30 (app.js) PongApp.FRAME_MS_PONG: Long Pong.App.FRAME_MS_PONG (30L)
key to action pongKey(isDown, key) → action or null PongApp.pongKey(isDown: Boolean, key: String): Option[PongAction] Pong.App.pongKey (isDown: bool) (key: string) : PongAction option
actions over time pongActions PongApp.pongActions: Stream[PongAction] Pong.App.pongActions : Stream<PongAction>
states over time pongStates(seed, actions) PongApp.pongStates(seed: Long, actions): Stream[PongState] Pong.App.pongStates (seed: uint32) (actions: Stream<PongAction>) : Stream<PongState>
the whole program pongMain (a value) PongApp.pongMain: IO[Stream[IO[Unit]]] Pong.App.pongMain () : IO<Stream<IO<unit>>>
entry point (given) js/src/pong/main.js, page js/pong.html PongMain in scala/web/Main.scala, page scala/pong.html fsharp/web/PongMain.fs, page fsharp/pong.html

Tests and tools

Task JavaScript (cd js) Scala (cd scala) F# (cd fsharp)
one module's tests node --test "test/m03-*.test.js" or ./test.sh M03 ./test.sh M03 ./test.sh M03
all tests (with the purity audit) ./test.sh ./test.sh ./test.sh
test files test/m03-rules.test.js test/M03Rules.test.scala tests/M03Rules.fs
purity audit test/purity.test.js test/Purity.test.scala (PuritySuite) tests/Purity.fs
build for the browser nothing to build ./build-web.sh (Scala.js, writes web/dist/) ./build-web.sh (Fable, writes web/dist/)
play node scripts/serve.mjs from the repository root, then http://localhost:8000/js/ (Pong: /js/pong.html) same server, http://localhost:8000/scala/ same server, http://localhost:8000/fsharp/

See also: Module 0 for setup, the method for the conversion recipe, the README for the module map, and MENTOR.md for AI-mentor mode.