docs(course): replace Scala bridges with general programming in 00–05, README, mentor, spec
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
9891bb596d
commit
d8828e1b6a
11 files changed
+109
-54
No files matched your search
@@ -45,7 +45,9 @@ update : (GameState, Action) -> GameState
|
||||
|
||||
This is the most important function signature in the course. It has a name in several communities: Redux calls it a **reducer**, Elm calls it `update`, and in automata theory it is the **transition function** of a state machine. Everything the game can ever do is described by this one function and the start state.
|
||||
|
||||
> **From Scala:** dispatching on a sum type is a `match`. In Scala, `case Action.Turn(dir) => ...` extracts the direction; in F#, `| Turn dir -> ...`; in JS, `switch (action.tag)` and then read `action.dir`. For now a final catch-all case (`case _`, `| _ ->`, `default:`) handles the actions that do nothing. In Module 10 you will replace it by explicit cases, so that the Scala and F# compilers warn you if you ever forget one.
|
||||
> **From everyday programming:** if you have written an event handler that does `switch (event.type)` with one case per kind of event, or a Redux reducer, you have written this dispatch. The difference here is that each case *returns* the next state instead of changing fields.
|
||||
|
||||
Dispatching on a sum type is pattern matching (Module 2). In Scala, `case Action.Turn(dir) => ...` extracts the direction; in F#, `| Turn dir -> ...`; in JS, `switch (action.tag)` and then read `action.dir`. For now a final catch-all case (`case _`, `| _ ->`, `default:`) handles the actions that do nothing. In Module 10 you will replace it by explicit cases, so that the Scala and F# compilers warn you if you ever forget one.
|
||||
|
||||
### A game is a fold
|
||||
|
||||
@@ -101,6 +103,8 @@ Note the argument orders, which trip people up: JS `reduce(step, initial)` is ca
|
||||
|
||||
A game of Snake is, literally, `actions.reduce(update, start)`. Time and input have become a list.
|
||||
|
||||
> **From everyday programming:** databases rely on the same idea. A database writes every change to a log (the *write-ahead log*) before it applies it; after a crash, it rebuilds its state by replaying the log over the last saved snapshot. A bank works the same way: your balance is a fold over your transactions, which is why your statement lists the transactions and not only the balance.
|
||||
|
||||
### History is a scan
|
||||
|
||||
A fold gives you only the final state. Sometimes you want every state along the way: to draw a replay frame by frame, to step backwards through a game in a debugger, or to find the first state where something went wrong. The operation that keeps every intermediate accumulator is called a **scan**:
|
||||
@@ -127,7 +131,7 @@ txs.scanLeft(100)(apply) // List(100, 150, 130, 135)
|
||||
List.scan apply 100 txs // [100; 150; 130; 135]
|
||||
```
|
||||
|
||||
> **From Scala:** `scanLeft` is in the standard library of Scala and F#. In JavaScript you can build a scan out of `reduce`, by making the accumulator the *list of states so far* instead of the latest state. Think about what the step function then has to do. (Building a new array on every step with `[...acc, next]` costs O(n) per step and O(n²) overall. For games of a few thousand steps that does not matter; persistent lists, as in Scala and F#, avoid it.)
|
||||
`scanLeft` is in the standard library of Scala, and `List.scan` in F#'s. In JavaScript you can build a scan out of `reduce`, by making the accumulator the *list of states so far* instead of the latest state. Think about what the step function then has to do. (Building a new array on every step with `[...acc, next]` costs O(n) per step and O(n²) overall. For games of a few thousand steps that does not matter; persistent lists, as in Scala and F#, avoid it.)
|
||||
|
||||
### What the fold gives you for free
|
||||
|
||||
|
||||
Reference in new issue
Block a user