fix: final review findings
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
82f47fb3ee
commit
b46a3611af
@@ -5,6 +5,10 @@ obj/
|
||||
.scala-build/
|
||||
.bsp/
|
||||
*/web/dist/
|
||||
.bloop/
|
||||
.metals/
|
||||
.idea/
|
||||
.vscode/
|
||||
|
||||
# build outputs
|
||||
.fable/
|
||||
|
||||
@@ -44,9 +44,9 @@ To play the game in any track you also need a modern browser. [Module 0](course/
|
||||
Run the tests (from the repository root):
|
||||
|
||||
```sh
|
||||
cd js && node --test "test/m03-*.test.js" # JavaScript, one module (all: ./test.sh)
|
||||
cd scala && ./test.sh M03 # Scala, one module (all: ./test.sh)
|
||||
cd fsharp && ./test.sh M03 # F#, one module (all: ./test.sh)
|
||||
(cd js && node --test "test/m03-*.test.js") # JavaScript, one module (all: ./test.sh)
|
||||
(cd scala && ./test.sh M03) # Scala, one module (all: ./test.sh)
|
||||
(cd fsharp && ./test.sh M03) # F#, one module (all: ./test.sh)
|
||||
```
|
||||
|
||||
In a fresh checkout, Module 0's tests pass and every other module's tests fail: the files in `src/` are stubs for you to fill in.
|
||||
|
||||
@@ -189,6 +189,8 @@ cd fsharp
|
||||
./test.sh # everything
|
||||
```
|
||||
|
||||
If `scala-cli` prints a hint that the munit dependency is outdated, ignore it: the course pins munit on purpose, for reproducibility.
|
||||
|
||||
Try it now with Module 0, which has a few sanity tests that already pass. They check that your toolchain works and that the fixtures folder can be read. If you see three green tests (Scala and F#: a short summary with no failures), you are set up.
|
||||
|
||||
### What a red test means
|
||||
|
||||
@@ -195,7 +195,7 @@ Our `view : GameState → Scene` is exactly this, for a canvas instead of HTML.
|
||||
|
||||
## Exercises
|
||||
|
||||
> **About test names.** The test titles quoted under "Proved by" are the JavaScript ones. The Scala and F# suites test the same things one-to-one, but their titles use each track's vocabulary: "events" instead of "inputs", `None` instead of `null`, `()` instead of `undefined`, `bind`/`choose` instead of `flatMap`/`filterMap`, `IO.map throwingF io` instead of `map(io, throwingF)`, and so on. Failing tests are reported by name when you run the module's tests; to read all titles, open `scala/test/M06View.test.scala` or `fsharp/tests/M06View.fs`. Fixture-based tests have the same case names in every track.
|
||||
> **About test names.** The test titles quoted under "Proved by" are the JavaScript ones, which carry an `m06` prefix (e.g. `m06 CELL is 20 pixels`). The Scala and F# suites test the same things one-to-one, without that prefix: `CELL is 20 pixels` is identical, and the fixture-driven cases appear as `view: <case name>` with the same case names in every track. Failing tests are reported by name when you run the module's tests; to read all titles, open `scala/test/M06View.test.scala` or `fsharp/tests/M06View.fs`.
|
||||
|
||||
### Exercise 6.1 — `view`
|
||||
|
||||
|
||||
@@ -229,11 +229,11 @@ Remember section 12 of Module 7: the loop only helps if each continuation does a
|
||||
That's the whole runtime. Go back to the stateful code at the start of Module 8, with its `let state` and logic in callbacks, and compare. There, the mutable variable held the *game state*, and game logic lived in the callbacks. Here, the variable holds the *current stream* — the runtime doesn't even know there's a game. It has no idea what a snake, a tick or a score is. All the game logic is in pure values; the runtime is a few lines of plumbing. That is what "pushing effects to the edge" means in practice.
|
||||
|
||||
```
|
||||
┌──────────────────────── runtime (impure, ~20 lines) ─────────────────────────┐
|
||||
browser ──key──▶│ feed(KeyDown key): [ios, next] = stream.step(ev); stream = next; ios.forEach(runIO) │──▶ canvas
|
||||
──time─▶│ feed(Time t) │
|
||||
└───────────────────────────────────────────────────────────────────────────────┘
|
||||
▲ the one `let` / `var` / ref cell: stream
|
||||
┌────────────────────────────── runtime (impure, ~20 lines) ─────────────────────────────────┐
|
||||
browser ──key──▶│ feed(KeyDown key): [ios, next] = stream.step(ev); stream = next; ios.forEach(runIO) │
|
||||
──time─▶│ feed(Time t) │
|
||||
└────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
▲ the one `let` / `var` / ref cell: stream
|
||||
```
|
||||
|
||||
The tests describe the behaviour precisely; read them before writing the code:
|
||||
|
||||
@@ -29,7 +29,13 @@ let private violations (code: string) =
|
||||
|
||||
let private fsFiles () =
|
||||
if Directory.Exists srcDir then
|
||||
Directory.GetFiles(srcDir, "*.fs", SearchOption.AllDirectories) |> Array.sort |> List.ofArray
|
||||
Directory.GetFiles(srcDir, "*.fs", SearchOption.AllDirectories)
|
||||
|> Array.filter (fun f ->
|
||||
// skip generated output: any path component named bin or obj
|
||||
let rel = Path.GetRelativePath(srcDir, f)
|
||||
not (rel.Split([| '/'; '\\' |]) |> Array.exists (fun d -> d = "bin" || d = "obj")))
|
||||
|> Array.sort
|
||||
|> List.ofArray
|
||||
else
|
||||
[]
|
||||
|
||||
|
||||
+1
-1
@@ -27,4 +27,4 @@ createServer(async (req, res) => {
|
||||
res.writeHead(e.code === 'EACCES' ? 403 : 404, { 'content-type': 'text/plain' })
|
||||
res.end(e.code === 'EACCES' ? 'forbidden' : 'not found')
|
||||
}
|
||||
}).listen(port, () => console.log(`Serving ${root} on http://localhost:${port}`))
|
||||
}).listen(port, '127.0.0.1', () => console.log(`Serving ${root} on http://localhost:${port}`))
|
||||
|
||||
Reference in New Issue
Block a user