@hobin/developer 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -11
- package/SOURCES.md +104 -0
- package/extensions/developer.ts +67 -24
- package/extensions/references/behavior-preserving-structural-change.md +145 -0
- package/extensions/skills.ts +1 -16
- package/extensions/state.ts +9 -1
- package/package.json +2 -1
- package/skills/abstraction-review/references/field-card.md +7 -0
- package/skills/abstraction-review/references/recipe-cards.md +67 -0
- package/skills/abstraction-review/references/repair-table.md +5 -0
- package/skills/model/SKILL.md +6 -1
- package/skills/model/references/problem-modeling.md +294 -235
- package/skills/model/references/worked-models-and-specialized-techniques.md +218 -0
- package/skills/naming-judgment/SKILL.md +5 -3
- package/skills/naming-judgment/references/domain-naming.md +202 -0
- package/skills/schedule/SKILL.md +1 -1
- package/skills/schedule/references/structural-change-timing.md +80 -13
- package/skills/signal/SKILL.md +2 -2
- package/skills/signal/references/structural-movement.md +202 -0
- package/skills/sketch/SKILL.md +35 -8
- package/skills/sketch/references/abstraction-barriers-and-closure.md +160 -0
- package/skills/sketch/references/abstraction-composition-and-state.md +184 -0
- package/skills/sketch/references/composition-generative-recursion-and-accumulators.md +196 -0
- package/skills/sketch/references/data-driven-design.md +177 -0
- package/skills/sketch/references/data-shape-template-catalog.md +241 -0
- package/skills/sketch/references/generic-operations-and-languages.md +134 -0
- package/skills/sketch/references/processes-state-and-time.md +171 -0
- package/skills/sketch/references/responsibility-and-variation.md +245 -0
- package/skills/verify/references/verifier-selection-and-pass-but-wrong.md +61 -3
- package/skills/naming-judgment/references/elements-of-clojure-naming.md +0 -74
- package/skills/signal/references/flocking-and-structural-movement.md +0 -157
- package/skills/sketch/references/design-recipe-and-abstraction-barriers.md +0 -169
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# Composition, Generative Recursion, And Accumulators
|
|
2
|
+
|
|
3
|
+
Use this reference after the base data recipe when structure alone does not
|
|
4
|
+
finish the design. It distinguishes four moves that are often blurred together:
|
|
5
|
+
designing an auxiliary, abstracting completed designs, generating new
|
|
6
|
+
subproblems, and remembering knowledge that traversal would otherwise lose.
|
|
7
|
+
|
|
8
|
+
## Design By Composition
|
|
9
|
+
|
|
10
|
+
Write a wishful top level before helper bodies:
|
|
11
|
+
|
|
12
|
+
```text
|
|
13
|
+
publishableTasks(tasks):
|
|
14
|
+
return sortByPriority(onlyReady(normalize(tasks)))
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
For each wished operation, run the full small recipe:
|
|
18
|
+
|
|
19
|
+
```text
|
|
20
|
+
purpose: what subproblem it solves
|
|
21
|
+
contract: input and output meanings
|
|
22
|
+
examples: representative success and boundary cases
|
|
23
|
+
template: derived from its data
|
|
24
|
+
reason: why it is a distinct subproblem
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
An auxiliary is earned when it solves a separate domain subproblem, processes a
|
|
28
|
+
referenced data definition, or generalizes a problem that the direct design
|
|
29
|
+
exposes. “The original function is long” is not an independent purpose.
|
|
30
|
+
|
|
31
|
+
## Abstraction From Completed Examples
|
|
32
|
+
|
|
33
|
+
Start with at least two correct concrete designs that share a template.
|
|
34
|
+
|
|
35
|
+
```text
|
|
36
|
+
sum(numbers):
|
|
37
|
+
Empty -> 0
|
|
38
|
+
Node(first, rest) -> first + sum(rest)
|
|
39
|
+
|
|
40
|
+
allReady(tasks):
|
|
41
|
+
Empty -> true
|
|
42
|
+
Node(first, rest) -> first.ready && allReady(rest)
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Align corresponding positions:
|
|
46
|
+
|
|
47
|
+
| Role | `sum` | `allReady` |
|
|
48
|
+
| --- | --- | --- |
|
|
49
|
+
| base | `0` | `true` |
|
|
50
|
+
| item projection | identity | `task.ready` |
|
|
51
|
+
| combine | `+` | `&&` |
|
|
52
|
+
|
|
53
|
+
Only now is a fold-shaped candidate visible:
|
|
54
|
+
|
|
55
|
+
```text
|
|
56
|
+
fold(sequence, base, project, combine)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
The shared template is evidence, not approval. Check that the roles have stable
|
|
60
|
+
meaning, old functions become simple calls, process/cost does not accidentally
|
|
61
|
+
change, and one realistic new case fits without adding flags. `abstraction-review`
|
|
62
|
+
owns the promotion decision.
|
|
63
|
+
|
|
64
|
+
## Structural Versus Generative Recursion
|
|
65
|
+
|
|
66
|
+
Structural recursion consumes pieces selected directly from the input data
|
|
67
|
+
definition. Generative recursion creates a new problem by domain knowledge.
|
|
68
|
+
|
|
69
|
+
```text
|
|
70
|
+
structural: totalEstimate(rest)
|
|
71
|
+
// rest is a field in Node(Task, Tasks)
|
|
72
|
+
|
|
73
|
+
generative: binarySearch(sorted, lower, midpoint - 1)
|
|
74
|
+
// the next interval is calculated, not selected from a data field
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Misclassifying the second as structural hides the algorithm idea and its
|
|
78
|
+
termination obligation.
|
|
79
|
+
|
|
80
|
+
## Generative Recursion Recipe
|
|
81
|
+
|
|
82
|
+
Answer four questions before the body:
|
|
83
|
+
|
|
84
|
+
```text
|
|
85
|
+
trivial: which problems have a direct answer?
|
|
86
|
+
generate: how are new subproblems produced?
|
|
87
|
+
solve/combine: how do recursive answers form this answer?
|
|
88
|
+
terminate: why do generated problems approach a trivial case?
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Worked binary-search sketch:
|
|
92
|
+
|
|
93
|
+
```text
|
|
94
|
+
search(sorted, target, low, high):
|
|
95
|
+
if low > high:
|
|
96
|
+
return NotFound // trivial
|
|
97
|
+
|
|
98
|
+
mid = floor((low + high) / 2) // generation knowledge
|
|
99
|
+
if sorted[mid] == target:
|
|
100
|
+
return Found(mid) // trivial
|
|
101
|
+
if target < sorted[mid]:
|
|
102
|
+
return search(sorted, target, low, mid - 1)
|
|
103
|
+
return search(sorted, target, mid + 1, high)
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Process examples must show generated intervals:
|
|
107
|
+
|
|
108
|
+
```text
|
|
109
|
+
[0, 6] -> [4, 6] -> [4, 4] -> Found(4)
|
|
110
|
+
[0, 0] -> [1, 0] -> NotFound
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Termination measure: `high - low + 1` is a natural number for non-trivial
|
|
114
|
+
calls, and each recursive call strictly decreases it. If no decreasing measure
|
|
115
|
+
is known, state the possible non-termination boundary rather than asserting
|
|
116
|
+
that the input “gets smaller.”
|
|
117
|
+
|
|
118
|
+
## Accumulator Recipe
|
|
119
|
+
|
|
120
|
+
Do not begin by adding an extra parameter. First identify the lost knowledge or
|
|
121
|
+
repeated work in a conventional design.
|
|
122
|
+
|
|
123
|
+
Example pressure: repeatedly appending to the end while reversing a sequence
|
|
124
|
+
reprocesses prior results. Introduce `seenReversed` with an invariant:
|
|
125
|
+
|
|
126
|
+
```text
|
|
127
|
+
original == reverse(seenReversed) ++ remaining
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Trace the relation:
|
|
131
|
+
|
|
132
|
+
```text
|
|
133
|
+
original [a,b,c], remaining [a,b,c], seenReversed []
|
|
134
|
+
original [a,b,c], remaining [b,c], seenReversed [a]
|
|
135
|
+
original [a,b,c], remaining [c], seenReversed [b,a]
|
|
136
|
+
original [a,b,c], remaining [], seenReversed [c,b,a]
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
The invariant determines initialization, update, and result:
|
|
140
|
+
|
|
141
|
+
```text
|
|
142
|
+
reverse(items):
|
|
143
|
+
loop(remaining, seenReversed):
|
|
144
|
+
Empty -> seenReversed
|
|
145
|
+
Node(first, rest) -> loop(rest, prepend(first, seenReversed))
|
|
146
|
+
return loop(items, Empty)
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Check the invariant at three points:
|
|
150
|
+
|
|
151
|
+
1. initialization relates the original input to the initial accumulator;
|
|
152
|
+
2. one transition preserves the relationship;
|
|
153
|
+
3. the base case plus the invariant implies the promised result.
|
|
154
|
+
|
|
155
|
+
Different invariants create different algorithms. Choose the simplest invariant
|
|
156
|
+
that solves observed pressure; an accumulator with no stated meaning is hidden
|
|
157
|
+
state.
|
|
158
|
+
|
|
159
|
+
## Iterative Refinement
|
|
160
|
+
|
|
161
|
+
When a required case cannot be expressed or a forbidden case remains possible,
|
|
162
|
+
change the data definition and propagate the change through:
|
|
163
|
+
|
|
164
|
+
```text
|
|
165
|
+
data examples
|
|
166
|
+
-> behavior examples
|
|
167
|
+
-> templates
|
|
168
|
+
-> implementations
|
|
169
|
+
-> tests
|
|
170
|
+
-> serialized/public compatibility surfaces
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
A local type edit is incomplete when old persisted forms or callers remain in
|
|
174
|
+
circulation.
|
|
175
|
+
|
|
176
|
+
## Failure Diagnosis
|
|
177
|
+
|
|
178
|
+
| Symptom | Repair |
|
|
179
|
+
| --- | --- |
|
|
180
|
+
| wished helper has no examples or independent purpose | inline it or run its own recipe |
|
|
181
|
+
| abstraction is derived from one function | complete another concrete design first |
|
|
182
|
+
| abstract parameters are implementation fragments | restate stable variation roles |
|
|
183
|
+
| generated recursive call lacks an algorithm idea | return to structural template or state generation rule |
|
|
184
|
+
| termination says only “smaller” | provide a well-founded measure or explicit divergence boundary |
|
|
185
|
+
| accumulator is added for style | show lost knowledge, repeated work, or process pressure |
|
|
186
|
+
| accumulator update is guessed | derive it from the invariant |
|
|
187
|
+
|
|
188
|
+
## Source Trace
|
|
189
|
+
|
|
190
|
+
- [*How to Design Programs*, official living edition](https://htdp.org/2026-5-28/Book/index.html):
|
|
191
|
+
design by composition, abstraction from concrete functions, generative
|
|
192
|
+
recursion and termination, accumulators and invariants, and iterative
|
|
193
|
+
refinement.
|
|
194
|
+
- Harold Abelson and Gerald Jay Sussman with Julie Sussman, *Structure and
|
|
195
|
+
Interpretation of Computer Programs*, Second Edition, MIT Press, 1996:
|
|
196
|
+
wishful procedure decomposition, higher-order abstraction, and process shape.
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# Data-Driven Design
|
|
2
|
+
|
|
3
|
+
Use this reference when product information should determine the program shape.
|
|
4
|
+
The central insight is concrete: **a data definition is a construction rule for
|
|
5
|
+
valid values, and that same rule determines the first honest function
|
|
6
|
+
template**. Code shape should not be guessed from framework files or a favorite
|
|
7
|
+
pattern when the cases are already present in the data.
|
|
8
|
+
|
|
9
|
+
## The Six-Artifact Recipe
|
|
10
|
+
|
|
11
|
+
Produce these artifacts in order. Each one catches a different error.
|
|
12
|
+
|
|
13
|
+
```text
|
|
14
|
+
1. data definition + interpretation
|
|
15
|
+
2. representative data examples
|
|
16
|
+
3. signature + purpose + executable stub
|
|
17
|
+
4. behavior examples
|
|
18
|
+
5. template derived only from the data definition
|
|
19
|
+
6. completed function + checks
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
The order is diagnostic, not ceremonial. If the template has no branch for a
|
|
23
|
+
required case, repair the data definition. If the template exposes all required
|
|
24
|
+
ingredients but the result is wrong, repair the completed body. If no template
|
|
25
|
+
ingredient can express the purpose, design an auxiliary with its own recipe.
|
|
26
|
+
|
|
27
|
+
In an existing repository, recover the same artifacts from schemas, types,
|
|
28
|
+
fixtures, callers, UI states, persistence, and tests. Do not copy pedagogical
|
|
29
|
+
comments into production merely to imitate the recipe.
|
|
30
|
+
|
|
31
|
+
## Complete Example: Let The Variants Write The Skeleton
|
|
32
|
+
|
|
33
|
+
Requirement: show the customer-facing instruction for a fulfillment method.
|
|
34
|
+
|
|
35
|
+
### 1. Define and interpret the data
|
|
36
|
+
|
|
37
|
+
```text
|
|
38
|
+
Fulfillment =
|
|
39
|
+
Pickup(storeId) // collect from this store
|
|
40
|
+
| Delivery(address) // send to this address
|
|
41
|
+
| Locker(lockerId) // collect from this locker
|
|
42
|
+
|
|
43
|
+
Excluded: a fulfillment value with none or several of these variants.
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
This is not merely a union type. Its interpretation says the three variants are
|
|
47
|
+
different product meanings, so silently merging branches would lose policy.
|
|
48
|
+
|
|
49
|
+
### 2. Construct one value from every clause
|
|
50
|
+
|
|
51
|
+
```text
|
|
52
|
+
Pickup("gangnam")
|
|
53
|
+
Delivery("12 Teheran-ro")
|
|
54
|
+
Locker("L-17")
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
If a valid example cannot be constructed, the definition is unusable. Add
|
|
58
|
+
boundary examples when fields have their own restrictions.
|
|
59
|
+
|
|
60
|
+
### 3. State what the function computes
|
|
61
|
+
|
|
62
|
+
```text
|
|
63
|
+
instruction : Fulfillment -> Text
|
|
64
|
+
purpose: produce the instruction a customer should follow
|
|
65
|
+
stub: instruction(method) = ""
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
The purpose says *what*, not “inspect the tag and branch.”
|
|
69
|
+
|
|
70
|
+
### 4. Work behavior examples before implementation
|
|
71
|
+
|
|
72
|
+
```text
|
|
73
|
+
instruction(Pickup("gangnam"))
|
|
74
|
+
== "Pick up at gangnam"
|
|
75
|
+
instruction(Delivery("12 Teheran-ro"))
|
|
76
|
+
== "Deliver to 12 Teheran-ro"
|
|
77
|
+
instruction(Locker("L-17"))
|
|
78
|
+
== "Collect from locker L-17"
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### 5. Derive, do not invent, the template
|
|
82
|
+
|
|
83
|
+
One data clause becomes one branch. Fields in that clause become available
|
|
84
|
+
ingredients.
|
|
85
|
+
|
|
86
|
+
```text
|
|
87
|
+
instruction(method):
|
|
88
|
+
match method:
|
|
89
|
+
Pickup(storeId) -> ... storeId ...
|
|
90
|
+
Delivery(address) -> ... address ...
|
|
91
|
+
Locker(lockerId) -> ... lockerId ...
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
The strings and formatting policy do not come from the data shape; the purpose
|
|
95
|
+
and examples supply them. The branch structure and accessible fields do.
|
|
96
|
+
|
|
97
|
+
### 6. Complete and check
|
|
98
|
+
|
|
99
|
+
```text
|
|
100
|
+
instruction(method):
|
|
101
|
+
match method:
|
|
102
|
+
Pickup(storeId) -> "Pick up at " + storeId
|
|
103
|
+
Delivery(address) -> "Deliver to " + address
|
|
104
|
+
Locker(lockerId) -> "Collect from locker " + lockerId
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Run all three examples. Then try a fake fourth variant. A compiler failure or
|
|
108
|
+
explicit missing branch reveals the exact extension point; a catch-all returning
|
|
109
|
+
an empty string would hide it.
|
|
110
|
+
|
|
111
|
+
## Select The Specific Recipe
|
|
112
|
+
|
|
113
|
+
Read only the detail that matches the unresolved design question:
|
|
114
|
+
|
|
115
|
+
| Evidence | Read | Expected artifact |
|
|
116
|
+
| --- | --- | --- |
|
|
117
|
+
| atomic values, intervals, itemizations, records, recursive or mutually recursive data, multiple complex inputs, complex outputs, or event loops | [Data-Shape Template Catalog](data-shape-template-catalog.md) | a template whose branches, selectors, recursive positions, and delegations correspond to the data definition |
|
|
118
|
+
| wished helpers, several completed concrete functions, generated subproblems, termination uncertainty, repeated work, or lost traversal knowledge | [Composition, Generative Recursion, And Accumulators](composition-generative-recursion-and-accumulators.md) | separately designed auxiliaries, a justified abstraction, a generation template plus termination argument, or an accumulator invariant |
|
|
119
|
+
|
|
120
|
+
These documents extend this recipe; they do not replace the six artifacts.
|
|
121
|
+
|
|
122
|
+
## Existing-Code Recovery
|
|
123
|
+
|
|
124
|
+
Use this recovery table rather than assuming the declared type is complete.
|
|
125
|
+
|
|
126
|
+
| Artifact | Repository evidence | Typical contradiction |
|
|
127
|
+
| --- | --- | --- |
|
|
128
|
+
| data definition | schema, type, constructors, persisted samples | runtime contains a legacy or impossible-looking shape |
|
|
129
|
+
| interpretation | product copy, callers, docs, analytics meaning | two same-shaped values mean different things |
|
|
130
|
+
| data examples | fixtures, factories, production samples | only the happy variant has an example |
|
|
131
|
+
| behavior examples | tests, screenshots, API examples | expected output encodes an undocumented default |
|
|
132
|
+
| template | branches, selectors, traversal, delegation | catch-all hides a meaningful clause |
|
|
133
|
+
| completed body | implementation | code needs information absent from the data model |
|
|
134
|
+
|
|
135
|
+
When evidence conflicts, keep the contradiction visible. `model` owns disputed
|
|
136
|
+
validity or policy; this reference shapes the implementable surface once the
|
|
137
|
+
meaning is accepted.
|
|
138
|
+
|
|
139
|
+
## Sketch Output
|
|
140
|
+
|
|
141
|
+
```text
|
|
142
|
+
Purpose: result in product language
|
|
143
|
+
Data: valid clauses, interpretation, excluded values
|
|
144
|
+
Data examples: at least one per clause and meaningful boundary
|
|
145
|
+
Behavior examples: input -> expected result with rationale
|
|
146
|
+
Template: branches, selectors, recursive calls, and delegations derived from data
|
|
147
|
+
Wish list: separately designed subproblems
|
|
148
|
+
Checks: examples, properties, boundaries, and compatibility evidence
|
|
149
|
+
First item: smallest executable step implied by the template
|
|
150
|
+
Deferred: abstractions or policies not yet earned
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Failure Diagnosis
|
|
154
|
+
|
|
155
|
+
| Symptom | Return to |
|
|
156
|
+
| --- | --- |
|
|
157
|
+
| valid product case has no representation | data definition and interpretation |
|
|
158
|
+
| data clause has no example | data examples |
|
|
159
|
+
| tests describe traversal rather than result | purpose and behavior examples |
|
|
160
|
+
| branch has no corresponding data clause | template derivation |
|
|
161
|
+
| recursive call is on an arbitrary “smaller” value | structural versus generative recursion recipe |
|
|
162
|
+
| helper has a body but no independent purpose | wish-list design |
|
|
163
|
+
| abstraction precedes comparable completed designs | abstraction-from-examples recipe |
|
|
164
|
+
| changed type ignores stored/public older forms | existing-code recovery and compatibility model |
|
|
165
|
+
|
|
166
|
+
## Source Trace
|
|
167
|
+
|
|
168
|
+
- Matthias Felleisen, Robert Bruce Findler, Matthew Flatt, and Shriram
|
|
169
|
+
Krishnamurthi, *How to Design Programs, Second Edition*, MIT Press, 2018,
|
|
170
|
+
and the [official living edition](https://htdp.org/2026-5-28/Book/index.html):
|
|
171
|
+
information analysis, data definitions and interpretations, examples,
|
|
172
|
+
signatures and purpose statements, templates, implementations, tests, and
|
|
173
|
+
iterative refinement.
|
|
174
|
+
- Harold Abelson and Gerald Jay Sussman with Julie Sussman, *Structure and
|
|
175
|
+
Interpretation of Computer Programs*, Second Edition, MIT Press, 1996:
|
|
176
|
+
wishful decomposition and the distinction between procedure text and the
|
|
177
|
+
process it generates.
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
# Data-Shape Template Catalog
|
|
2
|
+
|
|
3
|
+
Use this catalog after the six-artifact recipe in
|
|
4
|
+
[Data-Driven Design](data-driven-design.md). Its core rule is:
|
|
5
|
+
|
|
6
|
+
```text
|
|
7
|
+
data clause -> function branch
|
|
8
|
+
compound field -> selector or destructured value
|
|
9
|
+
self-reference -> natural recursive call at that exact position
|
|
10
|
+
reference to B -> delegation to B's template at that exact position
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
The template inventories available ingredients. The purpose and examples decide
|
|
14
|
+
how to combine them.
|
|
15
|
+
|
|
16
|
+
## Atomic And Fixed-Size Data
|
|
17
|
+
|
|
18
|
+
For an atomic value, the template contains the parameter and relevant domain
|
|
19
|
+
constants. There is no structural decomposition to invent.
|
|
20
|
+
|
|
21
|
+
```text
|
|
22
|
+
Celsius = Number interpreted as a measured temperature
|
|
23
|
+
|
|
24
|
+
alertLevel : Celsius -> Level
|
|
25
|
+
alertLevel(celsius) = ... celsius ... FREEZE_POINT ... HEAT_LIMIT ...
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Intervals add meaningful partitions only when behavior changes at their
|
|
29
|
+
boundaries:
|
|
30
|
+
|
|
31
|
+
```text
|
|
32
|
+
alertLevel(celsius):
|
|
33
|
+
if celsius < 0 -> ...
|
|
34
|
+
else if celsius < 35 -> ...
|
|
35
|
+
else -> ...
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Do not branch on arbitrary numeric ranges merely because a conditional is
|
|
39
|
+
possible. Each range needs a product interpretation and boundary examples.
|
|
40
|
+
|
|
41
|
+
## Itemizations And Enumerations
|
|
42
|
+
|
|
43
|
+
For a finite set of alternatives, create one branch and one behavior example per
|
|
44
|
+
meaningful clause. The complete fulfillment example in the parent reference is
|
|
45
|
+
the canonical shape.
|
|
46
|
+
|
|
47
|
+
Template:
|
|
48
|
+
|
|
49
|
+
```text
|
|
50
|
+
Result = Success(value) | Rejected(reason) | Retryable(delay)
|
|
51
|
+
|
|
52
|
+
render(result):
|
|
53
|
+
match result:
|
|
54
|
+
Success(value) -> ... value ...
|
|
55
|
+
Rejected(reason) -> ... reason ...
|
|
56
|
+
Retryable(delay) -> ... delay ...
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Merge two clauses only when their product meaning, not merely their current body,
|
|
60
|
+
is the same. A catch-all branch is suspicious when adding a variant should force
|
|
61
|
+
a new decision.
|
|
62
|
+
|
|
63
|
+
## Structures And Records
|
|
64
|
+
|
|
65
|
+
A record groups a fixed natural whole. List only fields relevant to the purpose;
|
|
66
|
+
the template is not permission to expose every stored field.
|
|
67
|
+
|
|
68
|
+
```text
|
|
69
|
+
Schedule = Schedule(start, end, timezone)
|
|
70
|
+
|
|
71
|
+
duration(schedule):
|
|
72
|
+
... schedule.start ... schedule.end ...
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
`timezone` remains absent because elapsed duration does not need it. If the
|
|
76
|
+
function unexpectedly needs recurrence policy, either the purpose is broader
|
|
77
|
+
than stated or the data definition lacks information.
|
|
78
|
+
|
|
79
|
+
## Self-Referential Data
|
|
80
|
+
|
|
81
|
+
A valid self-referential definition has at least one non-recursive clause from
|
|
82
|
+
which values can be constructed and at least one recursive clause that creates
|
|
83
|
+
larger values.
|
|
84
|
+
|
|
85
|
+
```text
|
|
86
|
+
Tasks = Empty | Node(Task, Tasks)
|
|
87
|
+
interpretation: a finite ordered task sequence
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Construct examples from the base outward:
|
|
91
|
+
|
|
92
|
+
```text
|
|
93
|
+
Empty
|
|
94
|
+
Node(Task("review", 20), Empty)
|
|
95
|
+
Node(Task("write", 40), Node(Task("review", 20), Empty))
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
For `totalEstimate`, derive the template mechanically:
|
|
99
|
+
|
|
100
|
+
```text
|
|
101
|
+
totalEstimate(tasks):
|
|
102
|
+
match tasks:
|
|
103
|
+
Empty -> ...
|
|
104
|
+
Node(first, rest) ->
|
|
105
|
+
... first ... totalEstimate(rest) ...
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Why that recursive call? The `Node` clause contains one `Tasks` field named
|
|
109
|
+
`rest`; the template therefore contains one natural recursive call on `rest`.
|
|
110
|
+
The completed function follows from examples:
|
|
111
|
+
|
|
112
|
+
```text
|
|
113
|
+
totalEstimate(tasks):
|
|
114
|
+
match tasks:
|
|
115
|
+
Empty -> 0
|
|
116
|
+
Node(first, rest) -> first.minutes + totalEstimate(rest)
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Test at least two recursive layers. Recursing on `dropEveryOther(rest)` or a
|
|
120
|
+
newly partitioned list is not structurally derived; use the generative-recursion
|
|
121
|
+
recipe instead.
|
|
122
|
+
|
|
123
|
+
## Mutually Referential Data
|
|
124
|
+
|
|
125
|
+
When definitions refer to each other, derive a family of templates with a
|
|
126
|
+
delegation wherever the reference occurs.
|
|
127
|
+
|
|
128
|
+
```text
|
|
129
|
+
Document = Document(List<Section>)
|
|
130
|
+
Section = Section(title, List<Block>, List<Section>)
|
|
131
|
+
Block = Paragraph(text) | Image(src)
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
For a word-count sketch:
|
|
135
|
+
|
|
136
|
+
```text
|
|
137
|
+
countDocument(document):
|
|
138
|
+
combine map(countSection, document.sections)
|
|
139
|
+
|
|
140
|
+
countSection(section):
|
|
141
|
+
combine map(countBlock, section.blocks)
|
|
142
|
+
map(countSection, section.children)
|
|
143
|
+
|
|
144
|
+
countBlock(block):
|
|
145
|
+
match block:
|
|
146
|
+
Paragraph(text) -> words(text)
|
|
147
|
+
Image(src) -> 0
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
The reference graph, not convenience, determines the delegations. An oversized
|
|
151
|
+
function with nested tags usually means the template family was collapsed.
|
|
152
|
+
|
|
153
|
+
## Two Complex Inputs
|
|
154
|
+
|
|
155
|
+
Do not automatically multiply every clause. Decide how the two values move.
|
|
156
|
+
|
|
157
|
+
### One input controls
|
|
158
|
+
|
|
159
|
+
`applyDiscounts(cart, rules)` traverses the cart while `rules` is constant or
|
|
160
|
+
queried. Derive the traversal from `Cart`; treat `Rules` as domain knowledge.
|
|
161
|
+
|
|
162
|
+
### Inputs move in lockstep
|
|
163
|
+
|
|
164
|
+
`sameShape(leftTree, rightTree)` compares corresponding nodes. State the shape
|
|
165
|
+
relation and branch on paired cases:
|
|
166
|
+
|
|
167
|
+
```text
|
|
168
|
+
(Leaf, Leaf)
|
|
169
|
+
(Node, Node) -> recurse on corresponding children
|
|
170
|
+
(Leaf, Node) or (Node, Leaf) -> false
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Inputs vary independently
|
|
174
|
+
|
|
175
|
+
Collision policy between `UserRole` and `ResourceState` may need the meaningful
|
|
176
|
+
cross-product or a decision table. Write all relevant combinations explicitly;
|
|
177
|
+
do not hide them in nested defaults.
|
|
178
|
+
|
|
179
|
+
The smell is not “two loops.” It is an unstated relationship between the input
|
|
180
|
+
shapes.
|
|
181
|
+
|
|
182
|
+
## Functions That Produce Complex Data
|
|
183
|
+
|
|
184
|
+
The output definition constrains constructors just as the input definition
|
|
185
|
+
constrains selectors.
|
|
186
|
+
|
|
187
|
+
```text
|
|
188
|
+
Notice = Info(text) | Warning(text) | Blocked(reason)
|
|
189
|
+
|
|
190
|
+
classify(order):
|
|
191
|
+
... -> Info(...)
|
|
192
|
+
... -> Warning(...)
|
|
193
|
+
... -> Blocked(...)
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Work expected output values before choosing constructors. If several output
|
|
197
|
+
variants are valid, the unresolved choice is policy, not implementation freedom.
|
|
198
|
+
|
|
199
|
+
## Interactive Programs
|
|
200
|
+
|
|
201
|
+
Define the world state before handlers:
|
|
202
|
+
|
|
203
|
+
```text
|
|
204
|
+
World = Editing(draft) | Saving(draft, requestId) | Failed(draft, reason)
|
|
205
|
+
|
|
206
|
+
onEvent : World x Event -> World
|
|
207
|
+
view : World -> UI
|
|
208
|
+
stop : World -> Boolean
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Then write event traces, not only snapshots:
|
|
212
|
+
|
|
213
|
+
```text
|
|
214
|
+
Editing("a") --Save--> Saving("a", 7)
|
|
215
|
+
Saving("a", 7) --Saved(7)--> Editing("a")
|
|
216
|
+
Saving("a", 7) --Saved(6)--> Saving("a", 7) // stale response ignored
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
The handlers derive branches from both the world and event definitions. If
|
|
220
|
+
correctness depends on history beyond `World`, refine the state or hand the
|
|
221
|
+
temporal rule to `model`.
|
|
222
|
+
|
|
223
|
+
## Diagnosis
|
|
224
|
+
|
|
225
|
+
| Symptom | Likely repair |
|
|
226
|
+
| --- | --- |
|
|
227
|
+
| branch does not correspond to a data clause | remove it or repair the data definition |
|
|
228
|
+
| selector is used outside its clause | restore the clause guard or use a better representation |
|
|
229
|
+
| recursive call has no self-reference | classify as generative recursion or remove it |
|
|
230
|
+
| self-reference has no recursive call | explain why that portion is intentionally ignored |
|
|
231
|
+
| mutually referential logic is one giant conditional | derive one template per definition |
|
|
232
|
+
| two complex inputs create accidental nested loops | state controlling, lockstep, or independent relation |
|
|
233
|
+
| event handler cannot distinguish stale events | add identity/history to the world model |
|
|
234
|
+
|
|
235
|
+
## Source Trace
|
|
236
|
+
|
|
237
|
+
- [*How to Design Programs*, official living edition](https://htdp.org/2026-5-28/Book/index.html):
|
|
238
|
+
the recipes for atomic data, intervals, itemizations, structures,
|
|
239
|
+
self-referential and intertwined definitions, multiple complex inputs,
|
|
240
|
+
complex outputs, and world programs. The examples here are adapted to ordinary
|
|
241
|
+
product code and are not copied from the book.
|