docs(course): replace Scala bridges with general programming in 00–05, README, mentor, spec

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Lucas Winther
2026-10-29 01:57:10 +01:00
co-authored by Claude Opus 5.5
parent 9891bb596d
commit d8828e1b6a
11 changed files with 109 additions and 54 deletions
+7 -7
View File
@@ -1,7 +1,7 @@
# Module 0 — Setup and how to use this course
> **Time:** ~1 h · **You'll edit:** nothing yet · **Tests:** JS `cd js && node --test "test/m00-*.test.js"` · Scala `cd scala && ./test.sh M00` · F# `cd fsharp && ./test.sh M00`
> **Prerequisites:** one course in Scala (or similar): you know `map`, `filter`, `fold`, case classes and pattern matching
> **Prerequisites:** general programming experience: variables, loops, functions, classes and objects, collections, callbacks or event handlers, and some unit tests. No prior functional programming is assumed.
## Why this module
@@ -81,7 +81,7 @@ Here is the whole program you are going to build. Keep coming back to this pictu
Read it from the top. The **pure core** is plain data and plain functions: given a state and an action, `update` computes the next state; given a state, `view` computes a list of shapes. Below that, **effects as values**: an `IO` is a *description* of something to do (like a recipe), and a `Stream` is a *description* of how to react to inputs over time. Building these descriptions changes nothing in the world. Finally, at **the edge**, a small runtime takes the one value `main` and actually runs it.
> **From Scala:** if you have heard of cats-effect or ZIO, this is the same idea in miniature. You will build `IO` and `Stream` yourself, from scratch, in about a hundred lines each, so there is nothing magic left.
> **From everyday programming:** you know the classic game loop, `while (running) { input(); update(); render(); }`, where each of the three calls reads and changes shared variables. This course keeps the same three parts but separates them: `update` and `render` become pure functions that return a new state and a picture, and only the loop itself, at the edge, touches the keyboard, the clock and the screen. You will build `IO` and `Stream` yourself, from scratch, in about a hundred lines each, so there is nothing magic left.
This design is sometimes called *functional core, imperative shell* (Gary Bernhardt). We push it further than usual: even the shell is mostly pure, and the truly imperative part is one function.
@@ -92,14 +92,14 @@ The course text is shared. Every code listing that you would write appears three
| Track | Why pick it | Tests with | Runs in the browser via |
|---|---|---|---|
| **JavaScript** (primary) | No compiler, no types: you must hold the discipline yourself, which teaches the most. Zero dependencies. | Node's built-in `node --test` | plain ES modules |
| **Scala 3** | Closest to what you already know. The compiler helps you (exhaustive matches, immutable by default). | munit via `scala-cli` | Scala.js |
| **Scala 3** | A typed language that mixes objects and functions; familiar if you know Java or Kotlin. The compiler helps you (exhaustive matches, immutable collections by default). | munit via `scala-cli` | Scala.js |
| **F#** | An ML-family language: lighter syntax, data-last functions and pipelines. A good second functional language. | Expecto via `dotnet` | Fable |
If you are unsure, take JavaScript. It is the primary track, and Module 1 bridges from Scala to JavaScript. The three tracks use the same names where they can. Where they differ, the text tells you the first time it matters, and `course/reference.md` has the full table. The most common differences:
If you are unsure, take JavaScript. It is the primary track, and Module 1 introduces the functional tools (`map`, `filter`, `reduce`, immutable updates, functions as values) in JavaScript first, with the Scala and F# spellings next to them. The three tracks use the same names where they can. Where they differ, the text tells you the first time it matters, and `course/reference.md` has the full table. The most common differences:
- JS functions take the data first: `map(io, f)`. Scala uses methods: `io.map(f)`. F# takes the data last so that pipelines work: `io |> IO.map f`.
- `flatMap` is called `bind` in F#.
- "No apple" is `null` in JS and `None` (an `Option`) in Scala and F#.
- "No apple" is `null` in JS and `None` (an `Option`, a value that is either `Some(x)` or `None`; Module 2 explains it) in Scala and F#.
- Random seeds are `number` in JS, `Long` in Scala, `uint32` in F#.
### Installing the tools
@@ -238,7 +238,7 @@ A green test does not prove your code is right, and a red test never means you a
> - `Math.random`, `Date.now`, `System.Random`, `DateTime`: hidden inputs. The same call gives a different answer each time.
> - `document`, `window`, `addEventListener`, `println`, `printf`: talking to the outside world.
>
> Without `let` or `var` you cannot write a classic `for (let i = 0; ...)` loop. That is deliberate: you will use `map`, `filter`, `reduce`, recursion and small helper functions instead, which is what you know from Scala.
> Without `let` or `var` you cannot write a classic `for (let i = 0; ...)` loop. That is deliberate: you will use `map`, `filter`, `reduce`, recursion and small helper functions instead. If you have not used them before, Module 1 introduces each of them with small examples.
>
> The audit is a blunt text scan. It can be fooled, and it sometimes complains about harmless code (for instance a string containing `--`). It is a guard rail, not a proof. The real rule is the one behind it: **outside the edge, a function's result depends only on its arguments, and calling it changes nothing.**
@@ -269,7 +269,7 @@ Every module has the same parts, in this order:
1. **Why this module**: the problem, usually what goes wrong in the stateful version.
2. **Learning objectives**: what you should be able to do afterwards.
3. **Concepts**: the ideas, with Scala bridges (blockquotes starting with **From Scala:**) and code in all three languages.
3. **Concepts**: the ideas, with bridges from programming you already know (blockquotes starting with **From everyday programming:**) and code in all three languages.
4. **Exercises**: the functions to write, with signatures in three languages, the tests and fixtures that check them, and pitfalls.
5. **Hints**: folded away; from Module 1 on, every coding exercise has a nudge, a stronger hint and a near-solution. Open them one tier at a time.
6. **Check your understanding**: questions you should be able to answer before moving on. Several ask you to predict an output or to say what would break if something changed.