functionalscript 0.45.0 → 0.46.1

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.
Files changed (296) hide show
  1. package/README.md +5 -3
  2. package/fjs/asn.1/module.f.mjs +8 -1
  3. package/fjs/asn.1/proof.f.d.mts +10 -0
  4. package/fjs/asn.1/proof.f.mjs +16 -0
  5. package/fjs/basen/base128/module.f.mjs +22 -5
  6. package/fjs/bnf/data/module.f.d.mts +32 -6
  7. package/fjs/bnf/data/module.f.mjs +112 -7
  8. package/fjs/bnf/data/proof.f.d.mts +2 -0
  9. package/fjs/bnf/data/proof.f.mjs +74 -3
  10. package/fjs/bnf/data/types.d.ts +20 -2
  11. package/fjs/bnf/descent/module.f.d.mts +28 -14
  12. package/fjs/bnf/descent/module.f.mjs +122 -77
  13. package/fjs/bnf/descent/proof.f.d.mts +2 -0
  14. package/fjs/bnf/descent/proof.f.mjs +117 -31
  15. package/fjs/bnf/descent/types.d.ts +12 -14
  16. package/fjs/bnf/ll1/module.f.d.mts +64 -22
  17. package/fjs/bnf/ll1/module.f.mjs +214 -154
  18. package/fjs/bnf/ll1/proof.f.d.mts +15 -2
  19. package/fjs/bnf/ll1/proof.f.mjs +323 -149
  20. package/fjs/bnf/ll1/types.d.ts +16 -24
  21. package/fjs/bnf/matcher/module.f.d.mts +66 -0
  22. package/fjs/bnf/matcher/module.f.mjs +81 -0
  23. package/fjs/bnf/matcher/proof.f.d.mts +10 -0
  24. package/fjs/bnf/matcher/proof.f.mjs +79 -0
  25. package/fjs/bnf/matcher/types.d.ts +54 -0
  26. package/fjs/bnf/testlib.f.d.mts +31 -0
  27. package/fjs/bnf/testlib.f.mjs +80 -0
  28. package/fjs/cas/cli/module.f.d.mts +1 -1
  29. package/fjs/cas/cli/module.f.mjs +14 -20
  30. package/fjs/cas/cli/proof.f.d.mts +1 -3
  31. package/fjs/cas/cli/proof.f.mjs +44 -33
  32. package/fjs/cas/evo/module.f.d.mts +64 -18
  33. package/fjs/cas/evo/module.f.mjs +148 -70
  34. package/fjs/cas/evo/proof.f.d.mts +10 -1
  35. package/fjs/cas/evo/proof.f.mjs +305 -223
  36. package/fjs/cas/evo/types.d.ts +45 -9
  37. package/fjs/cas/module.f.d.mts +18 -32
  38. package/fjs/cas/module.f.mjs +129 -128
  39. package/fjs/cas/proof.f.d.mts +11 -8
  40. package/fjs/cas/proof.f.mjs +259 -147
  41. package/fjs/cas/types.d.ts +24 -10
  42. package/fjs/ci/config/module.f.d.mts +2 -2
  43. package/fjs/ci/config/module.f.mjs +2 -2
  44. package/fjs/ci/module.f.d.mts +5 -5
  45. package/fjs/ci/module.f.mjs +8 -7
  46. package/fjs/ci/nix/module.f.d.mts +7 -5
  47. package/fjs/ci/nix/module.f.mjs +13 -12
  48. package/fjs/ci/nix/proof.f.mjs +2 -2
  49. package/fjs/ci/proof.f.mjs +9 -5
  50. package/fjs/cli/module.f.d.mts +4 -6
  51. package/fjs/cli/module.f.mjs +4 -8
  52. package/fjs/cli/proof.f.mjs +17 -16
  53. package/fjs/cli/types.d.ts +2 -3
  54. package/fjs/common/monoid/types.d.ts +1 -1
  55. package/fjs/crypto/hmac/module.f.mjs +2 -2
  56. package/fjs/crypto/sha2/module.f.mjs +3 -1
  57. package/fjs/crypto/sha2/proof.f.d.mts +1 -0
  58. package/fjs/crypto/sha2/proof.f.mjs +24 -0
  59. package/fjs/crypto/sha2/types.d.ts +11 -0
  60. package/fjs/crypto/sign/module.f.mjs +2 -2
  61. package/fjs/dev/module.f.d.mts +13 -4
  62. package/fjs/dev/module.f.mjs +56 -27
  63. package/fjs/dev/update/module.f.d.mts +9 -4
  64. package/fjs/dev/update/module.f.mjs +14 -10
  65. package/fjs/dev/update/proof.f.d.mts +1 -3
  66. package/fjs/dev/update/proof.f.mjs +10 -5
  67. package/fjs/djs/module.f.d.mts +13 -5
  68. package/fjs/djs/module.f.mjs +31 -16
  69. package/fjs/djs/parser/module.f.d.mts +13 -3
  70. package/fjs/djs/parser/module.f.mjs +117 -16
  71. package/fjs/djs/parser/proof.f.d.mts +5 -1
  72. package/fjs/djs/parser/proof.f.mjs +274 -12
  73. package/fjs/djs/parser/types.d.ts +7 -1
  74. package/fjs/djs/proof.f.d.mts +18 -2
  75. package/fjs/djs/proof.f.mjs +187 -12
  76. package/fjs/djs/serializer/module.f.d.mts +25 -8
  77. package/fjs/djs/serializer/module.f.mjs +61 -15
  78. package/fjs/djs/serializer/proof.f.d.mts +5 -0
  79. package/fjs/djs/serializer/proof.f.mjs +23 -1
  80. package/fjs/djs/tokenizer/module.f.d.mts +17 -5
  81. package/fjs/djs/tokenizer/module.f.mjs +99 -60
  82. package/fjs/djs/tokenizer/proof.f.d.mts +1 -1
  83. package/fjs/djs/tokenizer/proof.f.mjs +74 -67
  84. package/fjs/djs/transpiler/module.f.d.mts +12 -7
  85. package/fjs/djs/transpiler/module.f.mjs +82 -56
  86. package/fjs/djs/transpiler/types.d.ts +7 -3
  87. package/fjs/djs/types.d.ts +7 -1
  88. package/fjs/effects/list/module.f.d.mts +14 -11
  89. package/fjs/effects/list/module.f.mjs +13 -11
  90. package/fjs/effects/list/types.d.ts +27 -7
  91. package/fjs/effects/memory/module.f.d.mts +2 -1
  92. package/fjs/effects/memory/module.f.mjs +3 -4
  93. package/fjs/effects/memory/proof.f.mjs +13 -7
  94. package/fjs/effects/memory/types.d.ts +4 -3
  95. package/fjs/effects/mock/module.f.d.mts +20 -4
  96. package/fjs/effects/mock/module.f.mjs +37 -5
  97. package/fjs/effects/mock/types.d.ts +10 -1
  98. package/fjs/effects/module.d.mts +4 -2
  99. package/fjs/effects/module.f.d.mts +466 -280
  100. package/fjs/effects/module.f.mjs +537 -299
  101. package/fjs/effects/module.mjs +2 -1
  102. package/fjs/effects/node/memory/module.d.mts +4 -2
  103. package/fjs/effects/node/memory/module.mjs +6 -3
  104. package/fjs/effects/node/memory/proof.mjs +10 -4
  105. package/fjs/effects/node/module.d.mts +4 -3
  106. package/fjs/effects/node/module.f.d.mts +168 -33
  107. package/fjs/effects/node/module.f.mjs +256 -52
  108. package/fjs/effects/node/module.mjs +57 -34
  109. package/fjs/effects/node/proof.f.d.mts +28 -2
  110. package/fjs/effects/node/proof.f.mjs +161 -42
  111. package/fjs/effects/node/types.d.ts +106 -18
  112. package/fjs/effects/node/virtual/module.f.d.mts +18 -4
  113. package/fjs/effects/node/virtual/module.f.mjs +110 -68
  114. package/fjs/effects/node/virtual/proof.f.d.mts +28 -2
  115. package/fjs/effects/node/virtual/proof.f.mjs +190 -9
  116. package/fjs/effects/proof.f.d.mts +69 -37
  117. package/fjs/effects/proof.f.mjs +410 -130
  118. package/fjs/effects/types.d.ts +161 -33
  119. package/fjs/emergent_testing/module.f.d.mts +19 -12
  120. package/fjs/emergent_testing/module.f.mjs +93 -33
  121. package/fjs/emergent_testing/proof.f.d.mts +21 -7
  122. package/fjs/emergent_testing/proof.f.mjs +166 -32
  123. package/fjs/emergent_testing/types.d.ts +22 -4
  124. package/fjs/fsm/module.f.d.mts +14 -4
  125. package/fjs/fsm/module.f.mjs +54 -37
  126. package/fjs/fsm/proof.f.d.mts +2 -0
  127. package/fjs/fsm/proof.f.mjs +83 -114
  128. package/fjs/js/keywords/module.f.d.mts +52 -0
  129. package/fjs/js/keywords/module.f.mjs +72 -0
  130. package/fjs/js/keywords/proof.f.d.mts +3 -0
  131. package/fjs/js/keywords/proof.f.mjs +13 -0
  132. package/fjs/js/tokenizer/module.f.d.mts +26 -6
  133. package/fjs/js/tokenizer/module.f.mjs +145 -159
  134. package/fjs/js/tokenizer/proof.f.d.mts +1 -0
  135. package/fjs/js/tokenizer/proof.f.mjs +54 -24
  136. package/fjs/js/tokenizer/types.d.ts +33 -24
  137. package/fjs/mcp/cas/module.f.d.mts +1 -6
  138. package/fjs/mcp/cas/module.f.mjs +55 -51
  139. package/fjs/mcp/cas/proof.f.d.mts +15 -0
  140. package/fjs/mcp/cas/proof.f.mjs +174 -0
  141. package/fjs/mcp/evo/module.f.d.mts +19 -10
  142. package/fjs/mcp/evo/module.f.mjs +48 -27
  143. package/fjs/mcp/evo/proof.f.d.mts +6 -1
  144. package/fjs/mcp/evo/proof.f.mjs +115 -31
  145. package/fjs/mcp/module.f.d.mts +4 -4
  146. package/fjs/mcp/module.f.mjs +6 -6
  147. package/fjs/mcp/proof.f.d.mts +5 -3
  148. package/fjs/mcp/proof.f.mjs +112 -46
  149. package/fjs/media/html/module.f.mjs +1 -1
  150. package/fjs/media/json/extended/module.f.d.mts +82 -0
  151. package/fjs/media/json/extended/module.f.mjs +153 -0
  152. package/fjs/media/json/extended/proof.f.d.mts +42 -0
  153. package/fjs/media/json/extended/proof.f.mjs +127 -0
  154. package/fjs/media/json/extended/types.d.ts +23 -0
  155. package/fjs/media/json/module.f.d.mts +8 -2
  156. package/fjs/media/json/module.f.mjs +43 -41
  157. package/fjs/media/json/number/module.f.d.mts +59 -0
  158. package/fjs/media/json/number/module.f.mjs +136 -0
  159. package/fjs/media/json/number/proof.f.d.mts +24 -0
  160. package/fjs/media/json/number/proof.f.mjs +86 -0
  161. package/fjs/media/json/number/types.d.ts +28 -0
  162. package/fjs/media/json/parser/module.f.d.mts +25 -13
  163. package/fjs/media/json/parser/module.f.mjs +114 -70
  164. package/fjs/media/json/parser/proof.f.d.mts +5 -0
  165. package/fjs/media/json/parser/proof.f.mjs +31 -1
  166. package/fjs/media/json/parser/types.d.ts +33 -14
  167. package/fjs/media/json/rtti/module.f.d.mts +1 -1
  168. package/fjs/media/json/rtti/module.f.mjs +1 -1
  169. package/fjs/media/json/rtti/proof.f.mjs +9 -9
  170. package/fjs/media/json/schema/module.f.mjs +3 -13
  171. package/fjs/media/json/schema/proof.f.d.mts +0 -1
  172. package/fjs/media/json/schema/proof.f.mjs +1 -2
  173. package/fjs/media/json/serializer/module.f.d.mts +32 -1
  174. package/fjs/media/json/serializer/module.f.mjs +64 -2
  175. package/fjs/media/json/tokenizer/module.f.mjs +7 -3
  176. package/fjs/media/json/tokenizer/proof.f.d.mts +6 -0
  177. package/fjs/media/json/tokenizer/proof.f.mjs +62 -21
  178. package/fjs/media/json/types.d.ts +36 -10
  179. package/fjs/media/lock/module.f.d.mts +100 -0
  180. package/fjs/media/lock/module.f.mjs +125 -0
  181. package/fjs/media/lock/proof.f.d.mts +33 -0
  182. package/fjs/media/lock/proof.f.mjs +196 -0
  183. package/fjs/media/lock/types.d.ts +15 -0
  184. package/fjs/media/module.f.d.mts +6 -5
  185. package/fjs/media/module.f.mjs +8 -7
  186. package/fjs/media/note/module.f.d.mts +121 -0
  187. package/fjs/media/note/module.f.mjs +131 -0
  188. package/fjs/media/note/proof.f.d.mts +29 -0
  189. package/fjs/media/note/proof.f.mjs +150 -0
  190. package/fjs/media/note/types.d.ts +10 -0
  191. package/fjs/media/proof.f.d.mts +4 -1
  192. package/fjs/media/proof.f.mjs +40 -21
  193. package/fjs/media/revision/module.f.d.mts +78 -7
  194. package/fjs/media/revision/module.f.mjs +119 -12
  195. package/fjs/media/revision/proof.f.d.mts +10 -0
  196. package/fjs/media/revision/proof.f.mjs +88 -0
  197. package/fjs/media/revision/types.d.ts +34 -5
  198. package/fjs/media/type/module.f.d.mts +33 -15
  199. package/fjs/media/type/module.f.mjs +35 -29
  200. package/fjs/media/type/proof.f.d.mts +2 -1
  201. package/fjs/media/type/proof.f.mjs +30 -10
  202. package/fjs/module.f.mjs +29 -8
  203. package/fjs/nanvm/proof.f.mjs +3 -3
  204. package/fjs/nanvm/rust/module.f.mjs +1 -1
  205. package/fjs/nanvm/update/module.f.d.mts +4 -4
  206. package/fjs/nanvm/update/module.f.mjs +8 -9
  207. package/fjs/nanvm/update/proof.f.mjs +4 -3
  208. package/fjs/proof.f.d.mts +3 -3
  209. package/fjs/proof.f.mjs +36 -10
  210. package/fjs/protocol/json_rpc/module.f.d.mts +2 -2
  211. package/fjs/protocol/json_rpc/module.f.mjs +3 -3
  212. package/fjs/protocol/json_rpc/proof.f.mjs +4 -4
  213. package/fjs/protocol/mcp/module.f.d.mts +10 -13
  214. package/fjs/protocol/mcp/module.f.mjs +74 -58
  215. package/fjs/protocol/mcp/proof.f.d.mts +13 -4
  216. package/fjs/protocol/mcp/proof.f.mjs +193 -61
  217. package/fjs/protocol/mcp/stdio/module.f.d.mts +15 -7
  218. package/fjs/protocol/mcp/stdio/module.f.mjs +38 -25
  219. package/fjs/protocol/mcp/stdio/proof.f.d.mts +4 -2
  220. package/fjs/protocol/mcp/stdio/proof.f.mjs +45 -12
  221. package/fjs/protocol/mcp/stdio/types.d.ts +1 -1
  222. package/fjs/protocol/mcp/types.d.ts +17 -6
  223. package/fjs/sul/id/module.f.d.mts +0 -1
  224. package/fjs/sul/id/module.f.mjs +2 -3
  225. package/fjs/sul/level/hash/module.f.mjs +2 -1
  226. package/fjs/sul/level/hash/proof.f.mjs +2 -2
  227. package/fjs/sul/module.f.mjs +18 -13
  228. package/fjs/text/code_point/module.f.d.mts +8 -0
  229. package/fjs/text/code_point/module.f.mjs +8 -1
  230. package/fjs/text/code_point/proof.f.d.mts +1 -0
  231. package/fjs/text/code_point/proof.f.mjs +11 -0
  232. package/fjs/text/sgr/module.f.d.mts +4 -6
  233. package/fjs/text/sgr/module.f.mjs +6 -7
  234. package/fjs/text/utf16/module.f.mjs +7 -2
  235. package/fjs/text/utf16/proof.f.mjs +3 -3
  236. package/fjs/text/utf8/module.f.mjs +4 -2
  237. package/fjs/types/array/module.f.mjs +14 -2
  238. package/fjs/types/bit_vec/module.f.d.mts +0 -2
  239. package/fjs/types/bit_vec/module.f.mjs +46 -32
  240. package/fjs/types/bit_vec/proof.f.mjs +2 -2
  241. package/fjs/types/btree/remove/module.f.mjs +1 -1
  242. package/fjs/types/btree/set/module.f.mjs +9 -11
  243. package/fjs/types/btree/set/proof.f.mjs +12 -0
  244. package/fjs/types/byte_set/module.f.d.mts +11 -4
  245. package/fjs/types/byte_set/module.f.mjs +14 -6
  246. package/fjs/types/byte_set/proof.f.mjs +7 -7
  247. package/fjs/types/function/compare/module.f.mjs +10 -3
  248. package/fjs/types/list/module.f.d.mts +1 -1
  249. package/fjs/types/list/module.f.mjs +1 -1
  250. package/fjs/types/nullable/module.f.d.mts +18 -4
  251. package/fjs/types/nullable/module.f.mjs +21 -4
  252. package/fjs/types/nullable/proof.f.d.mts +4 -0
  253. package/fjs/types/nullable/proof.f.mjs +15 -0
  254. package/fjs/types/object/module.f.d.mts +12 -2
  255. package/fjs/types/object/module.f.mjs +11 -1
  256. package/fjs/types/patricia_trie/module.f.mjs +26 -11
  257. package/fjs/types/result/module.f.d.mts +3 -3
  258. package/fjs/types/result/module.f.mjs +3 -3
  259. package/fjs/types/rtti/common/module.f.d.mts +25 -28
  260. package/fjs/types/rtti/common/module.f.mjs +36 -32
  261. package/fjs/types/rtti/common/proof.f.mjs +5 -5
  262. package/fjs/types/rtti/data/module.f.d.mts +14 -0
  263. package/fjs/types/rtti/data/module.f.mjs +25 -2
  264. package/fjs/types/rtti/data/proof.f.d.mts +1 -0
  265. package/fjs/types/rtti/data/proof.f.mjs +25 -2
  266. package/fjs/types/rtti/parse/module.f.d.mts +24 -14
  267. package/fjs/types/rtti/parse/module.f.mjs +37 -28
  268. package/fjs/types/rtti/parse/proof.f.d.mts +3 -2
  269. package/fjs/types/rtti/parse/proof.f.mjs +33 -14
  270. package/fjs/types/rtti/proof.f.mjs +3 -1
  271. package/fjs/types/rtti/ts/module.f.mjs +13 -17
  272. package/fjs/types/rtti/ts/types.d.ts +14 -1
  273. package/fjs/types/rtti/validate/module.f.d.mts +81 -19
  274. package/fjs/types/rtti/validate/module.f.mjs +107 -61
  275. package/fjs/types/rtti/validate/proof.f.d.mts +13 -10
  276. package/fjs/types/rtti/validate/proof.f.mjs +186 -197
  277. package/fjs/types/sorted_set/module.f.d.mts +18 -0
  278. package/fjs/types/sorted_set/module.f.mjs +22 -0
  279. package/fjs/types/sorted_set/proof.f.d.mts +1 -0
  280. package/fjs/types/sorted_set/proof.f.mjs +16 -1
  281. package/fjs/types/uint8array/module.f.d.mts +1 -1
  282. package/fjs/types/uint8array/module.f.mjs +1 -1
  283. package/fjs/website/module.f.d.mts +3 -3
  284. package/fjs/website/module.f.mjs +4 -7
  285. package/fjs/website/proof.f.mjs +2 -1
  286. package/package.json +2 -2
  287. package/fjs/dev/package_json/module.f.d.mts +0 -39
  288. package/fjs/dev/package_json/module.f.mjs +0 -40
  289. package/fjs/dev/package_json/proof.f.d.mts +0 -6
  290. package/fjs/dev/package_json/proof.f.mjs +0 -32
  291. package/fjs/effects/eff/module.f.d.mts +0 -20
  292. package/fjs/effects/eff/module.f.mjs +0 -70
  293. package/fjs/effects/eff/proof.f.d.mts +0 -15
  294. package/fjs/effects/eff/proof.f.mjs +0 -69
  295. package/fjs/effects/eff/types.d.ts +0 -71
  296. package/fjs/types/rtti/validate/types.d.ts +0 -6
@@ -1,23 +1,69 @@
1
1
  /**
2
- * Core effect type constructors and combinators.
3
- *
4
- * An `Effect<O, T>` **is** the raw value — a `Pure` thunk (`() => T`) or a `Do`
5
- * node (`{ command, payload, continuation }`). It is plain data with no methods.
6
- * Composition is provided externally by {@link step}. The optional
7
- * method-chaining wrapper lives in `fjs/effects/eff/module.f.mjs`.
8
- *
9
- * **Three functions discriminate `Pure` from `Do`** — {@link step},
10
- * {@link match}, and {@link runPure} — plus the node proof in
11
- * `fjs/effects/proof.f.mjs` that pins the representation on purpose. Everything
12
- * else, interpreters included, goes through `match` or `runPure`. The count is
13
- * the point: a `typeof e === 'function'` check appearing in a fifth place is a
14
- * review flag, because the representation is only cheap to change while its
15
- * readers stay enumerable.
16
- *
17
- * A `decode` function (`(e: Effect<O, T>) => Decoded<O, T>`) once funnelled all
18
- * of that through a single `{ done, result }` / `{ done, command, payload,
2
+ * The effect system: the representation, its interpreters, and the combinators
3
+ * that compose it.
4
+ *
5
+ * An `Effect<O, T, E>` **is** the raw value a `Pure` thunk
6
+ * (`() => Result<T, E>`) or a `Do` node (`{ command, payload, continuation }`).
7
+ * It is plain data with no methods; see [`./types.ts`](./types.ts) for the
8
+ * type-level API and [`./README.md`](./README.md) for why the error channel is
9
+ * part of it.
10
+ *
11
+ * **The three branch-aware operations are the whole composition vocabulary:**
12
+ *
13
+ * - {@link step} continue on `ok`, propagate the `error`. The normal path.
14
+ * - {@link catchStep} continue on `error`, preserve the `ok`. The error path.
15
+ * - {@link resultStep} — continue with the complete `Result`. Both paths.
16
+ *
17
+ * Around them: {@link pure} and the two lifts {@link pureOk} / {@link pureError}
18
+ * that enter the layer, the projections {@link mapStep} and
19
+ * {@link resultMapStep} that end a chain, {@link history} /
20
+ * {@link historyStep} for a chain whose later links read earlier values, and
21
+ * {@link foldStep} / {@link forEachStep} for iteration. The operation
22
+ * constructor is {@link do_}, and the eliminators are {@link match},
23
+ * {@link partialMatch} and {@link runPure}.
24
+ *
25
+ * **The error channel is unioned, not unified**, following `okThen`
26
+ * (`fjs/types/result/module.f.mjs`), the pure sibling of this bind: neither
27
+ * side is pre-widened, and a branch that is passed through stays the very tuple
28
+ * it arrived as rather than being rebuilt to retag it into a wider type.
29
+ * {@link step} unions the error channel and replaces the success type;
30
+ * {@link catchStep} mirrors it, unioning the success channel and replacing the
31
+ * error type.
32
+ *
33
+ * Recovery therefore never needs `try`/`catch` — which FunctionalScript does
34
+ * not offer, and whose `throw` stays reserved for panics. Escalating an error
35
+ * to a panic is a decision the program makes explicitly, by `unwrap`ping what
36
+ * {@link resultStep} hands it.
37
+ *
38
+ * **This was two modules, and briefly two of each combinator.** A `./io/`
39
+ * directory held the branch-aware set while this file held a `Result`-blind
40
+ * `step`, `mapStep`, `history`, `historyStep`, `foldStep` and `forEachStep`
41
+ * beside the representation. Those were traps rather than a layer: an operation
42
+ * must return a `Result`, so every effect carries one, and a `step` that ran
43
+ * its continuation whether or not the previous effect failed described no case
44
+ * worth having. They are gone — {@link resultStep} *is* that former general
45
+ * `step`, at the type that says what its continuation receives — and with the
46
+ * name collision went the only reason for two modules. The directory was called
47
+ * `io` because it was the fallible layer over a raw one; nothing in it was ever
48
+ * specific to IO.
49
+ *
50
+ * **Three functions discriminate `Pure` from `Do`** — {@link resultStep},
51
+ * {@link _matchWith} and {@link runPure} — plus the node proof in
52
+ * `./proof.f.mjs` that pins the representation on purpose. Everything else,
53
+ * interpreters included, goes through `match`, {@link partialMatch}, or
54
+ * `runPure`. The count is the point: a `typeof e === 'function'` check
55
+ * appearing in a fourth place is a review flag, because the representation is
56
+ * only cheap to change while its readers stay enumerable. It used to be a
57
+ * cross-module claim, with one of the three on the far side of the boundary.
58
+ *
59
+ * `match` and `partialMatch` are two entry points, not two discriminators:
60
+ * they differ only in what a missing handler means and share `_matchWith` for
61
+ * the shape test, so adding the partial variant left the count where it was.
62
+ *
63
+ * A `decode` function (`(e: Effect<O, T, E>) => Decoded<O, T, E>`) once funnelled
64
+ * all of that through a single `{ done, result }` / `{ done, command, payload,
19
65
  * continuation }` record, so that exactly one function held the shape test. It
20
- * has been removed. `Effect` is a function type unioned with an object type, so
66
+ * has been removed. An effect is a function type unioned with an object type, so
21
67
  * `typeof e === 'function'` is already a complete discriminant: `decode` bought
22
68
  * no narrowing, it re-encoded that narrowing as a `done` flag to be re-narrowed
23
69
  * one indirection later, and its `Decoded` record was declared in terms of the
@@ -26,358 +72,550 @@
26
72
  * the same nothing — and with {@link Do} now carrying named fields there is not
27
73
  * even a positional layout left for it to insulate anyone from.
28
74
  *
29
- * Effect helpers come in two shapes. **Step adapters** return a continuation
30
- * `(t: T) => Effect<Q, R>` meant to be passed into a step see {@link okStep}.
31
- * **Step variants** take the effect itself first, like {@link step} — see
32
- * {@link historyStep}. {@link mapStep} is the variant for the end of a chain:
33
- * a pure projection over an effect's result, which is a `step` that continues
34
- * with no further effect.
35
- *
36
- * **Do not nest steps.** Bind each intermediate effect to its own name, so a
37
- * sequence reads top-to-bottom in evaluation order:
38
- *
39
- * ```js
40
- * // avoid — reads inside-out, and gains a level of indentation per link
41
- * step(a, x => step(f(x), y => step(g(y), z => h(z))))
42
- *
43
- * // prefer — flat, one name per link
44
- * const x0 = step(a, f)
45
- * const x1 = step(x0, g)
46
- * return step(x1, h)
47
- * ```
48
- *
49
- * **A step call that does not fit one line breaks after `(`, one argument per
50
- * line.** This holds for every step variant — {@link step}, {@link mapStep},
51
- * {@link historyStep}, {@link foldStep}, {@link forEachStep} — and it is the
52
- * same rule as taking the effect first, written out at the call site. A step
53
- * variant is this module's
54
- * `do` notation: the arguments are a statement list in execution order, so each
55
- * one gets a line and the sequence reads down the page. Packing the leading
56
- * effect onto the `(` line and wrapping the rest beneath it hides which of them
57
- * runs first. The closing `)` may sit on its own line or trail the last
58
- * argument:
59
- *
60
- * ```js
61
- * return step(
62
- * collectRead(cas.read(hash)),
63
- * ([tag, value]) => pure(tag === 'error' ? null : decodeRevisionVec(value)))
64
- * ```
65
- *
66
- * When a later link needs a value from an earlier one, that is not a reason to
67
- * nest: a nested continuation only reaches back because it closes over the
68
- * enclosing scope. {@link historyStep} carries the value forward instead, so
69
- * the chain stays flat:
70
- *
71
- * ```js
72
- * // avoid — nested only so `h` can still see `x`
73
- * step(a, x => step(f(x), y => h(x, y)))
74
- *
75
- * // prefer — the history tuple carries `x` forward alongside `y`
76
- * const x0 = historyStep(history(a), f)
77
- * return step(x0, ([y, x]) => h(x, y))
78
- * ```
79
- *
80
- * Nesting is often forced by nothing more than a local declared inside a
81
- * continuation that does not depend on it. Hoist such locals above the chain
82
- * and the nesting usually dissolves on its own.
83
- *
84
- * That advice is for code *using* this module, and the combinators defined
85
- * here are what make it followable. The nesting has to exist somewhere: a name
86
- * cannot be bound to an effect that has not been produced yet, so `f(param)`
87
- * cannot become a `const` until `e` resolves. {@link step} recurses into
88
- * itself inside the continuation it rebuilds, {@link foldStep} composes one
89
- * step per item, and {@link historyStep} runs `f` inside `e`'s continuation.
90
- * Each writes that nesting down **once**, in one line, so that no caller ever
91
- * writes it again — that is what a combinator here is *for*. Without
92
- * {@link historyStep} the flat form would be unavailable the moment a later
93
- * link needed an earlier link's value.
94
- *
95
- * See `./types.ts` for the type-level API.
75
+ * **The composition rules:** bind each link in a sequence to its own name at one
76
+ * level, do not nest steps, and break a call that does not fit one line after
77
+ * `(` with one argument per line.
96
78
  *
97
79
  * @module
98
80
  *
99
81
  * @import { List } from '../types/list/types.ts'
82
+ * @import { Fold } from '../types/function/operator/types.ts'
100
83
  * @import { Option } from '../types/option/types.ts'
101
84
  * @import { Result } from '../types/result/types.ts'
102
- * @import { Fold } from '../types/function/operator/types.ts'
103
- * @import { Cont, Do, Effect, F, History, MatchResult, Operation, OperationMap, Param, Pr, Pure, Return, ToAsyncOperationMap } from './types.ts'
85
+ * @import { Commands, Effect, ErrOf, Func, MatchResult, NotImplemented, OkOf, Operation, OperationMap, PartialOperationMap } from './types.ts'
104
86
  */
105
87
 
106
88
  import { assert } from '../asserts/module.f.mjs'
107
89
  import { fold } from '../types/list/module.f.mjs'
90
+ import { error, mapOk, ok } from '../types/result/module.f.mjs'
108
91
  import { at } from '../types/object/module.f.mjs'
109
92
 
110
- /** @type {<T>(v: T) => Effect<never, T>} */
111
- export const pure = v => () => v
93
+ /**
94
+ * Lifts an already-computed {@link Result} into an effect that performs no
95
+ * command.
96
+ *
97
+ * It takes the `Result` rather than a bare value because that is what a `Pure`
98
+ * holds: the two channels are the representation's, so a constructor that took
99
+ * only a success would be `pureOk`, which is exactly what
100
+ * [`./module.f.mjs`](./module.f.mjs) exports alongside `pureError`. Reach
101
+ * for those; this one is for the `Result` you already have in your hand — a
102
+ * branch passing an incoming error through unchanged, or a runner's answer.
103
+ *
104
+ * **Both channels are read off the argument**, via {@link OkOf} / {@link ErrOf}
105
+ * rather than by matching `Result<T, E>` directly. Inference against the union
106
+ * cannot tell which half a one-sided argument belongs to — `pure(ok(v))` would
107
+ * infer the error channel as `T` as readily as `never` — so the halves are
108
+ * projected out of the concrete type instead. That is what makes `pureOk` land
109
+ * on `Effect<never, T, never>` and `pureError` on `Effect<never, never, E>`.
110
+ *
111
+ * @type {<R extends Result<unknown, unknown>>(r: R) => Effect<never, OkOf<R>, ErrOf<R>>}
112
+ */
113
+ export const pure = r =>
114
+ // The thunk *is* the effect. TypeScript cannot see that `R` and
115
+ // `Result<OkOf<R>, ErrOf<R>>` are the same type for a generic `R`, so the
116
+ // one place the representation is constructed says so by hand.
117
+ /** @type {any} */ (() => r)
112
118
 
113
119
  /**
114
- * Composes effects: run `e`, then continue with `f` applied to its result.
115
- * The data-first primitive — raw `Effect` in, raw `Effect` out. Chains as
116
- * `step(step(e, f), g)`.
120
+ * @type {<O extends Operation>(command: O[0]) => Func<O>}
121
+ */
122
+ export const do_ = command => (...payload) => ({ command, payload, continuation: pure })
123
+
124
+ /**
125
+ * Runs an effect that reaches its result without performing a command: `[r]`
126
+ * for a {@link Pure}, empty for a {@link Do}. Forces the thunk in the `Pure`
127
+ * case, which {@link Pure}'s contract makes free of consequence.
117
128
  *
118
- * **`step` is not lazy.** It reads `e`'s shape immediately, so a `Pure` head is
119
- * forced and `f` is called right there: `step(pure(v), f)` *is* `f(v)`,
120
- * evaluated where the composition is written rather than where the effect is
121
- * run. Only the `Do` case defers — the continuation rebuilt around `f` runs
122
- * when a runner reaches that node.
129
+ * The eliminator for callers that expect no operations at all the other side
130
+ * of {@link match}, which is for callers that intend to perform them.
123
131
  *
124
- * That is sound rather than an oversight, and it is sound only because of
125
- * {@link Pure}'s contract: a `Pure` holds a value that has already been
126
- * computed, so forcing it early observes nothing, repeats nothing, and can
127
- * throw nothing. `step` never performs a `Do` node, which is where anything
128
- * real lives. Break the contracthide work behind the thunk and merely
129
- * composing a chain starts running the program.
132
+ * **The result is tagged on purpose**, and the tag is not the `Result`'s. An
133
+ * `Option` distinguishes "reached a value" from "stopped at a command";
134
+ * the `Result` inside it distinguishes success from failure. Collapsing them
135
+ * would lose the case this exists to rule out an effect that unexpectedly
136
+ * stopped at a `Do` nodeso `[r]` is a pure result and `[]` is a `Do`.
130
137
  *
131
- * A composition cannot be suspended, and no combinator can fix that:
132
- * `defer: (() => Effect<O, T>) => Effect<O, T>` cannot be written here, because
133
- * the `Pure` / `Do` tag must be known before anything runs and the union has no
134
- * third case meaning "not yet decided". That is inherent to the representation,
135
- * not a gap in this module's API. A caller that needs to name a composition
136
- * without performing it yet has to keep the ingredients and defer the `step`
137
- * itself — `Eff` does exactly this, holding its history tuple as a thunk (`h`)
138
- * precisely because composing it eagerly is the one thing it cannot take back.
138
+ * `O` stays generic rather than narrowing to `Effect<never, T, E>`. An effect is
139
+ * covariant in `O`, so `Effect<never, T, E>` is assignable to `Effect<O, T, E>`
140
+ * and not the reverse a continuation's result is always the wider type and
141
+ * would be rejected. `Do<never, T, E>` is uninhabited besides, which would make
142
+ * the empty case unreachable without a cast.
139
143
  *
140
- * @type {<O extends Operation, T, Q extends Operation, R>(e: Effect<O, T>, f: (t: T) => Effect<Q, R>) => Effect<O | Q, R>}
144
+ * @type {<O extends Operation, T, E>(e: Effect<O, T, E>) => Option<Result<T, E>>}
141
145
  */
142
- export const step = (e, f) =>
143
- typeof e === 'function'
144
- ? f(e())
145
- : { ...e, continuation: x => step(e.continuation(x), f) }
146
+ export const runPure = e =>
147
+ typeof e === 'function' ? [e()] : []
146
148
 
147
149
  /**
148
- * Applies a pure function to an effect's result: the functor `map` of the
149
- * effect monad, and a {@link step} whose continuation performs nothing further.
150
- *
151
- * Prefer it over the `step(e, t => pure(f(t)))` it abbreviates. The two are the
152
- * same value, but they read as different things: a `step` announces another
153
- * link in a sequence of effects, and a trailing pure projection is not one —
154
- * it is where the sequence ends. Saying so in the combinator's name keeps the
155
- * "one name per link" shape of a chain honest about how many effects it runs.
156
- *
157
- * **The operation set does not widen.** The result is `Effect<O, R>`, not
158
- * `Effect<O | Q, R>`, because a pure projection issues no commands — nothing a
159
- * runner has to know how to interpret is added by `f`. That is what separates
160
- * this from `step`, beyond the shorter spelling.
161
- *
162
- * A constant variant (`constStep(e, v)`) is deliberately absent: `mapStep(e,
163
- * () => v)` already reads clearly, and it keeps `v`'s evaluation inside the
164
- * continuation where `step` puts it, rather than moving it to where the
165
- * composition is written.
166
- *
167
- * @type {<O extends Operation, T, R>(e: Effect<O, T>, f: (t: T) => R) => Effect<O, R>}
150
+ * Decodes an effect's next step and dispatches its command to `map`,
151
+ * returning either the final result or the operation's output `R` paired
152
+ * with the continuation. The one world-specific step — `await` for async
153
+ * runners, state threading for sync ones is left to the caller, so every
154
+ * interpreter loop is this skeleton plus a single eliminator line.
155
+ *
156
+ * **An interpreter sees the whole `Result` and must not short-circuit on it.**
157
+ * A runner answers a failed command through the *ordinary* continuation that
158
+ * is what makes `error(notImplemented)` recoverable — so the `done` payload
159
+ * here is `Result<T, E>` rather than the `ok` half. Separating the channels is
160
+ * the composition layer's job, one level up.
161
+ *
162
+ * **The handler is looked up with `at`, never with `map[command]`.**
163
+ * `OperationMap<O, R>` pins `command` to `O[0]` at the type level, but a `Do`
164
+ * node's `command` is runtime data — it can reach an interpreter from a decoded
165
+ * payload or a deserialized continuation, where no type ever constrained it.
166
+ * `map` is an ordinary object, so a plain index read resolves an inherited name
167
+ * (`'constructor'`, `'toString'`, `'hasOwnProperty'`) to the `Object.prototype`
168
+ * member instead of `undefined`, and the line below would then call it with the
169
+ * node's payload: a value the type system promised was `(...payload) => R` turns
170
+ * out to be an arbitrary inherited function, chosen by the same input that
171
+ * supplies its arguments. `at` reads through `getOwnPropertyDescriptor`, which
172
+ * only ever sees own properties, so such a command yields `null` and never a
173
+ * callable.
174
+ *
175
+ * **A `null` handler means one of two different things**, and this is the only
176
+ * place that can still tell them apart. With a total {@link OperationMap} every
177
+ * `O1 extends O` the signature admits has its command in `map`, so a miss means
178
+ * the node's `command` was never the `O1[0]` it claimed to be — a malformed
179
+ * node, and a panic. With a {@link PartialOperationMap} a miss may instead be an
180
+ * operation the runner deliberately does not implement, which is an *outcome*: a
181
+ * program receives `error(notImplemented)` through the ordinary continuation and
182
+ * decides for itself whether to recover, fall back, or panic. {@link partialMatch}
183
+ * is the variant that distinguishes the two; this one keeps the strict reading,
184
+ * because a total map has no second case to distinguish.
185
+ *
186
+ * The two share {@link _matchWith}, so `typeof e === 'function'` still appears
187
+ * in exactly the three places the module header names.
188
+ *
189
+ * @template {Operation} O
190
+ * @template R
191
+ * @param {OperationMap<O, R>} map
168
192
  */
169
- export const mapStep = (e, f) =>
170
- step(e, t => pure(f(t)))
193
+ export const match = map =>
194
+ _matchWith(/** @type {(command: O[0]) => R} */ (command => {
195
+ assert(false, command)
196
+ }))(map)
171
197
 
172
198
  /**
173
- * Like {@link step}, but keeps the values instead of discarding them: runs `e`
174
- * to get the history `p`, continues with `f(...p)` to get `r`, and yields
175
- * `[r, ...p]` the same history with `r` prepended.
199
+ * {@link match} for a runner that is *meant* to lack operations.
200
+ *
201
+ * A command in `commands` with no handler in `map` is a capability this runner
202
+ * does not have: `onMissing` builds the answer and the program resumes with it,
203
+ * which is what lets `O` mean "the operations a computation may request" rather
204
+ * than "the operations every runner implements". A command outside `commands`
205
+ * is still a malformed node and still panics — an omitted handler and a garbled
206
+ * `command` are not the same failure, and collapsing them would turn a probable
207
+ * bug into a routine outcome.
208
+ *
209
+ * **`onMissing` is supplied by the caller because only the caller can build an
210
+ * `R`.** `R` is the *runner's* wrapper — `Promise<…>` for an async loop,
211
+ * `(state) => [state, …]` for a state-threading one — not the operation's
212
+ * return type, so this function has no way to construct one. Each runner writes
213
+ * the injector once, next to the loop that defines the shape.
176
214
  *
177
- * This is what a chain of named intermediate effects cannot otherwise express.
178
- * Each `step`'s continuation sees only the result of the effect it consumes, so
179
- * a later link has no way to reach an earlier one. `historyStep` carries every
180
- * earlier value forward, and the next destructuring names the parts:
215
+ * @template {Operation} O
216
+ * @template R
217
+ * @param {Commands<O>} commands
218
+ * @param {(command: O[0]) => R} onMissing
219
+ */
220
+ export const partialMatch = (commands, onMissing) =>
221
+ _matchWith((/** @type {O[0]} */ command) => {
222
+ assert(commands.includes(command), command)
223
+ return onMissing(command)
224
+ })
225
+
226
+ /**
227
+ * The shared body of {@link match} and {@link partialMatch}: decode the node,
228
+ * look the handler up by own property, and hand a miss to `onMissing`.
181
229
  *
182
- * ```js
183
- * const b = historyStep(history(a), decodeRevisionBlob(cas))
184
- * const c = step(b, ([revision, hash]) => ...)
185
- * ```
230
+ * @template {Operation} O
231
+ * @template R
232
+ * @param {(command: O[0]) => R} onMissing
233
+ */
234
+ const _matchWith = onMissing =>
235
+ /** @param {PartialOperationMap<O, R>} map */
236
+ map =>
237
+ /**
238
+ * @template {O} O1
239
+ * @template T
240
+ * @template E
241
+ * @param {Effect<O1, T, E>} e
242
+ * @returns {MatchResult<O1, T, E, R>}
243
+ */
244
+ e => {
245
+ if (typeof e === 'function') { return ['done', e()] }
246
+ const { command, payload, continuation } = e
247
+ const handler = at(command)(map)
248
+ return [
249
+ 'cont',
250
+ handler === null ? onMissing(command) : handler(...payload),
251
+ continuation,
252
+ ]
253
+ }
254
+
255
+ /**
256
+ * Lifts a value into a successful `Effect` — `pure(ok(v))` written once.
186
257
  *
187
- * Chaining mimics an async function, one `await` per link `const hash = ...`
188
- * then `const revision = ...`, with both still reachable at the end. It takes a
189
- * history and returns one, so it composes with itself to any depth; only the
190
- * entry point needs {@link history}:
258
+ * One of the two entry points into the layer: {@link step} and its siblings
259
+ * compose `Effect`s but cannot produce the first one, and the only other source
260
+ * is an operation's own `Result`.
191
261
  *
192
- * ```js
193
- * const h0 = history(readHash(cas))
194
- * const h1 = historyStep(h0, hash => decodeRevisionBlob(cas)(hash))
195
- * const h2 = historyStep(h1, (revision, hash) => ...)
196
- * ```
262
+ * The error channel is `never`, which is not a special case to handle but the
263
+ * ordinary consequence of the union rules: `never | E` is `E`, so a lifted
264
+ * value composes with any chain without widening its errors.
197
265
  *
198
- * **Newest first.** A position is distance back from the current link, not
199
- * evaluation order, so a destructuring reads reverse-chronologically:
200
- * `([z, y, x]) => ...` binds `x` earliest. Reaching further back costs an index
201
- * rather than a traversal, but a long chain makes the positions hard to count.
202
- * When that starts to hurt, collapse it into a record of named fields
203
- * (`pure({ hash, revision } as const)`) and start a fresh history from there.
204
- *
205
- * `Readonly<P>` on `f`'s rest parameter is load-bearing: inferring `P` from a
206
- * bare rest parameter yields a *mutable*, labelled tuple (`[next: string]`),
207
- * which then rejects the `readonly` tuples every history is built from.
208
- *
209
- * @type {<O extends Operation, P extends readonly unknown[], Q extends Operation, R>(
210
- * e: History<O, P>,
211
- * f: (...p: Readonly<P>) => Effect<Q, R>
212
- * ) => History<O | Q, readonly[R, ...P]>}
266
+ * @type {<T>(v: T) => Effect<never, T, never>}
213
267
  */
214
- export const historyStep = (e, f) =>
215
- step(e, param => step(f(...param), result => pure([result, ...param])))
268
+ export const pureOk = v => pure(ok(v))
216
269
 
217
270
  /**
218
- * Starts a history, lifting a plain result into a one-element tuple so that
219
- * {@link historyStep} can extend it.
271
+ * Lifts an error into a failed `Effect` `pure(error(e))` written once, and
272
+ * the mirror of {@link pureOk}, with the success channel `never` instead.
220
273
  *
221
- * Creating a history is the *only* thing this does every later link goes
222
- * through `historyStep`, which is what lets one combinator cover chains of any
223
- * length. Fusing the two (a step that both starts and extends) is what makes
224
- * chains stop composing: such a step nests its predecessor's tuple instead of
225
- * flattening it, so link two would have to be spelled differently from link
226
- * three.
274
+ * This is how a program *originates* a failure: a fallback that has run out of
275
+ * options, or a guard that rejects its input before performing anything. A
276
+ * runner producing `error(notImplemented)` does not go through here that
277
+ * error arrives through an operation's own continuation.
227
278
  *
228
- * @type {<O extends Operation, T>(e: Effect<O, T>) => History<O, readonly[T]>}
279
+ * @type {<E>(e: E) => Effect<never, never, E>}
229
280
  */
230
- export const history = e =>
231
- step(e, v => pure([v]))
281
+ export const pureError = e => pure(error(e))
232
282
 
233
283
  /**
234
- * @type {<O extends Operation>(command: O[0]) => (...payload: Param<O>) => Effect<O, Return<O>>}
284
+ * Builds the {@link NotImplemented} a runner answers with when it cannot
285
+ * dispatch `command`.
286
+ *
287
+ * It names the command and nothing else. A `Do` node's payload may hold
288
+ * functions — `createServer`'s listener, `sandbox`'s thunk, `test`'s body — so
289
+ * carrying it would break the serializability this error type promises, and the
290
+ * command name is the part a program can act on anyway.
291
+ *
292
+ * @type {(command: string) => NotImplemented}
235
293
  */
236
- export const do_ = command => (...payload) => ({ command, payload, continuation: pure })
294
+ export const notImplemented = command => ['notImplemented', command]
237
295
 
238
296
  /**
239
- * Sequentially threads a state value through an effect for each item produced by
240
- * `items`.
297
+ * The normal path: run `e`, and continue with `f` **only** if it succeeded. An
298
+ * `error` short-circuits the rest of the chain and is passed through unchanged.
241
299
  *
242
- * Given `f: item => state => Effect<Q, state>`, `init: S`, and an `items` that
243
- * yields `[x₀, x₁, …]`, builds `step(step(f(x₀)(init), f(x₁)), f(x₂))…` and
244
- * yields a single effect producing the final state.
300
+ * This is the default error propagation the migration exists to provide the
301
+ * structured replacement for exception-style propagation, analogous to Rust's
302
+ * `?`. A sequence therefore reads as its success path, and mentions errors only
303
+ * where it intentionally handles them:
245
304
  *
246
- * Sequential — each step depends on the previous state. Compare to `all`,
247
- * which fans out independent effects.
305
+ * ```js
306
+ * const a = writeFile(...)
307
+ * const b = step(a, () => console('written'))
308
+ * ```
248
309
  *
249
- * **A step variant** (see the two shapes described in this module's header): the
250
- * effect comes first, as in {@link step} and {@link historyStep}. `items` is an
251
- * `Effect<O, List<T>>` rather than a bare `List<T>` because the list a caller
252
- * folds over is normally *produced* by an effect `cas.list()`, a `readdir`.
253
- * Taking the plain list would force every such caller to open a continuation
254
- * just to name the list (`step(cas.list(), foldStep(…))`), which is the nesting
255
- * this module exists to keep out of call sites. A caller that already holds the
256
- * list lifts it with `pure`, which costs a wrapper but no indentation.
310
+ * `'written'` is printed only when `writeFile` returned `ok`. Written with the
311
+ * `Result`-blind `step` this replaced, the same line printed it either way
312
+ * and note that it was the *value-discarding* continuation that hid the
313
+ * hazard, since one that read the value would not have compiled.
257
314
  *
258
- * `O` and `Q` are separate on purpose: the operations needed to produce the list
259
- * are rarely the ones the body performs, and the result unions them.
315
+ * **The error types are unioned** (`E | F`), so `f` may fail in its own way
316
+ * without either side being pre-widened; the operation sets union too, since
317
+ * `f` performs effects of its own.
260
318
  *
261
- * **The argument order is deliberately not `fold`'s** from `fjs/types/list`, and
262
- * the difference is what the two combinators are *for*. `fold` is a data
263
- * pipeline: it is curried `f`-first because the list is the thing being threaded
264
- * through, and nothing about it happens in time. A step variant is a sequencing
265
- * constructit is this module's `do` notation, and reading one top-to-bottom
266
- * is reading the order the program executes in. The effect therefore has to come
267
- * first, because it is what happens first. Currying `f` ahead of `items` would
268
- * put the *body* of the loop above the thing it loops over, which is exactly the
269
- * inversion `do` exists to remove.
319
+ * The body is {@link resultStep} over the one branch this layer is named for:
320
+ * an `error` is handed back as the very tuple it arrived as rather than rebuilt
321
+ * to retag it into a wider type, which is what makes `E | F` expressible instead
322
+ * of forcing both sides to one error type. The continuation is annotated for
323
+ * that reason its two branches have different types, and the annotation
324
+ * states the union they belong to rather than leaving the compiler to infer it
325
+ * from whichever it reads first.
270
326
  *
271
- * That is also why the whole family `step`, `historyStep`, `foldStep`,
272
- * `forEachStep` — takes its effect first and breaks one argument per line when
273
- * it wraps (see this module's header): every such call is a statement list, and
274
- * each line is one statement in execution order.
327
+ * This used to route through an `okStep` exported by the representation
328
+ * module. Nothing
329
+ * else ever called it: the adapter *was* this function's body, one indirection
330
+ * away, so it is written here now and that module has one export fewer.
275
331
  *
276
332
  * @template {Operation} O
277
333
  * @template T
334
+ * @template E
278
335
  * @template {Operation} Q
279
- * @template S
280
- * @param {Effect<O, List<T>>} items
281
- * @param {S} init
282
- * @param {(item: T) => (state: S) => Effect<Q, S>} f
283
- * @returns {Effect<O | Q, S>}
336
+ * @template R
337
+ * @template F
338
+ * @param {Effect<O, T, E>} e
339
+ * @param {(t: T) => Effect<Q, R, F>} f
340
+ * @returns {Effect<O | Q, R, E | F>}
284
341
  */
285
- export const foldStep = (items, init, f) => {
286
- /** @type {Fold<T, Effect<O | Q, S>>} */
287
- const op = item => acc => step(acc, f(item))
288
- return step(items, fold(op)(pure(init)))
342
+ export const step = (e, f) => {
343
+ /** @type {(r: Result<T, E>) => Effect<Q, R, E | F>} */
344
+ const cont = r => r[0] === 'error' ? pure(r) : f(r[1])
345
+ return resultStep(e, cont)
289
346
  }
290
347
 
291
348
  /**
292
- * Sequentially runs `f(item)` for each item produced by `items`, discarding
293
- * intermediate results. The `void` accumulator sibling of {@link foldStep}, and
294
- * a step variant on the same grounds.
349
+ * The error path: run `e`, and continue with `f` **only** if it failed. An `ok`
350
+ * is preserved unchanged, so the recovery is the only branch that mentions the
351
+ * failure.
295
352
  *
296
- * @type {<O extends Operation, T, Q extends Operation>(items: Effect<O, List<T>>, f: (item: T) => Effect<Q, void>) => Effect<O | Q, void>}
353
+ * The exact mirror of {@link step}: where that unions the error channel and
354
+ * replaces the success type, this unions the success channel (`T | R` — the
355
+ * preserved value or the recovery's) and replaces the error type with `f`'s.
356
+ * Recovering from every error therefore leaves `F` uninhabited, and the type
357
+ * says so.
358
+ *
359
+ * Use it for *intentional* recovery — a fallback operation after a
360
+ * `NotImplemented`, a default for a missing file — never as a blanket
361
+ * "continue anyway"; that is what {@link step}'s propagation already prevents.
362
+ *
363
+ * The local continuation is annotated for the same reason {@link step}'s is:
364
+ * its two branches have different types — `f`'s effect and the untouched `ok`
365
+ * tuple — and the annotation states the union they belong to instead of
366
+ * leaving the compiler to infer it from whichever branch it reads first.
367
+ *
368
+ * @template {Operation} O
369
+ * @template T
370
+ * @template E
371
+ * @template {Operation} Q
372
+ * @template R
373
+ * @template F
374
+ * @param {Effect<O, T, E>} e
375
+ * @param {(err: E) => Effect<Q, R, F>} f
376
+ * @returns {Effect<O | Q, T | R, F>}
297
377
  */
298
- export const forEachStep = (items, f) =>
299
- foldStep(items, undefined, item => () => f(item))
378
+ export const catchStep = (e, f) => {
379
+ /** @type {(r: Result<T, E>) => Effect<Q, T | R, F>} */
380
+ const cont = r => r[0] === 'error' ? f(r[1]) : pure(r)
381
+ return resultStep(e, cont)
382
+ }
300
383
 
301
384
  /**
302
- * A step adapter for the `error` short-circuit: `error` pass it through
303
- * unchanged as `pure`, `ok` continue with `f`. Collapses the hand-written
304
- * `r[0] === 'error' ? pure(r) : f(r[1])` check that recurs at every site
305
- * chaining `Effect<O, Result<T, E>>` steps.
385
+ * Both paths: run `e` and hand `f` the complete `Result`, which then decides
386
+ * what the outcome is. Use it where both branches genuinely matter — a report
387
+ * that records the failure, a retry policy, or the point where a program
388
+ * escalates an error to a panic by `unwrap`ping it.
389
+ *
390
+ * **This is the layer's primitive**, and {@link step} and {@link catchStep} are
391
+ * written in terms of it — each is this function with a continuation that
392
+ * inspects the tag first. It used to be the other way round: a `step` in the
393
+ * representation module composed effects with opaque payloads, and this was
394
+ * that function re-exported under a narrower type. Since every effect carries a
395
+ * `Result`, the opaque spelling described nothing the `Result`-shaped one does
396
+ * not, so the general function lives here now, at the type that says what its
397
+ * continuation receives.
398
+ *
399
+ * `finallyStep` is declined on the principle that a derivable form earns a name
400
+ * by being canonical vocabulary, and that one has not shown it is. It is
401
+ * `resultStep` plus a policy, and adds no expressive power until real consumers
402
+ * demonstrate a repeated policy worth naming.
403
+ *
404
+ * **It is not lazy.** It reads `e`'s shape immediately, so a `Pure` head is
405
+ * forced and `f` is called right there: `resultStep(pure(r), f)` *is* `f(r)`,
406
+ * evaluated where the composition is written rather than where the effect is
407
+ * run. Only the `Do` case defers — the continuation rebuilt around `f` runs
408
+ * when a runner reaches that node.
409
+ *
410
+ * That is sound rather than an oversight, and it is sound only because of
411
+ * `Pure`'s contract: a `Pure` holds a result that has already been computed, so
412
+ * forcing it early observes nothing, repeats nothing, and can throw nothing.
413
+ * Composing never performs a `Do` node, which is where anything real lives.
414
+ * Break the contract — hide work behind the thunk — and merely composing a
415
+ * chain starts running the program.
306
416
  *
307
- * @type {<T, E, O extends Operation, R>(f: (value: T) => Effect<O, Result<R, E>>) => (r: Result<T, E>) => Effect<O, Result<R, E>>}
417
+ * A composition cannot be suspended, and no combinator can fix that:
418
+ * `defer: (() => Effect<O, T, E>) => Effect<O, T, E>` cannot be written, because
419
+ * the `Pure` / `Do` tag must be known before anything runs and the union has no
420
+ * third case meaning "not yet decided". That is inherent to the representation,
421
+ * not a gap in this module's API. A caller that needs to name a composition
422
+ * without performing it yet has to keep the ingredients and defer the step
423
+ * itself.
424
+ *
425
+ * @type {<O extends Operation, T, E, Q extends Operation, R, F>(
426
+ * e: Effect<O, T, E>,
427
+ * f: (r: Result<T, E>) => Effect<Q, R, F>
428
+ * ) => Effect<O | Q, R, F>}
308
429
  */
309
- export const okStep = f => r =>
310
- r[0] === 'error' ? pure(r) : f(r[1])
430
+ export const resultStep = (e, f) =>
431
+ typeof e === 'function'
432
+ ? f(e())
433
+ : { ...e, continuation: x => resultStep(e.continuation(x), f) }
311
434
 
312
435
  /**
313
- * Runs an effect that reaches its value without performing a command: `[t]` for
314
- * a {@link Pure}, empty for a {@link Do}. Forces the thunk in the `Pure` case,
315
- * which {@link Pure}'s contract makes free of consequence.
436
+ * Applies a pure function to the `ok` value, passing an `error` through
437
+ * unchanged: the functor `map` of this layer, and a {@link step} whose
438
+ * continuation performs nothing further.
316
439
  *
317
- * The eliminator for callers that expect no operations at all the other side
318
- * of {@link match}, which is for callers that intend to perform them.
440
+ * A trailing pure projection is where a sequence *ends*, not another link in
441
+ * it, and spelling it as a step misreports how many effects a chain runs
442
+ * (`./todo/map-step-combinator.md`). Without this, every such site would
443
+ * regress to exactly that spelling, now with a `pureOk` inside it.
319
444
  *
320
- * **The result is tagged on purpose.** Returning `T | null` would collapse two
321
- * distinct outcomes whenever `T` itself admits `null`: `runPure(pure(null))` and
322
- * `runPure(someDo)` would both be `null`, so a caller asserting `null` would
323
- * accept an effect that unexpectedly stopped at a command — exactly the case
324
- * this exists to rule out. `Option<T>` keeps them apart: `[null]` is a pure
325
- * `null`, `[]` is a `Do`.
445
+ * **The operation set does not widen**: a pure projection issues no commands. Neither does the error channel — `f` cannot
446
+ * fail, so a chain that only projects its value keeps the errors it already
447
+ * had.
326
448
  *
327
- * `O` stays generic rather than narrowing to `Effect<never, T>`. `Effect` is
328
- * covariant in `O`, so `Effect<never, T>` is assignable to `Effect<O, T>` and
329
- * not the reverse a continuation's result is always the wider type and would
330
- * be rejected. `Do<never, T>` is uninhabited besides, which would make the empty
331
- * case unreachable without a cast.
449
+ * @type {<O extends Operation, T, E, R>(e: Effect<O, T, E>, f: (t: T) => R) => Effect<O, R, E>}
450
+ */
451
+ export const mapStep = (e, f) => resultMapStep(e, mapOk(f))
452
+
453
+ /**
454
+ * Applies a pure function to the whole {@link Result}: the both-branches
455
+ * sibling of {@link mapStep}, and the {@link resultStep} whose continuation
456
+ * performs nothing further.
457
+ *
458
+ * Reach for it where a projection genuinely decides the outcome rather than
459
+ * transforming a value — turning any answer into a fixed one, replacing a
460
+ * channel wholesale, re-tagging a failure. Where only the success is being
461
+ * transformed, {@link mapStep} says so and leaves the channel alone.
462
+ *
463
+ * **Neither channel is preserved**, which is the difference that matters
464
+ * against `mapStep`: `f` returns a `Result<R, F>` of its own, so a caller can
465
+ * discard errors here. That is exactly what makes it the honest spelling for a
466
+ * site that means to — the discarding is written down, in a function that says
467
+ * it takes both branches, instead of being implied by a value-shaped `map` that
468
+ * quietly received a `Result`.
469
+ *
470
+ * Its two output channels are read off `f`'s return type with {@link OkOf} /
471
+ * {@link ErrOf}, for the reason `pure` gives: inference against the `Result`
472
+ * union cannot place a one-sided return, so a projection that always answers
473
+ * `ok` would otherwise acquire an error channel it never produces.
474
+ *
475
+ * @type {<O extends Operation, T, E, R extends Result<unknown, unknown>>(
476
+ * e: Effect<O, T, E>,
477
+ * f: (r: Result<T, E>) => R
478
+ * ) => Effect<O, OkOf<R>, ErrOf<R>>}
479
+ */
480
+ export const resultMapStep = (e, f) => resultStep(e, r => pure(f(r)))
481
+
482
+ /**
483
+ * Empties the error channel by **panicking** on it: `ok` values continue
484
+ * unchanged, an `error` is thrown as `summary(e)`, and what comes back is an
485
+ * `Effect<O, T, never>` — a `never` that is earned rather than asserted, since
486
+ * the only way past this point is success.
487
+ *
488
+ * This is the program exercising its right to treat a failure as fatal, and it
489
+ * is a policy — not a conversion. It belongs at a site that genuinely has no
490
+ * answer to the failure: a build tool that cannot read its own sources, a
491
+ * proof whose fixture is missing. Where a caller could do something else,
492
+ * {@link catchStep} or {@link resultStep} is the honest spelling, and a chain
493
+ * that merely passes the failure along wants {@link step}.
494
+ *
495
+ * **`summary` is what names the errors being panicked on, and it is required
496
+ * for that reason rather than for the message.** Without it this function was
497
+ * generic in `E` and therefore compiled however far a channel widened: one
498
+ * fallible read added upstream enlarged what every downstream call crashed on,
499
+ * silently. A renderer written for a particular channel cannot accept a wider
500
+ * one — parameters are contravariant — so widening becomes a compile error at
501
+ * the site that chose to panic, which is the site that has to choose again.
502
+ *
503
+ * The `IoChannel` renderer is `errorSummary` (`./node/module.f.mjs`); pass a
504
+ * narrower one where the channel is narrower. An inline `e => String(e)`
505
+ * accepts anything and gives the old behaviour back — that is an escape hatch,
506
+ * and being written out at the call site is the point.
507
+ *
508
+ * This being one greppable name still matters: every occurrence is a site that
509
+ * has chosen to panic, so the choice can be reviewed. What the name alone
510
+ * could not do is tell a reviewer that a site's *scope* had grown since they
511
+ * last looked at it, which is what the argument adds.
512
+ *
513
+ * @type {<O extends Operation, T, E>(e: Effect<O, T, E>, summary: (e: E) => string) => Effect<O, T, never>}
514
+ */
515
+ export const unwrapStep = (e, summary) => resultMapStep(e, r => {
516
+ if (r[0] === 'error') { throw summary(r[1]) }
517
+ return r
518
+ })
519
+
520
+ /**
521
+ * Starts a history from a fallible effect, lifting its `ok` value into a
522
+ * one-element tuple that {@link historyStep} extends — the entry point a chain
523
+ * needs exactly once.
332
524
  *
333
- * @type {<O extends Operation, T>(e: Effect<O, T>) => Option<T>}
525
+ * @type {<O extends Operation, T, E>(e: Effect<O, T, E>) => Effect<O, readonly[T], E>}
334
526
  */
335
- export const runPure = e =>
336
- typeof e === 'function' ? [e()] : []
527
+ export const history = e => mapStep(e, v => [v])
337
528
 
338
529
  /**
339
- * Decodes an effect's next step and dispatches its command to `map`,
340
- * returning either the final result or the operation's output `R` paired
341
- * with the continuation. The one world-specific step — `await` for async
342
- * runners, state threading for sync ones — is left to the caller, so every
343
- * interpreter loop is this skeleton plus a single eliminator line.
530
+ * Like {@link step}, but carries the values forward instead of discarding
531
+ * them: runs `e` for the history `p`, continues with `f(...p)` for `r`, and
532
+ * yields `[r, ...p]`.
533
+ *
534
+ * This is what keeps a **fallible** chain flat. Each `step`'s continuation sees
535
+ * only the value it consumes, so a later link cannot reach an earlier one, and
536
+ * the alternative — nesting so the inner continuation closes over the outer
537
+ * one's parameter — is what `fjs/AGENTS.md` §3.4 rules out. A `Result`-blind
538
+ * `historyStep` could not serve: it carried each link's `Result` into the tuple
539
+ * rather than its value, so every later link had to destructure results it had
540
+ * no intention of handling. That is why this one exists and that one does not.
541
+ *
542
+ * The history holds `ok` values only. An `error` short-circuits the chain, so a
543
+ * failed link contributes nothing to the tuple — which is the point: a later
544
+ * link reads earlier values without asking whether they are there.
344
545
  *
345
- * **The handler is looked up with `at`, never with `map[command]`.**
346
- * `OperationMap<O, R>` pins `command` to `O[0]` at the type level, but a `Do`
347
- * node's `command` is runtime data — it can reach an interpreter from a decoded
348
- * payload or a deserialized continuation, where no type ever constrained it.
349
- * `map` is an ordinary object, so a plain index read resolves an inherited name
350
- * (`'constructor'`, `'toString'`, `'hasOwnProperty'`) to the `Object.prototype`
351
- * member instead of `undefined`, and the line below would then call it with the
352
- * node's payload: a value the type system promised was `(...payload) => R` turns
353
- * out to be an arbitrary inherited function, chosen by the same input that
354
- * supplies its arguments. `at` reads through `getOwnPropertyDescriptor`, which
355
- * only ever sees own properties, so such a command yields `null` and never a
356
- * callable.
546
+ * @template {Operation} O
547
+ * @template {readonly unknown[]} P
548
+ * @template E
549
+ * @template {Operation} Q
550
+ * @template R
551
+ * @template F
552
+ * @param {Effect<O, P, E>} e
553
+ * @param {(...p: Readonly<P>) => Effect<Q, R, F>} f
554
+ * @returns {Effect<O | Q, readonly[R, ...P], E | F>}
555
+ */
556
+ export const historyStep = (e, f) => {
557
+ /** @type {(param: P) => Effect<Q, readonly[R, ...P], F>} */
558
+ const cont = param => mapStep(f(...param), result => [result, ...param])
559
+ return step(e, cont)
560
+ }
561
+
562
+ /**
563
+ * Threads a state through one effect per item, short-circuiting on the first
564
+ * `error`.
565
+ *
566
+ * **`items` is an `Effect` like everything else here.** Its payload was once
567
+ * unwrapped, on the argument that a held list has no error channel and
568
+ * requiring one would mean lifting at every call site. Lifting is one `pureOk`
569
+ * call, and that version made the *fallible* producer pay instead:
570
+ * `fjs/cas/cli` wrapped the whole fold in a `step` whose only job was to unwrap
571
+ * `list()` so `pure` could wrap it again. A producer that can fail now feeds
572
+ * the fold directly, and one that cannot is unaffected when it later can.
573
+ *
574
+ * **The argument order is deliberately not `fold`'s** from `fjs/types/list`, and
575
+ * the difference is what the two combinators are *for*. `fold` is a data
576
+ * pipeline: it is curried `f`-first because the list is the thing being threaded
577
+ * through, and nothing about it happens in time. A step variant is a sequencing
578
+ * construct — this module's `do` notation — and reading one top-to-bottom is
579
+ * reading the order the program executes in. The effect therefore has to come
580
+ * first, because it is what happens first. Currying `f` ahead of `items` would
581
+ * put the *body* of the loop above the thing it loops over, which is exactly the
582
+ * inversion `do` exists to remove.
357
583
  *
358
- * A `null` handler is an invariant violation, not an outcome: every `O1 extends
359
- * O` the signature admits has its command in `map`, so reaching it means the
360
- * node's `command` was never the `O1[0]` it claimed to be. It therefore throws
361
- * (`assert`) rather than widening {@link MatchResult} with a variant no
362
- * type-correct caller could ever observe — a runner cannot resume a command it
363
- * has no handler for, so there is nothing for a recovery branch to do.
584
+ * That is also why the whole family {@link step}, {@link historyStep},
585
+ * `foldStep`, {@link forEachStep} takes its effect first and breaks one
586
+ * argument per line when it wraps: every such call is a statement list, and each
587
+ * line is one statement in execution order.
364
588
  *
365
589
  * @template {Operation} O
366
- * @template R
367
- * @param {OperationMap<O, R>} map
590
+ * @template T
591
+ * @template {Operation} Q
592
+ * @template S
593
+ * @template E
594
+ * @param {Effect<O, List<T>, E>} items
595
+ * @param {S} init
596
+ * @param {(item: T) => (state: S) => Effect<Q, S, E>} f
597
+ * @returns {Effect<O | Q, S, E>}
368
598
  */
369
- export const match = map =>
370
- /**
371
- * @template {O} O1
372
- * @template T
373
- * @param {Effect<O1, T>} e
374
- * @returns {MatchResult<O1, T, R>}
375
- */
376
- e => {
377
- if (typeof e === 'function') { return ['done', e()] }
378
- const { command, payload, continuation } = e
379
- const handler = /** @type {(...payload: readonly unknown[]) => R} */
380
- (at(command)(/** @type {any} */ (map)))
381
- assert(handler !== null, command)
382
- return ['cont', handler(...payload), continuation]
383
- }
599
+ export const foldStep = (items, init, f) => {
600
+ /** @type {Fold<T, Effect<Q, S, E>>} */
601
+ const op = item => acc => step(acc, f(item))
602
+ return step(items, fold(op)(pureOk(init)))
603
+ }
604
+
605
+ /**
606
+ * Runs `f(item)` for each item in order, stopping at the first failure and
607
+ * propagating it. The `void` accumulator sibling of {@link foldStep}.
608
+ *
609
+ * Stopping is the difference against the `Result`-blind `forEachStep` this
610
+ * replaced: that one ran every item whatever each answered, because its `void`
611
+ * accumulator had nothing to carry a failure in — and TypeScript's `void`
612
+ * return position accepts a `Result`-valued effect silently, so the discard
613
+ * does not even show up as a type error.
614
+ *
615
+ * @type {<O extends Operation, T, Q extends Operation, E>(
616
+ * items: Effect<O, List<T>, E>,
617
+ * f: (item: T) => Effect<Q, void, E>
618
+ * ) => Effect<O | Q, void, E>}
619
+ */
620
+ export const forEachStep = (items, f) =>
621
+ foldStep(items, undefined, item => () => f(item))