refactor(book): restructure chapters, extract Proof.lean, and fix the generated project - #23
Conversation
The order becomes IntroCS, IntroL, Logic, Sets, SeaBattle, Morphology,
InfEngine, English. Two chapters move and one is created by promotion.
`SeaBattle` moves after `Logic` and `Sets` because its exercises prove
theorems about `WellFormed` by induction on an inductive predicate, which
needs tactics `Logic` presents. `Morphology` moves after `SeaBattle` so its
exercises may use those tactics too, instead of being confined to what
`IntroL` alone allows; it depends on nothing but `IntroL`, so the move is
free.
Mastermind is dropped. It was disconnected from the Sea Battle material
before it, and it announced a semantics in propositional logic that it never
gave — the propositional content was about eighteen lines the implementation
never used. Reinstating it would mean writing that encoding, not translating
it. Two exercises go with it, `four-turn-game` and `chess-grammar`.
With Mastermind gone `Games.lean` wrapped a single section, so `SeaBattle`
becomes the chapter: `CSwL/Games/SeaBattle.lean` moves to
`CSwL/SeaBattle.lean` and the glue file is deleted. The `Games` tag goes with
it — `IntroL`'s `{ref "Games"}` becomes `{ref "SeaBattle"}`.
Prose that named positions rather than chapters is corrected, as
STYLE-WRITING.md requires after a move: `Morphology`'s opening no longer
says "o capítulo anterior", and a stale comment in `Logic.lean` claiming
English precedes it is fixed.
Documentation that indexes by chapter order is updated: DEVIATIONS.md's
table, dependency paragraph and per-chapter sections (reordered and
renumbered); PROVENANCE.md's paths and dropped-exercise records;
README.md's chapter list; and STYLE-CODE.md's first-use ledger.
Re-deriving that ledger turned up something the original sweep missed:
`native_decide` is used eleven times in `SeaBattle.lean`, in ordinary code
rather than in solutions, so students see it. It is presented nowhere. The
known-gaps entry is widened accordingly; the fix belongs to #7.
Refs #3, #12.
Issues #9 and #12. The proof material was scattered: `IntroL` presented `Prop`, the tactic table and induction; `PL` and `FOL` each re-presented `Prop` before getting to their own subject. Four blocks now move verbatim into a new `CSwL/Logic/Proof.lean`, which `Logic.lean` includes first: IntroL:300-623 "O tipo Prop e Provas" IntroL:724-748 "Prova por indução" PL:91-368 "Lógica Proposicional em Lean" FOL:44-126 "As regras dos quantificadores em Lean" `IntroL` is now deliberately pre-proof: it uses `example`, `theorem` and `rfl` only as the shape an exercise's tests take, and new prose says so and forwards to `Proof` for what proving means. `PL` and `FOL` keep syntax as a data type, computable semantics, and the bridge to `Prop`. `PL`'s `variable (p q : Prop)` was leaking past the "Sintaxe" section and shadowing the `valuation-table` exercise's own `p`, `q`, `r`; it is now scoped. This only surfaced in the variant builds, not in `lake build`. `STYLE-CODE.md`'s ledger is re-derived: the `Logic` row becomes three, every proof tactic moves from `IntroL` to `Logic/Proof`, and `show`, `omega` and `decide` are recorded under "Known gaps" — they survive only inside the `twice` exercise's solutions, where nothing presents them. A `:::dev` note in `Sets.lean` records that `Sets.Entity` and `FOL.Entity` are distinct types sharing a name, accepted deliberately. This commit was prepared with Claude Code. The AI performed the verbatim block moves, re-derived the feature ledger, and drafted the new Portuguese prose in `IntroL.lean` and `Logic/Proof.lean`; that prose is a draft awaiting human review.
A reading pass over the chapter that the two preceding commits rearranged, checking the text against what the code now does rather than against what it did before the restructuring. FOL gains a computable `Formula.eval` and the bridge section that PL already had, so the chapter now has the same three-part shape as PL: syntax as data, computable semantics, and the theorem relating the two readings. PL's semantics section was rewritten around that same shape, and Proof was condensed. Fix five cross-references that resolved to nothing. Record in `PROVENANCE.md` that CSwFP/5.19 and 5.20 became portable only with this reorganization, since they need a semantics to be stated against, and update the `STYLE-CODE.md` ledger for the features that moved with the text: `List.all`/`List.any` to `IntroL`, `deriving BEq` from `Morphology` to `FOL`, and `List.contains` and `induction … generalizing` newly used in `FOL`. Add Nederpelt & Geuvers (2014) to the bibliography, cited where `Proof.lean` explains that Lean's foundation makes types and programs computable.
The two-level table of contents was self-imposed. `htmlSplit := .never` means
"do not split here nor in any part below", so a glue chapter carrying it keeps
its sections on one page however long they grow. Dropping it from `Logic.lean`
lets Verso split at the depth it already defaults to (`htmlDepth := 2`), which
is what fp-lean runs.
Each section declares `file := "Tag"`, without which Verso builds the URL by
sluggifying the Portuguese title and the accents come out mangled
(`L___gica-proposicional`).
_out/<variant>/html/Logic/{index,Proof,PL,FOL}/
The chapter index is now a 63 KB landing page instead of carrying all three
sections, and the four variants render with no unresolved cross-references.
The generated Lean is untouched, verified byte-identical against a snapshot
taken before the change: `walkOuter` writes one file per chapter and reads
`file :=` only from chapters, while `walkSection` takes its target file as a
parameter and never consults metadata. Splitting that output by section is the
other half of the issue and is left for its own commit, since it has to invert
`chapterImports`, which currently flattens a section's imports into its
chapter on purpose.
Refs #11
AI-usage disclosure: this change was developed with the assistance of Claude
Opus 5 (Anthropic, September 2026, via Claude Code). The author decided to
take up the issue and reviewed the result; the assistant made the edits,
checked the claims the issue made against Verso's source and the extractor's,
and verified the generated Lean was unchanged. The author takes full
responsibility for the final content.
`file := "introL"` renamed the generated chapter to `CSwL/introL.lean`, and `import CSwL.IntroL` then stopped reaching the extracted project: the generated `Morphology.lean` lost it and failed with `Unknown identifier IntroL.initS`, plus every test downstream of `swedishPlural`. `chapterModules` is built from the *generated* file names, so it held `CSwL.introL` while the source header says `CSwL.IntroL`. The two no longer matched, `isSection` (`CSwLMeta/Save/Project.lean`) concluded the import named a section whose content had been merged into its chapter, and dropped it — the one disposal path that does not reach the `reportError` for an unresolvable import a few lines below. A `file :=` that does not match the module name therefore removes an import in silence. Introduced in 57a6702, found while building the generated project for #22. Refs #22 AI-usage disclosure: this change was developed with the assistance of Claude Opus 5 (Anthropic, September 2026, via Claude Code). The assistant traced the missing import to the case mismatch and confirmed it by regenerating with the name restored; the author reviewed the result. The author takes full responsibility for the final content.
`gapShip` is a well-formed definition that happened to share a ```lean +error fence with `badState`, the definition the fence is there to reject. In the book both elaborate in place and nothing is wrong. In the extracted project the fence becomes an indented `sf_expect_failure` block, so `gapShip` was defined *inside* it and the three examples below, at file scope, could not see it — reported as `Expected type must not contain free variables` from the `native_decide` that needed it. It now has a fence of its own, immediately before, and the `+error` fence holds only what is meant to fail. The issue attributed this to `solution!(by …)` emitting `(by …)`. That is not the cause: removing the parentheses by hand leaves the error exactly where it was, and the parenthesised form elaborates fine. Refs #22 AI-usage disclosure: this change was developed with the assistance of Claude Opus 5 (Anthropic, September 2026, via Claude Code). The assistant tested the parenthesis hypothesis the issue proposed, found it did not hold, and located the scoping cause; the author reviewed the result. The author takes full responsibility for the final content.
Three solutions were written in a shape that only survives while the marker is there. The `solutions` variant strips the marker in place, and what was left did not parse or did not prove. `Sets.lean`, `ex_3_12`: a structure instance opened on the same line as `solution!(`, and its remaining fields were aligned against a column that the marker created. Removing `solution!` shortens the first line by nine characters, the fields no longer line up under `refl`, and the instance fails to parse. The body now starts on the line below, indented, so the alignment is its own. `Proof.lean`, three tactic blocks: written `solution!( … )` rather than as an indented block. As a tactic the marker is replaced by `all_goals`, so the block has to be applicable to a single goal. Two were merely parenthesised and became indented. In `vestidos₁` the shape was wrong: `rcases` ran outside the marker and the three bullets inside it, so `all_goals` applied all three to each of the three goals it left open. The `rcases` moves inside, and the block opens and closes the case split itself. `STYLE-CODE.md` now states both rules, which it did not: a tactic solution takes the indented form and must suit a single goal, and a multi-line term begins on the line after `solution!(`. The emitter is not at fault and is unchanged: `solution!` is used in both forms in sf-in-lean, and a parenthesised *term* — including a `by` block — is what it is for. Refs #22 AI-usage disclosure: this change was developed with the assistance of Claude Opus 5 (Anthropic, September 2026, via Claude Code). The author judged the fault to lie in the book's use of `solution!` rather than in the macro and pointed at sf-in-lean for the idiom; the assistant located the three occurrences, found the `all_goals` constraint behind the `vestidos₁` failure, and wrote the style-guide entry. The author takes full responsibility for the final content.
`buildProject` has been there since the saver was written, behind a `verify` flag that defaulted to `false` and that nothing ever set. The three defects fixed in this branch all had the same shape — the book compiled, the project handed to the student did not — and none of them was visible to `lake build` at the repository root, where a solution is elaborated in place, marker and all. Only the extracted source shows what the student receives. `verify := true` for `solutions` alone. It is the one variant whose proofs are all meant to be complete, so "it compiles" both means something and is now true. `student` and `terse` replace answers with `sorry` and fail by design; verifying them would report expected failures as errors. `grading` is the same Lean with the autograder's attributes over it. The two reasons recorded for keeping this off no longer hold, and the comment now says what was measured instead of what was assumed. The toolchain mismatch is gone: the generated project and `CSwL` are both on `v4.33.0`. And Mathlib is not compiled "from scratch on every `make`" — the generated project vendors its own, 7.5 GB, but once built an incremental `lake build` takes about three seconds. The cost that remains is disk, and a `make clean` that pays for it again. Checked by reintroducing the `ex_3_12` defect: `make solutions` fails, exit 2, naming the file and line inside the generated project. Closes #22 AI-usage disclosure: this change was developed with the assistance of Claude Opus 5 (Anthropic, September 2026, via Claude Code). The author decided to turn the check on; the assistant measured the two recorded objections, found both stale, limited the flag to `solutions`, and verified that a failing generated project fails `make`. The author takes full responsibility for the final content.
The extracted project mirrored the book's chapters but not its files: three
section files of 441, 599 and 772 lines arrived merged into one 1819-line
`CSwL/Logic.lean`, while their sources sit in `CSwL/Logic/`. The output now
mirrors the source — a `PL.lean` in the book is a `PL.lean` in the project —
and the chapter becomes a glue module importing its sections.
The criterion is `file :=`. It cannot be read off the document: after
`{include 1 …}` a section that came from its own file and one written inline
in the chapter are the same `Part`. The key is already what names the HTML
page, and only the three sections that should become files carry it, so
`isOwnModule` asks for it and the inline sections (`pl-syntax`,
`pl-semantics`, …) stay merged as before. Nothing changes for a chapter whose
sections set no `file :=` — `Morphology` is untouched until someone adds the
key.
`chapterImports` did not need inverting, only documenting: it keys on
`chapterModules`, which is built from the generated file names, so a section
that now has a module of its own is no longer taken for a merged one and its
`import` survives as written. Each section keeps the imports its own header
declares — `Mathlib.Tactic.ByContra` lands in `PL.lean`, `Mathlib.Tactic.Use`
in `FOL.lean` — which is what 0b137cb had to work around while the output was
flat.
The silent drop that 0b137cb worked around and that 47e435a ran into is now
an error. An `import` that resolves to a chapter the project emits under
another name — `CSwL.IntroL` against a chapter emitted as `introL.lean` — used
to be taken for a merged section and dissolved, and the failure surfaced three
chapters away as `Unknown identifier IntroL.initS`. Extraction now stops and
names the file and the cause.
Verified: the split content is identical to the previous single file except
that each section's headings rise one level, as a file's own headings should;
all four variants emit the same eleven modules; and the generated `solutions`
project still builds, which is the check #22 turned on.
Refs #11
AI-usage disclosure: this change was developed with the assistance of Claude
Opus 5 (Anthropic, September 2026, via Claude Code). The author set the
principle that the Lean output should mirror the source's file organisation;
the assistant chose `file :=` as the criterion, wrote the change, and verified
the split against the previous output. The author takes full responsibility
for the final content.
…se it `CSwLCompat` backs the `sf_experiment` and `sf_expect_failure` macros that a ```lean +error fence becomes in the extracted project. Only `SeaBattle.lean` and `Logic/PL.lean` have such a fence; `Logic.lean` and `Logic/Proof.lean` imported the module without using it, and the extracted project carried the import into the student's files. The module itself is still needed, in every variant including `student`: the two fences that remain are pedagogical — `badState`, which must fail to typecheck, and `doesNotWork`, which shows that a proof of a `Prop` cannot be pattern-matched — so the blocks belong in the student's project and the project does not build without the macro that carries them. AI-usage disclosure: this change was developed with the assistance of Claude Opus 5 (Anthropic, September 2026, via Claude Code). The author asked whether the module was needed in the student output; the assistant established that it is, found the two imports that are not, and removed them. The author takes full responsibility for the final content.
…order The ledger is indexed by book order, and the reorganisation moved nearly every row. It had been patched by hand as chapters moved; rescanning the sources found five rows that no longer said where a feature is first used. - `native_decide` — `Logic/PL`, not `SeaBattle`. It is named in the prose of three exercises there (`:282`, `:308`, `:348`), two chapters before the row claimed. This is the worst of the five: the tactic trusts the compiler rather than the kernel, and "Known gaps" now records that `Logic/PL` is where the explanation it still lacks belongs. - `assumption` — `Logic/Proof`, not `Sets`. It is presented there in prose (`:172`) and used at `:182`, so this row was not a gap, only misplaced. - `DecidableEq` — `IntroL`, not `Logic/PL`. Presented twice, at `:327` with `deriving` and at `:862` alongside `BEq`. - `trivial` — stays in `Sets`, but it is a term there (`:517`), not a tactic, and moves to the types column. - `funext` — solution only, like the `show`/`omega`/`decide` beside it: its only use is inside the `twice` exercise's answer. Stale line numbers in "Known gaps" corrected (`Sets.lean:507` → `:517`, the `twice` exercise's `:1154-1158` → `:1136-1141`). "How to read the table" gains the scanning method, since the naive one is what let these drift: a search that ignores ```` ```lean ```` fences reports Portuguese as Lean — "trivialmente" matches `trivial` — and a tactic named in a sentence is a mention, not a use. Uses are found inside fences with comments stripped, presentations in the prose, and the rule holds only when a presentation precedes the first use in book order. Closes #10 AI-usage disclosure: this change was developed with the assistance of Claude Opus 5 (Anthropic, September 2026, via Claude Code). The assistant wrote the fence-aware scan, checked each disagreement between it and the table against the sources, and distinguished the real errors from matches on Portuguese prose; the author reviewed the result. The author takes full responsibility for the final content.
There was a problem hiding this comment.
🟡 Changes recommended
Unresolved critical build, extraction, and semantic issues remain, along with moderate correctness issues.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
This PR restructures the book, consolidates proof material under Logic, adds computable FOL semantics, and improves generated-project extraction.
Changes:
- Reorders chapters, promotes SeaBattle, removes Games/Mastermind, and narrows IntroL.
- Adds
Logic/Proof.leanand splits generated Logic modules. - Updates extraction validation, documentation, and project verification.
File summaries
| File | Summary and final comments |
|---|---|
STYLE-CODE.md |
Updates the feature ledger and extraction guidance. Three nits: inaccurate Proof description (2 votes), omitted linarith (1), and omitted Proof from the split example (1). |
README.md |
Updates project organization. One nit (1): document Logic/Proof.lean. |
PROVENANCE.md |
Updates provenance inventory. One nit (3): regenerate the incorrect count and stale identifiers/paths. |
DEVIATIONS.md |
Documents restructuring. Four nits (2, 3, 1, 1): stale SeaBattle, IntroL, Morphology, and migration descriptions. |
CSwLMeta/Save/Project.lean |
Adds generated-project validation and import checks; no final comments. |
CSwLMeta/Save/Extract.lean |
Adds section-level generated modules; no final comments. |
CSwL/Sets.lean |
Updates exercise extraction; no final comments. |
CSwL/SeaBattle.lean |
Promotes SeaBattle and adjusts extraction scope. One critical comment (1): exposed examples still use incompatible term-form solution markers. |
CSwL/Morphology.lean |
Updates chapter integration; no final comments. |
CSwL/Logic/Proof.lean |
Consolidates proof material. Two critical comments (1 each), one moderate (3), and four nits (2, 1, 1, 1): solution-marker extraction failures, an unproved theorem, spelling errors, and an untracked linarith example. |
CSwL/Logic/PL.lean |
Refactors propositional semantics. Three critical comments (3, 3, 1), two moderate (3, 2), and four nits (1 each): invalid Boolean operators/types, valuation pairing, insufficient testing, and solution-marker formatting. |
CSwL/Logic/FOL.lean |
Adds computable FOL semantics. One nit (1): align the model introduction with the predicates actually used. |
CSwL/Logic.lean |
Provides Logic glue; no final comments. |
CSwL/IntroL.lean |
Narrows IntroL to programming-language material; no final comments. |
CSwL/Games/Mastermind.lean |
Removes Mastermind material; no final comments. |
CSwL/Games.lean |
Removes obsolete Games glue; no final comments. |
Book.lean |
Applies the revised chapter order; no final comments. |
Bib.lean |
Adds supporting references; no final comments. |
Review details
Suppressed comments (17)
CSwL/Logic/FOL.lean:396
- The model introduced below is not limited to
P,R, andS:int0interprets eight named predicates (Girl,Boy,Princess,Dwarf,Giant,Child,Love, andDefeat). As written, the semantic setup describes a different language from the one the examples evaluate; align this introduction with the concrete model.
Por conveniência, nos limitamos a um fragmento de língua com apenas três letras de predicado: `P` (unário), `R` (binário), e `S` (ternário).
Como deve ser uma estrutura extralinguística para as constantes `P`, `R` e `S`? Tal estrutura deve conter ao menos um domínio de discurso `D`, formado por entidades individuais, com uma interpretação para `P`, para `R` e para `S`. Essas interpretações são dadas por uma função `Interp`, que a cada nome de predicado e a cada lista de elementos do domínio associa um valor de verdade.
CSwL/Logic/PL.lean:354
- This proof uses the parenthesized
by solution!(native_decide)shape instead of the standalone indented tactic form required bySTYLE-CODE.md:184-188. Use an indentedsolution!block so the source follows the extraction contract consistently.
example : Form.impliesL [depo1, depo2, depo3] banguSolution = true :=
by solution!(native_decide)
CSwL/Logic/PL.lean:247
- These proofs use the parenthesized
by solution!(decide)shape instead of the standalone indented tactic form required bySTYLE-CODE.md:184-188. Use an indentedsolution!block so the source follows the extraction contract consistently.
example : form3.eval vs1 = true := by solution!(decide)
example : form4.eval vs1 = true := by solution!(decide)
example : form5.eval vs2 = true := by solution!(decide)
CSwL/Logic/PL.lean:289
- This marker is also used in the parenthesized form inside a tactic proof.
STYLE-CODE.md:184-188requires tactic answers to use standalone indentation; change the marker to an indented block rather than relying on the term-shaped form.
solution!(native_decide)
CSwL/Logic/PL.lean:318
- This
native_decideproof usessolution!(…)as a tactic block, but the repository's extraction convention requires the standalone indented tactic form. Put the marker on its own line and indentnative_decidebeneath it.
solution!(native_decide)
CSwL/Logic/Proof.lean:171
- Correct the spelling of
taticatotática.
A regra de introdução de `→` diz que para provar `P → Q`, supomos `P` e derivamos `Q`. A tatica `intro` move o antecedente para as hipóteses. A regra de eliminação é a chamada regra *modus ponens*. De `P → Q` e de `P`, conclua `Q`. Em Lean isso é aplicação `h hP` já é a prova de `Q`. A tática `apply` faz o mesmo de trás para frente, ela transforma o objetivo `Q` no objetivo `P`. A {tactic}`exact` fecha a prova indicando a hipótese cujo tipo corresponde ao _goal_ aberto. A {tactic}`assumption` fecha o _goal_ quando o tipo de alguma das hipóteses corresponde ao tipo do _goal_, sem precisarmos passar a hipótese nominalmente, como quando usamos {tactic}`exact`.
CSwL/Logic/Proof.lean:57
- Correct the spelling of
identificastoidênticas.
A tática {tactic}`rfl` tem limitações, embora possamos provar que duas funções são identificas a menos da sua mudança nos nomes dos parâmetros, precisamos do teorema sobre a comutatividade dos naturais para provar o segundo exemplo.
CSwL/Logic/Proof.lean:354
- This tactic solution is still written as
solution!(…); after marker stripping it is not the required standalone indented tactic block, so the generatedsolutionssource can fail even though the book source elaborates with the marker. Use the standalone tactic form.
solution!(
intro hp
apply h2
apply h
exact hp)
CSwL/Logic/Proof.lean:532
- This newly added non-solution example uses
linarith, but theLogic/Proofrow inSTYLE-CODE.mddoes not record it and the recap below lists other tactics instead. Either record its presentation/first use in the ledger or replace this example with a tactic already accounted for, so the feature ledger remains accurate.
linarith
CSwL/Logic/Proof.lean:368
- This is another tactic solution written as
solution!(…)afterby, contrary to the standalone indented form required by the saver. In the extracted solutions it does not have the intendedall_goalsblock shape; movesolution!to its own line and indent all tactics beneath it.
solution!(
intro h
unfold E at h
linarith)
CSwL/Logic/Proof.lean:381
- This proof has the same parenthesized tactic-marker shape. Keep the entire tactic sequence inside one standalone indented
solution!block so the generated solution replaces the marker withall_goalswithout changing the block's structure.
solution!(
intro h
unfold E at h
obtain ⟨h1, h2⟩ := h
rewrite [h1,h2]
CSwL/SeaBattle.lean:302
- Closing the
+errorblock here exposes the following exercise examples to the extracted solutions project, but those examples still use term-formsolution!(by native_decide). The teacher rewrite removes onlysolution!, producing parenthesized tactic terms such as(by native_decide), which is the generated-project failure reported foraddShip; rewrite these examples using the indented tactic form (or fix the term-marker emitter) before enabling verification.
DEVIATIONS.md:92 - The dependency rationale says all Morphology proofs are
rfl, butCSwL/Morphology/SwedishPlural.lean:69and:72still usenative_decide. This makes the chapter-order documentation false and hides the same known ledger gap this PR records; qualify the statement with those two exceptions.
`Morphology.lean` requires only `IntroL.lean` — its three sections are programs, and the proofs in them are `rfl` on concrete values. It is placed after `SeaBattle.lean` rather than at its old position right after `IntroL.lean` so that its exercises may use the tactics `Logic.lean` presents, rather than being confined to what `IntroL.lean` alone allows.
DEVIATIONS.md:335
- This migration note is stale after moving Morphology after
Logic: it says every feature must have been presented inIntroL, while the surrounding rationale explicitly allows the chapter to use tactics fromLogicand the sources include later/solution-only features. Point the check at the actual ledger/order instead.
*Migration*: check that every Lean feature used here was presented in `IntroL.lean`.
README.md:72
- The file-organization example omits the newly extracted
CSwL/Logic/Proof.lean, even thoughProofis the first included section and has its own generated module. Include it so the README describes all three Logic section files.
(`CSwL/Logic/PL.lean`, `CSwL/Logic/FOL.lean`) — the same pattern
STYLE-CODE.md:62
- The regenerated ledger omits
linarith, although the newProof.leanpresents and uses it (Proof.lean:359-368and:532). Add it to this first-use row; otherwise the ledger is incomplete and the feature is not tracked.
| `Logic/Proof` | `open` | `¬`, `∀`, `∃`, `∧`, `∨`, `↔`, `≠` | `intro`, `exact`, `apply`, `cases … with`, `constructor`, `obtain`, `have`, `use`, `left`, `right`, `rcases`, `by_cases`, `by_contra`, `assumption` |
STYLE-CODE.md:149
- The HTML-splitting example lists only
Logic/PL/andLogic/FOL/, but the newProofsection also hasfile := "Proof"and becomesLogic/Proof/. The style guidance should not omit the first section it is intended to describe.
the chapter's directory — `Logic/PL/`, `Logic/FOL/` — which is what the
- Files reviewed: 18/18 changed files
- Comments generated: 13
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| example : Feq1.equivalent Feq2 := by | ||
| solution!(native_decide) |
| solution!( | ||
| constructor | ||
| · intro h | ||
| obtain ⟨h1, h2⟩ := h | ||
| apply And.intro | ||
| · exact h2 | ||
| · exact h1 | ||
| · intro h | ||
| constructor | ||
| · exact h.2 | ||
| · exact h.1) |
| have hMa : Ma := by | ||
| rcases ha with hMa | hAa | hCa | ||
| · solution! | ||
| exact hMa | ||
| · solution! | ||
| exact absurd hAa hnAa | ||
| · solution! | ||
| exact absurd hCa hnCa |
| ### Grammars: Mathlib's `ContextFreeGrammar`, deferred | ||
|
|
||
| `Games.lean` presents a BNF grammar for each game in prose, and then models it not as a grammar but as a handful of ordinary Lean types: enumerations for the terminal categories (`Colour`, `Answer`, `Column`, `Ship`), a `structure` for each rule with a fixed shape (`Turn`, `Attack`, `Move`), and `List`, `Vector` or a subtype where the BNF recurses or bounds a length (`abbrev Game := List Turn`, `abbrev Guess := Vector Colour 4`, `Reaction := { r : List Answer // r.length ≤ 4 }`). | ||
| `SeaBattle.lean` presents a BNF grammar in prose, and then models it not as a grammar but as a handful of ordinary Lean types: enumerations for the terminal categories (`Colour`, `Answer`, `Column`, `Ship`), a `structure` for each rule with a fixed shape (`Turn`, `Attack`, `Move`), and `List`, `Vector` or a subtype where the BNF recurses or bounds a length (`abbrev Game := List Turn`, `abbrev Guess := Vector Colour 4`, `Reaction := { r : List Answer // r.length ≤ 4 }`). |
| **`instance` is presented here.** The chapter's "Classes de tipos" section shows type classes only from the *use* side — the `[BEq α]` in a signature, and the difference between `BEq` and `DecidableEq`. But instances are declared from `Logic.lean` onwards: `PL.lean` gives `ToString Form`, `FOL.lean` three more, and `English.lean` fifteen, all of them `ToString`. Declaring an instance is a small step from the section already there, and it is the last piece of type classes the book actually needs — no chapter declares a `class` of its own. | ||
|
|
||
| **`Prop` is presented here, minimally.** Not by choice: inductive types bring `deriving DecidableEq`, `decide` and `#check 1 = 1`, all of which display `Prop`. `Games/Mastermind.lean` already derives `DecidableEq` on `Colour` and `Answer` in its first code block, two chapters before any logic. The student sees `Prop` whether or not it is introduced. So the chapter presents proposition-as-type, proof-as-term, and `rfl`, `intro`, `exact`, `decide` — and leaves natural deduction and quantifiers to `Logic.lean`. Without this, `IntroL.lean`, `Morphology.lean` and `Games.lean` are `Bool` and `#eval` throughout, which is the original book with Lean as a costume. | ||
| **`Prop` is presented here, minimally.** Not by choice: inductive types bring `deriving DecidableEq`, `decide` and `#check 1 = 1`, all of which display `Prop`. `SeaBattle.lean` already derives `DecidableEq` on its enumerations and `Answer` in its first code block, two chapters before any logic. The student sees `Prop` whether or not it is introduced. So the chapter presents proposition-as-type, proof-as-term, and `rfl`, `intro`, `exact`, `decide` — and leaves natural deduction and quantifiers to `Logic.lean`. Without this, `IntroL.lean`, `Morphology.lean` and `SeaBattle.lean` are `Bool` and `#eval` throughout, which is the original book with Lean as a costume. |
| The book has 76 exercises as of 2026-09-13, and all but one of them | ||
| appear somewhere below — either in a table that names its source, or | ||
| in the list of those with no counterpart. Two checks keep it that way: | ||
| no `(name := …)` in `CSwL/` should be absent from this file, and no | ||
| name cited here should have stopped existing. |
| column does not by itself settle whether the rule holds. `Logic/Proof.lean` | ||
| presents fourteen tactics in a plain code fence — one line each, `rfl` through | ||
| `funext` — and a scan that reads only ```` ```lean ```` blocks misses it. Check |
`IntroL.lean` loses roughly 80% of its lines. Three sections leave it:
"As duas leituras de uma função" and "Tipos na gramática e na
computação" move on, and "Tipos como disciplina" is dropped as the
book's thesis rather than the chapter's content. The `leanOutput`
blocks and `#reduce` go with them. What remains is the language the
rest of the book uses, and the chapter is left deliberately pre-proof.
`Proof.lean` gains the extensional/intensional discussion as a new
section, "Extensionalidade de Funções", which is where `funext`
belongs now that it is presented as a tactic. Its sections gain tags,
"Prova por indução" becomes "Provas por Indução", and a new exercise
`implication-as-disj` is added while `and-comm` drops to one star.
`PL.lean` and `FOL.lean` get prose revisions. `FOL.lean` also renders
its quantifiers as `∀`/`∃` rather than `A`/`E`, and its
scope-ambiguity passage is cut. Cross-references that pointed at an
exercise now point at a section instead: exercises cannot be
`{ref}`-ed, tracked in #24.
`DEVIATIONS.md` catches up with all of this. CSwFP/2.5 — the type BNF
and the three typing rules — moves from `IntroL.lean` to
`English.lean`, which already carried the rest of 2.5 and already
names `e` and `t`. The paragraph claiming `Prop` is presented in
`IntroL.lean` is rewritten: the argument for it was that the student
would meet `Prop` unintroduced, and that fell when `Proof.lean` became
part of the third chapter, ahead of every chapter that displays it.
Two stale claims are corrected along the way — the `restful` example
is gone with 2.5, and the instances declared downstream are three
`Repr` in `FOL.lean`, not `ToString` in `PL.lean`.
The restructuring agreed in September 2026, and the defects it uncovered.
Closes #3 and its five children (#8, #9, #10, #11, #12) — already closed with
their evidence — and #22.
The book's spine
The chapter order is now
IntroCS,IntroL,Logic,Sets,SeaBattle,Morphology,InfEngine,English. Mastermind is dropped (it announced asemantics in propositional logic it never delivered),
SeaBattleis promotedto a chapter, and
Gamesis gone.Propand the tactics were presented in three places. They are now presentedonce, in a new
CSwL/Logic/Proof.lean, which absorbsIntroL's "O tipo Prop eProvas",
PL's "Lógica Proposicional em Lean" andFOL's "As regras dosquantificadores".
LogicholdsProof,PL,FOLin that order;IntroLisnow about Lean as a programming language, keeping only the
example/theorem/rflkernel thatMorphologyneeds beforeLogic.FOLgained a computableFormula.evaland the bridge intoPropthatPLalready had, so the two chapters now have the same three-part shape: syntax as
data, computable semantics, and the theorem relating the two readings.
The generated project never compiled
Building the extracted project for the first time showed it had never worked.
This is the part worth reviewing closely, since the diagnosis in #22 turned out
to be wrong on both counts and the emitter was never at fault:
gapShipsat in the same ```lean +error fence as the definition meant tofail, so in the extracted project it was scoped inside an
sf_expect_failureblock while three examples used it at file scope.solution!uses were written in a shape that only survives while themarker is there — a structure instance aligned against a column the marker
created, and tactic blocks parenthesised rather than indented. As a tactic
solution!is replaced byall_goals, so the block must suit a single goal;vestidos₁had itsrcasesoutside the marker and its bullets inside.file := "introL"no longer matchedimport CSwL.IntroL, surfacing three chapters away asUnknown identifier IntroL.initS.makenow builds the generatedsolutionsproject (verify := truefor thatvariant alone), so a book that compiles while the student's project does not
fails loudly. The two reasons recorded for keeping that check off were stale
and are replaced with measurements: the toolchains now match, and Mathlib is
not rebuilt on every
make— it is 7.5 GB vendored once, ~3 s incrementally.Output that mirrors the source
Each section of
Logicgets its own HTML page, and — following the principlethat the Lean output should mirror the source's file organisation — its own
generated module:
replacing one 1819-line file. The criterion is
file :=, so a chapter whosesections do not set it is untouched (
Morphologyis opt-in later). Eachsection keeps the imports its own header declares. The silent import-dropping
path is now an error naming the file and the cause.
Checks
make allclean; the generatedsolutionsandgradingprojects build withzero errors.
that each section's headings rise one level, as a file's own headings should.
STYLE-CODE.mdledger was rescanned rather than patched; five rows werewrong.
native_decideis first met inLogic/PL, two chapters before theledger claimed, and is still presented nowhere — recorded in "Known gaps",
which changes Lean features used without being presented #7's picture of the same defect.
Note for the reviewer
studentandtersegenerated projects still fail to build, by design: theiranswers are
sorry. Onlysolutionsis verified.Three
sorrys remain in the sources (Sets.lean:258,:272,Proof.lean:158). All predate this branch and are deliberate exerciseplaceholders, though
Proof.lean:158reads as a proved theorem that is not —worth a separate decision.
AI-usage disclosure: the work in this branch was carried out with the
assistance of Claude Opus 5 (Anthropic, September 2026, via Claude Code). The
author set the direction throughout — the chapter order, the decision that the
solution!failures were misuse rather than a macro defect, the principle thatthe Lean output should mirror the source — and revised the prose. The assistant
diagnosed the extraction defects, wrote the saver changes, and ran the
verification. The author takes full responsibility for the final content.