@k2works/claude-code-booster 3.5.0 → 3.6.0
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/LICENSE +21 -21
- package/README.md +42 -42
- package/bin/claude-code-booster +90 -90
- package/lib/assets/.claude/README.md +239 -239
- package/lib/assets/.claude/scripts/generate-inception-deck.mjs +911 -911
- package/lib/assets/.claude/settings.json +11 -11
- package/lib/assets/.claude/skills/ai-agent-guidelines/SKILL.md +111 -111
- package/lib/assets/.claude/skills/analyzing-architecture/SKILL.md +83 -83
- package/lib/assets/.claude/skills/analyzing-business/SKILL.md +95 -95
- package/lib/assets/.claude/skills/analyzing-data-model/SKILL.md +77 -77
- package/lib/assets/.claude/skills/analyzing-domain-model/SKILL.md +117 -88
- package/lib/assets/.claude/skills/analyzing-inception-deck/SKILL.md +84 -84
- package/lib/assets/.claude/skills/analyzing-non-functional/SKILL.md +95 -95
- package/lib/assets/.claude/skills/analyzing-operation/SKILL.md +95 -95
- package/lib/assets/.claude/skills/analyzing-requirements/SKILL.md +91 -91
- package/lib/assets/.claude/skills/analyzing-tech-stack/SKILL.md +101 -101
- package/lib/assets/.claude/skills/analyzing-test-strategy/SKILL.md +89 -89
- package/lib/assets/.claude/skills/analyzing-ui-design/SKILL.md +80 -80
- package/lib/assets/.claude/skills/analyzing-usecases/SKILL.md +72 -72
- package/lib/assets/.claude/skills/creating-adr/SKILL.md +113 -113
- package/lib/assets/.claude/skills/developing-backend/SKILL.md +100 -100
- package/lib/assets/.claude/skills/developing-frontend/SKILL.md +93 -93
- package/lib/assets/.claude/skills/developing-release/SKILL.md +120 -120
- package/lib/assets/.claude/skills/generating-slides/SKILL.md +94 -94
- package/lib/assets/.claude/skills/git-commit/SKILL.md +81 -81
- package/lib/assets/.claude/skills/killing-processes/SKILL.md +44 -44
- package/lib/assets/.claude/skills/operating-backup/SKILL.md +59 -59
- package/lib/assets/.claude/skills/operating-cicd/SKILL.md +54 -54
- package/lib/assets/.claude/skills/operating-deploy/SKILL.md +67 -67
- package/lib/assets/.claude/skills/operating-docs/SKILL.md +219 -219
- package/lib/assets/.claude/skills/operating-provision/SKILL.md +77 -77
- package/lib/assets/.claude/skills/operating-setup/SKILL.md +63 -63
- package/lib/assets/.claude/skills/orchestrating-analysis/SKILL.md +104 -104
- package/lib/assets/.claude/skills/orchestrating-development/SKILL.md +162 -161
- package/lib/assets/.claude/skills/orchestrating-operation/SKILL.md +158 -158
- package/lib/assets/.claude/skills/orchestrating-project/SKILL.md +144 -144
- package/lib/assets/.claude/skills/planning-releases/SKILL.md +119 -119
- package/lib/assets/.claude/skills/syncing-github-project/SKILL.md +151 -151
- package/lib/assets/.claude/skills/tracking-progress/SKILL.md +91 -91
- package/lib/assets/.claude/skills/validating-iteration-plan/SKILL.md +215 -215
- package/lib/assets/.devcontainer/devcontainer.json +34 -34
- package/lib/assets/.env.example +17 -17
- package/lib/assets/.gitattributes +4 -4
- package/lib/assets/.github/workflows/docker-publish.yml +77 -77
- package/lib/assets/.github/workflows/mkdocs.yml +39 -39
- package/lib/assets/AGENTS.md +94 -94
- package/lib/assets/CLAUDE.md +183 -183
- package/lib/assets/README.md +254 -254
- package/lib/assets/docker-compose.yml +33 -33
- package/lib/assets/docs/adr/index.md +10 -10
- package/lib/assets/docs/article/functional-desgin-ppp/all/01-immutability-and-data-transformation.md +475 -475
- package/lib/assets/docs/article/functional-desgin-ppp/all/02-function-composition.md +519 -519
- package/lib/assets/docs/article/functional-desgin-ppp/all/03-polymorphism.md +537 -537
- package/lib/assets/docs/article/functional-desgin-ppp/all/04-data-validation.md +300 -300
- package/lib/assets/docs/article/functional-desgin-ppp/all/05-property-based-testing.md +320 -320
- package/lib/assets/docs/article/functional-desgin-ppp/all/06-tdd-and-functional.md +498 -498
- package/lib/assets/docs/article/functional-desgin-ppp/all/07-composite-pattern.md +298 -298
- package/lib/assets/docs/article/functional-desgin-ppp/all/08-decorator-pattern.md +291 -291
- package/lib/assets/docs/article/functional-desgin-ppp/all/09-adapter-pattern.md +336 -336
- package/lib/assets/docs/article/functional-desgin-ppp/all/10-strategy-pattern.md +303 -303
- package/lib/assets/docs/article/functional-desgin-ppp/all/11-command-pattern.md +286 -286
- package/lib/assets/docs/article/functional-desgin-ppp/all/12-visitor-pattern.md +322 -322
- package/lib/assets/docs/article/functional-desgin-ppp/all/13-abstract-factory-pattern.md +319 -319
- package/lib/assets/docs/article/functional-desgin-ppp/all/14-abstract-server-pattern.md +365 -365
- package/lib/assets/docs/article/functional-desgin-ppp/all/15-gossiping-bus-drivers.md +156 -156
- package/lib/assets/docs/article/functional-desgin-ppp/all/16-payroll-system.md +178 -178
- package/lib/assets/docs/article/functional-desgin-ppp/all/17-video-rental-system.md +312 -312
- package/lib/assets/docs/article/functional-desgin-ppp/all/18-concurrency-system.md +287 -287
- package/lib/assets/docs/article/functional-desgin-ppp/all/19-wa-tor-simulation.md +286 -286
- package/lib/assets/docs/article/functional-desgin-ppp/all/20-pattern-interactions.md +274 -274
- package/lib/assets/docs/article/functional-desgin-ppp/all/21-best-practices.md +294 -294
- package/lib/assets/docs/article/functional-desgin-ppp/all/22-oo-to-fp-migration.md +337 -337
- package/lib/assets/docs/article/functional-desgin-ppp/all/index.md +388 -388
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/01-immutability-and-data-transformation.md +273 -273
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/02-function-composition.md +380 -380
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/03-polymorphism.md +384 -384
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/04-clojure-spec.md +350 -350
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/05-property-based-testing.md +352 -352
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/06-tdd-in-functional.md +383 -383
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/07-composite-pattern.md +529 -529
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/08-decorator-pattern.md +395 -395
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/09-adapter-pattern.md +399 -399
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/10-strategy-pattern.md +485 -485
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/11-command-pattern.md +566 -566
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/12-visitor-pattern.md +567 -567
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/13-abstract-factory-pattern.md +475 -475
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/14-abstract-server-pattern.md +462 -462
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/15-gossiping-bus-drivers.md +325 -325
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/16-payroll-system.md +401 -401
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/17-video-rental-system.md +450 -450
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/18-concurrency-system.md +475 -475
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/19-wator-simulation.md +739 -739
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/20-pattern-interactions.md +567 -567
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/21-best-practices.md +518 -518
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/22-oo-to-fp-migration.md +532 -532
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/index.md +241 -241
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/01-immutability-and-data-transformation.md +383 -383
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/02-function-composition.md +374 -374
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/03-polymorphism.md +375 -375
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/04-data-validation.md +195 -195
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/05-property-based-testing.md +268 -268
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/06-tdd-and-fp.md +294 -294
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/07-effects-and-pure-functions.md +164 -164
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/08-error-handling-strategies.md +168 -168
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/09-io-and-external-systems.md +254 -254
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/10-concurrency-patterns.md +269 -269
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/11-command-pattern.md +148 -148
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/12-visitor-pattern.md +176 -176
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/13-abstract-factory-pattern.md +604 -604
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/14-abstract-server-pattern.md +729 -729
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/15-gossiping-bus-drivers.md +291 -291
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/16-payroll-system.md +420 -420
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/17-video-rental-system.md +319 -319
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/18-concurrency-system.md +466 -466
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/19-wator-simulation.md +523 -523
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/20-pattern-interactions.md +287 -287
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/21-best-practices.md +340 -340
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/22-oo-to-fp-migration.md +395 -395
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/index.md +248 -248
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/01-immutability-and-data-transformation.md +384 -384
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/02-function-composition.md +452 -452
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/03-polymorphism.md +495 -495
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/04-data-validation.md +416 -416
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/05-property-based-testing.md +382 -382
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/06-tdd-functional.md +687 -687
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/07-composite-pattern.md +442 -442
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/08-decorator-pattern.md +479 -479
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/09-adapter-pattern.md +479 -479
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/10-strategy-pattern.md +427 -427
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/11-command-pattern.md +428 -428
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/12-visitor-pattern.md +339 -339
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/13-abstract-factory-pattern.md +309 -309
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/14-abstract-server-pattern.md +596 -596
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/15-gossiping-bus-drivers.md +355 -355
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/16-payroll-system.md +350 -350
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/17-video-rental-system.md +414 -414
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/18-concurrency-system.md +367 -367
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/19-wator-simulation.md +403 -403
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/20-pattern-interactions.md +291 -291
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/21-best-practices.md +324 -324
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/22-oo-to-fp-migration.md +332 -332
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/index.md +274 -274
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/01-immutability-and-data-transformation.md +298 -298
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/02-function-composition.md +304 -304
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/03-polymorphism.md +362 -362
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/04-data-validation.md +257 -257
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/05-property-based-testing.md +254 -254
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/06-tdd-functional.md +283 -283
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/07-composite-pattern.md +395 -395
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/08-decorator-pattern.md +319 -319
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/09-adapter-pattern.md +382 -382
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/10-strategy-pattern.md +287 -287
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/11-command-pattern.md +303 -303
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/12-visitor-pattern.md +326 -326
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/13-abstract-factory-pattern.md +332 -332
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/14-abstract-server-pattern.md +379 -379
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/15-gossiping-bus-drivers.md +177 -177
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/16-payroll-system.md +219 -219
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/17-video-rental-system.md +244 -244
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/18-concurrency-system.md +363 -363
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/19-wator-simulation.md +438 -438
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/20-pattern-interactions.md +325 -325
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/21-best-practices.md +403 -403
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/22-oo-to-fp-migration.md +469 -469
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/index.md +174 -174
- package/lib/assets/docs/article/functional-desgin-ppp/index.md +90 -90
- package/lib/assets/docs/article/functional-desgin-ppp/rust/01-immutability-and-data-transformation.md +450 -450
- package/lib/assets/docs/article/functional-desgin-ppp/rust/02-function-composition.md +463 -463
- package/lib/assets/docs/article/functional-desgin-ppp/rust/03-polymorphism.md +425 -425
- package/lib/assets/docs/article/functional-desgin-ppp/rust/04-data-validation.md +273 -273
- package/lib/assets/docs/article/functional-desgin-ppp/rust/05-property-based-testing.md +247 -247
- package/lib/assets/docs/article/functional-desgin-ppp/rust/06-tdd-and-functional.md +841 -841
- package/lib/assets/docs/article/functional-desgin-ppp/rust/07-composite-pattern.md +384 -384
- package/lib/assets/docs/article/functional-desgin-ppp/rust/08-decorator-pattern.md +383 -383
- package/lib/assets/docs/article/functional-desgin-ppp/rust/09-adapter-pattern.md +339 -339
- package/lib/assets/docs/article/functional-desgin-ppp/rust/10-strategy-pattern.md +331 -331
- package/lib/assets/docs/article/functional-desgin-ppp/rust/11-command-pattern.md +356 -356
- package/lib/assets/docs/article/functional-desgin-ppp/rust/12-visitor-pattern.md +379 -379
- package/lib/assets/docs/article/functional-desgin-ppp/rust/13-abstract-factory-pattern.md +361 -361
- package/lib/assets/docs/article/functional-desgin-ppp/rust/14-abstract-server-pattern.md +392 -392
- package/lib/assets/docs/article/functional-desgin-ppp/rust/15-gossiping-bus-drivers.md +300 -300
- package/lib/assets/docs/article/functional-desgin-ppp/rust/16-payroll-system.md +297 -297
- package/lib/assets/docs/article/functional-desgin-ppp/rust/17-video-rental-system.md +304 -304
- package/lib/assets/docs/article/functional-desgin-ppp/rust/18-concurrency-system.md +315 -315
- package/lib/assets/docs/article/functional-desgin-ppp/rust/19-wator-simulation.md +311 -311
- package/lib/assets/docs/article/functional-desgin-ppp/rust/20-pattern-interactions.md +304 -304
- package/lib/assets/docs/article/functional-desgin-ppp/rust/21-best-practices.md +336 -336
- package/lib/assets/docs/article/functional-desgin-ppp/rust/22-oo-to-fp-migration.md +349 -349
- package/lib/assets/docs/article/functional-desgin-ppp/rust/index.md +243 -243
- package/lib/assets/docs/article/functional-desgin-ppp/scala/01-immutability-and-data-transformation.md +328 -328
- package/lib/assets/docs/article/functional-desgin-ppp/scala/02-function-composition.md +348 -348
- package/lib/assets/docs/article/functional-desgin-ppp/scala/03-polymorphism.md +357 -357
- package/lib/assets/docs/article/functional-desgin-ppp/scala/04-data-validation.md +364 -364
- package/lib/assets/docs/article/functional-desgin-ppp/scala/05-property-based-testing.md +515 -515
- package/lib/assets/docs/article/functional-desgin-ppp/scala/06-tdd-functional.md +557 -557
- package/lib/assets/docs/article/functional-desgin-ppp/scala/07-composite-pattern.md +363 -363
- package/lib/assets/docs/article/functional-desgin-ppp/scala/08-decorator-pattern.md +327 -327
- package/lib/assets/docs/article/functional-desgin-ppp/scala/09-adapter-pattern.md +517 -517
- package/lib/assets/docs/article/functional-desgin-ppp/scala/10-strategy-pattern.md +441 -441
- package/lib/assets/docs/article/functional-desgin-ppp/scala/11-command-pattern.md +407 -407
- package/lib/assets/docs/article/functional-desgin-ppp/scala/12-visitor-pattern.md +379 -379
- package/lib/assets/docs/article/functional-desgin-ppp/scala/13-abstract-factory-pattern.md +398 -398
- package/lib/assets/docs/article/functional-desgin-ppp/scala/14-abstract-server-pattern.md +476 -476
- package/lib/assets/docs/article/functional-desgin-ppp/scala/15-gossiping-bus-drivers.md +391 -391
- package/lib/assets/docs/article/functional-desgin-ppp/scala/16-payroll-system.md +342 -342
- package/lib/assets/docs/article/functional-desgin-ppp/scala/17-video-rental-system.md +324 -324
- package/lib/assets/docs/article/functional-desgin-ppp/scala/18-concurrency-system.md +730 -730
- package/lib/assets/docs/article/functional-desgin-ppp/scala/19-wator-simulation.md +624 -624
- package/lib/assets/docs/article/functional-desgin-ppp/scala/20-pattern-interactions.md +512 -512
- package/lib/assets/docs/article/functional-desgin-ppp/scala/21-best-practices.md +433 -433
- package/lib/assets/docs/article/functional-desgin-ppp/scala/22-oo-to-fp-migration.md +688 -688
- package/lib/assets/docs/article/functional-desgin-ppp/scala/index.md +243 -243
- package/lib/assets/docs/article/getting-start-tdd/clojure/01-todo-list-and-first-test.md +166 -166
- package/lib/assets/docs/article/getting-start-tdd/clojure/02-fake-it-and-triangulation.md +162 -162
- package/lib/assets/docs/article/getting-start-tdd/clojure/03-obvious-implementation-and-refactoring.md +135 -135
- package/lib/assets/docs/article/getting-start-tdd/clojure/04-version-control-and-conventional-commits.md +88 -88
- package/lib/assets/docs/article/getting-start-tdd/clojure/05-package-management-and-static-analysis.md +299 -299
- package/lib/assets/docs/article/getting-start-tdd/clojure/06-task-runner-and-ci-cd.md +241 -241
- package/lib/assets/docs/article/getting-start-tdd/clojure/07-protocols-and-records.md +131 -131
- package/lib/assets/docs/article/getting-start-tdd/clojure/08-multimethods-and-design-patterns.md +130 -130
- package/lib/assets/docs/article/getting-start-tdd/clojure/09-namespaces-and-module-design.md +127 -127
- package/lib/assets/docs/article/getting-start-tdd/clojure/10-higher-order-functions-and-composition.md +114 -114
- package/lib/assets/docs/article/getting-start-tdd/clojure/11-persistent-data-and-pipeline.md +138 -138
- package/lib/assets/docs/article/getting-start-tdd/clojure/12-error-handling-and-spec.md +161 -161
- package/lib/assets/docs/article/getting-start-tdd/clojure/index.md +65 -65
- package/lib/assets/docs/article/getting-start-tdd/csharp/chapter01.md +232 -232
- package/lib/assets/docs/article/getting-start-tdd/csharp/chapter02.md +244 -244
- package/lib/assets/docs/article/getting-start-tdd/csharp/chapter03.md +202 -202
- package/lib/assets/docs/article/getting-start-tdd/csharp/chapter04.md +92 -92
- package/lib/assets/docs/article/getting-start-tdd/csharp/chapter05.md +256 -256
- package/lib/assets/docs/article/getting-start-tdd/csharp/chapter06.md +195 -195
- package/lib/assets/docs/article/getting-start-tdd/csharp/chapter07.md +214 -214
- package/lib/assets/docs/article/getting-start-tdd/csharp/chapter08.md +249 -249
- package/lib/assets/docs/article/getting-start-tdd/csharp/chapter09.md +174 -174
- package/lib/assets/docs/article/getting-start-tdd/csharp/chapter10.md +166 -166
- package/lib/assets/docs/article/getting-start-tdd/csharp/chapter11.md +192 -192
- package/lib/assets/docs/article/getting-start-tdd/csharp/chapter12.md +211 -211
- package/lib/assets/docs/article/getting-start-tdd/csharp/index.md +83 -83
- package/lib/assets/docs/article/getting-start-tdd/elixir/01-todo-list-and-first-test.md +87 -87
- package/lib/assets/docs/article/getting-start-tdd/elixir/02-fake-it-and-triangulation.md +95 -95
- package/lib/assets/docs/article/getting-start-tdd/elixir/03-obvious-implementation-and-refactoring.md +109 -109
- package/lib/assets/docs/article/getting-start-tdd/elixir/04-version-control-and-conventional-commits.md +96 -96
- package/lib/assets/docs/article/getting-start-tdd/elixir/05-package-management-and-static-analysis.md +88 -88
- package/lib/assets/docs/article/getting-start-tdd/elixir/06-task-runner-and-ci-cd.md +71 -71
- package/lib/assets/docs/article/getting-start-tdd/elixir/07-structs-and-protocols.md +110 -110
- package/lib/assets/docs/article/getting-start-tdd/elixir/08-pattern-matching-and-guards.md +108 -108
- package/lib/assets/docs/article/getting-start-tdd/elixir/09-module-design-and-behaviours.md +104 -104
- package/lib/assets/docs/article/getting-start-tdd/elixir/10-higher-order-functions-and-pipeline.md +178 -178
- package/lib/assets/docs/article/getting-start-tdd/elixir/11-stream-and-lazy-evaluation.md +142 -142
- package/lib/assets/docs/article/getting-start-tdd/elixir/12-error-handling-and-with.md +145 -145
- package/lib/assets/docs/article/getting-start-tdd/elixir/index.md +35 -35
- package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter01.md +202 -202
- package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter02.md +246 -246
- package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter03.md +218 -218
- package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter04.md +179 -179
- package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter05.md +267 -267
- package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter06.md +190 -190
- package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter07.md +161 -161
- package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter08.md +175 -175
- package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter09.md +222 -222
- package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter10.md +189 -189
- package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter11.md +212 -212
- package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter12.md +215 -215
- package/lib/assets/docs/article/getting-start-tdd/fsharp/index.md +71 -71
- package/lib/assets/docs/article/getting-start-tdd/go/01-todo-list-and-first-test.md +213 -213
- package/lib/assets/docs/article/getting-start-tdd/go/02-fake-it-and-triangulation.md +302 -302
- package/lib/assets/docs/article/getting-start-tdd/go/03-obvious-implementation-and-refactoring.md +339 -339
- package/lib/assets/docs/article/getting-start-tdd/go/04-version-control-and-conventional-commits.md +112 -112
- package/lib/assets/docs/article/getting-start-tdd/go/05-package-management-and-static-analysis.md +272 -272
- package/lib/assets/docs/article/getting-start-tdd/go/06-task-runner-and-ci-cd.md +233 -233
- package/lib/assets/docs/article/getting-start-tdd/go/07-encapsulation-and-polymorphism.md +394 -394
- package/lib/assets/docs/article/getting-start-tdd/go/08-design-patterns.md +422 -422
- package/lib/assets/docs/article/getting-start-tdd/go/09-solid-principles-and-module-design.md +400 -400
- package/lib/assets/docs/article/getting-start-tdd/go/10-higher-order-functions-and-composition.md +226 -226
- package/lib/assets/docs/article/getting-start-tdd/go/11-immutable-data-and-pipeline.md +296 -296
- package/lib/assets/docs/article/getting-start-tdd/go/12-error-handling-and-type-safety.md +411 -411
- package/lib/assets/docs/article/getting-start-tdd/go/index.md +83 -83
- package/lib/assets/docs/article/getting-start-tdd/haskell/01-todo-list-and-first-test.md +279 -279
- package/lib/assets/docs/article/getting-start-tdd/haskell/02-fake-it-and-triangulation.md +337 -337
- package/lib/assets/docs/article/getting-start-tdd/haskell/03-obvious-implementation-and-refactoring.md +257 -257
- package/lib/assets/docs/article/getting-start-tdd/haskell/04-version-control-and-conventional-commits.md +182 -182
- package/lib/assets/docs/article/getting-start-tdd/haskell/05-package-management-and-static-analysis.md +313 -313
- package/lib/assets/docs/article/getting-start-tdd/haskell/06-task-runner-and-ci-cd.md +309 -309
- package/lib/assets/docs/article/getting-start-tdd/haskell/07-algebraic-data-types-and-type-classes.md +412 -412
- package/lib/assets/docs/article/getting-start-tdd/haskell/08-pattern-matching-and-guards.md +390 -390
- package/lib/assets/docs/article/getting-start-tdd/haskell/09-module-design-and-smart-constructors.md +461 -461
- package/lib/assets/docs/article/getting-start-tdd/haskell/10-higher-order-functions-and-currying.md +434 -434
- package/lib/assets/docs/article/getting-start-tdd/haskell/11-function-composition-and-point-free.md +392 -392
- package/lib/assets/docs/article/getting-start-tdd/haskell/12-monad-and-error-handling.md +631 -631
- package/lib/assets/docs/article/getting-start-tdd/haskell/index.md +49 -49
- package/lib/assets/docs/article/getting-start-tdd/index.md +93 -93
- package/lib/assets/docs/article/getting-start-tdd/integration/01-language-overview.md +375 -375
- package/lib/assets/docs/article/getting-start-tdd/integration/02-test-framework-comparison.md +349 -349
- package/lib/assets/docs/article/getting-start-tdd/integration/03-tdd-pattern-comparison.md +445 -445
- package/lib/assets/docs/article/getting-start-tdd/integration/04-type-system-comparison.md +409 -409
- package/lib/assets/docs/article/getting-start-tdd/integration/05-dev-environment-comparison.md +330 -330
- package/lib/assets/docs/article/getting-start-tdd/integration/06-learning-roadmap.md +290 -290
- package/lib/assets/docs/article/getting-start-tdd/integration/index.md +69 -69
- package/lib/assets/docs/article/getting-start-tdd/java/01-todo-list-and-first-test.md +234 -234
- package/lib/assets/docs/article/getting-start-tdd/java/02-fake-it-and-triangulation.md +261 -261
- package/lib/assets/docs/article/getting-start-tdd/java/03-obvious-implementation-and-refactoring.md +185 -185
- package/lib/assets/docs/article/getting-start-tdd/java/04-version-control-and-conventional-commits.md +115 -115
- package/lib/assets/docs/article/getting-start-tdd/java/05-package-management-and-static-analysis.md +382 -382
- package/lib/assets/docs/article/getting-start-tdd/java/06-task-runner-and-ci-cd.md +272 -272
- package/lib/assets/docs/article/getting-start-tdd/java/07-encapsulation-and-polymorphism.md +626 -626
- package/lib/assets/docs/article/getting-start-tdd/java/08-design-patterns.md +393 -393
- package/lib/assets/docs/article/getting-start-tdd/java/09-solid-principles-and-module-design.md +310 -310
- package/lib/assets/docs/article/getting-start-tdd/java/10-higher-order-functions-and-composition.md +188 -188
- package/lib/assets/docs/article/getting-start-tdd/java/11-immutable-data-and-pipeline.md +167 -167
- package/lib/assets/docs/article/getting-start-tdd/java/12-error-handling-and-type-safety.md +205 -205
- package/lib/assets/docs/article/getting-start-tdd/java/index.md +61 -61
- package/lib/assets/docs/article/getting-start-tdd/node/01-todo-list-and-first-test.md +244 -244
- package/lib/assets/docs/article/getting-start-tdd/node/02-fake-it-and-triangulation.md +262 -262
- package/lib/assets/docs/article/getting-start-tdd/node/03-obvious-implementation-and-refactoring.md +169 -169
- package/lib/assets/docs/article/getting-start-tdd/node/04-version-control-and-conventional-commits.md +112 -112
- package/lib/assets/docs/article/getting-start-tdd/node/05-package-management-and-static-analysis.md +314 -314
- package/lib/assets/docs/article/getting-start-tdd/node/06-task-runner-and-ci-cd.md +235 -235
- package/lib/assets/docs/article/getting-start-tdd/node/07-encapsulation-and-polymorphism.md +327 -327
- package/lib/assets/docs/article/getting-start-tdd/node/08-design-patterns.md +322 -322
- package/lib/assets/docs/article/getting-start-tdd/node/09-solid-principles-and-module-design.md +285 -285
- package/lib/assets/docs/article/getting-start-tdd/node/10-higher-order-functions-and-composition.md +199 -199
- package/lib/assets/docs/article/getting-start-tdd/node/11-immutable-data-and-pipeline.md +207 -207
- package/lib/assets/docs/article/getting-start-tdd/node/12-error-handling-and-type-safety.md +295 -295
- package/lib/assets/docs/article/getting-start-tdd/node/index.md +56 -56
- package/lib/assets/docs/article/getting-start-tdd/php/01-todo-list-and-first-test.md +259 -259
- package/lib/assets/docs/article/getting-start-tdd/php/02-fake-it-and-triangulation.md +200 -200
- package/lib/assets/docs/article/getting-start-tdd/php/03-obvious-implementation-and-refactoring.md +248 -248
- package/lib/assets/docs/article/getting-start-tdd/php/04-version-control-and-conventional-commits.md +141 -141
- package/lib/assets/docs/article/getting-start-tdd/php/05-package-management-and-static-analysis.md +410 -410
- package/lib/assets/docs/article/getting-start-tdd/php/06-task-runner-and-ci-cd.md +321 -321
- package/lib/assets/docs/article/getting-start-tdd/php/07-encapsulation-and-polymorphism.md +372 -372
- package/lib/assets/docs/article/getting-start-tdd/php/08-design-patterns.md +453 -453
- package/lib/assets/docs/article/getting-start-tdd/php/09-solid-principles-and-module-design.md +460 -460
- package/lib/assets/docs/article/getting-start-tdd/php/10-higher-order-functions-and-composition.md +182 -182
- package/lib/assets/docs/article/getting-start-tdd/php/11-immutable-data-and-pipeline.md +266 -266
- package/lib/assets/docs/article/getting-start-tdd/php/12-error-handling-and-type-safety.md +308 -308
- package/lib/assets/docs/article/getting-start-tdd/php/index.md +84 -84
- package/lib/assets/docs/article/getting-start-tdd/python/01-todo-list-and-first-test.md +201 -201
- package/lib/assets/docs/article/getting-start-tdd/python/02-fake-it-and-triangulation.md +247 -247
- package/lib/assets/docs/article/getting-start-tdd/python/03-obvious-implementation-and-refactoring.md +199 -199
- package/lib/assets/docs/article/getting-start-tdd/python/04-version-control-and-conventional-commits.md +87 -87
- package/lib/assets/docs/article/getting-start-tdd/python/05-package-management-and-static-analysis.md +274 -274
- package/lib/assets/docs/article/getting-start-tdd/python/06-task-runner-and-ci-cd.md +190 -190
- package/lib/assets/docs/article/getting-start-tdd/python/07-encapsulation-and-polymorphism.md +208 -208
- package/lib/assets/docs/article/getting-start-tdd/python/08-design-patterns.md +172 -172
- package/lib/assets/docs/article/getting-start-tdd/python/09-solid-principles-and-module-design.md +130 -130
- package/lib/assets/docs/article/getting-start-tdd/python/10-higher-order-functions-and-composition.md +122 -122
- package/lib/assets/docs/article/getting-start-tdd/python/11-immutable-data-and-pipeline.md +116 -116
- package/lib/assets/docs/article/getting-start-tdd/python/12-error-handling-and-type-safety.md +126 -126
- package/lib/assets/docs/article/getting-start-tdd/python/index.md +55 -55
- package/lib/assets/docs/article/getting-start-tdd/ruby/01-todo-list-and-first-test.md +231 -231
- package/lib/assets/docs/article/getting-start-tdd/ruby/02-fake-it-and-triangulation.md +238 -238
- package/lib/assets/docs/article/getting-start-tdd/ruby/03-obvious-implementation-and-refactoring.md +228 -228
- package/lib/assets/docs/article/getting-start-tdd/ruby/04-version-control-and-conventional-commits.md +112 -112
- package/lib/assets/docs/article/getting-start-tdd/ruby/05-package-management-and-static-analysis.md +287 -287
- package/lib/assets/docs/article/getting-start-tdd/ruby/06-task-runner-and-ci-cd.md +248 -248
- package/lib/assets/docs/article/getting-start-tdd/ruby/07-encapsulation-and-polymorphism.md +279 -279
- package/lib/assets/docs/article/getting-start-tdd/ruby/08-design-patterns.md +329 -329
- package/lib/assets/docs/article/getting-start-tdd/ruby/09-solid-principles-and-module-design.md +196 -196
- package/lib/assets/docs/article/getting-start-tdd/ruby/10-higher-order-functions-and-composition.md +175 -175
- package/lib/assets/docs/article/getting-start-tdd/ruby/11-immutable-data-and-pipeline.md +237 -237
- package/lib/assets/docs/article/getting-start-tdd/ruby/12-error-handling-and-type-safety.md +398 -398
- package/lib/assets/docs/article/getting-start-tdd/ruby/index.md +83 -83
- package/lib/assets/docs/article/getting-start-tdd/rust/01-todo-list-and-first-test.md +211 -211
- package/lib/assets/docs/article/getting-start-tdd/rust/02-fake-it-and-triangulation.md +264 -264
- package/lib/assets/docs/article/getting-start-tdd/rust/03-obvious-implementation-and-refactoring.md +233 -233
- package/lib/assets/docs/article/getting-start-tdd/rust/04-version-control-and-conventional-commits.md +92 -92
- package/lib/assets/docs/article/getting-start-tdd/rust/05-package-management-and-static-analysis.md +212 -212
- package/lib/assets/docs/article/getting-start-tdd/rust/06-task-runner-and-ci-cd.md +164 -164
- package/lib/assets/docs/article/getting-start-tdd/rust/07-encapsulation-and-polymorphism.md +142 -142
- package/lib/assets/docs/article/getting-start-tdd/rust/08-design-patterns.md +145 -145
- package/lib/assets/docs/article/getting-start-tdd/rust/09-solid-principles-and-module-design.md +110 -110
- package/lib/assets/docs/article/getting-start-tdd/rust/10-higher-order-functions-and-composition.md +94 -94
- package/lib/assets/docs/article/getting-start-tdd/rust/11-immutable-data-and-pipeline.md +105 -105
- package/lib/assets/docs/article/getting-start-tdd/rust/12-error-handling-and-type-safety.md +112 -112
- package/lib/assets/docs/article/getting-start-tdd/rust/index.md +83 -83
- package/lib/assets/docs/article/getting-start-tdd/scala/01-todo-list-and-first-test.md +111 -111
- package/lib/assets/docs/article/getting-start-tdd/scala/02-fake-it-and-triangulation.md +107 -107
- package/lib/assets/docs/article/getting-start-tdd/scala/03-obvious-implementation-and-refactoring.md +99 -99
- package/lib/assets/docs/article/getting-start-tdd/scala/04-version-control-and-conventional-commits.md +123 -123
- package/lib/assets/docs/article/getting-start-tdd/scala/05-package-management-and-static-analysis.md +196 -196
- package/lib/assets/docs/article/getting-start-tdd/scala/06-task-runner-and-ci-cd.md +186 -186
- package/lib/assets/docs/article/getting-start-tdd/scala/07-case-classes-and-traits.md +139 -139
- package/lib/assets/docs/article/getting-start-tdd/scala/08-pattern-matching-and-sealed-traits.md +106 -106
- package/lib/assets/docs/article/getting-start-tdd/scala/09-packages-and-module-design.md +75 -75
- package/lib/assets/docs/article/getting-start-tdd/scala/10-higher-order-functions-and-composition.md +104 -104
- package/lib/assets/docs/article/getting-start-tdd/scala/11-collections-and-lazy-evaluation.md +94 -94
- package/lib/assets/docs/article/getting-start-tdd/scala/12-error-handling-and-type-safety.md +92 -92
- package/lib/assets/docs/article/getting-start-tdd/scala/index.md +65 -65
- package/lib/assets/docs/article/grokking-concurrency/all/index.md +404 -404
- package/lib/assets/docs/article/grokking-concurrency/all/part-1-ch02-sequential.md +554 -554
- package/lib/assets/docs/article/grokking-concurrency/all/part-2-ch04-05-threads.md +469 -469
- package/lib/assets/docs/article/grokking-concurrency/all/part-3-ch06-multitasking.md +520 -520
- package/lib/assets/docs/article/grokking-concurrency/all/part-4-ch07-parallel-patterns.md +420 -420
- package/lib/assets/docs/article/grokking-concurrency/all/part-5-ch08-09-synchronization.md +510 -510
- package/lib/assets/docs/article/grokking-concurrency/all/part-6-ch10-11-nonblocking-io.md +435 -435
- package/lib/assets/docs/article/grokking-concurrency/all/part-7-ch12-async.md +465 -465
- package/lib/assets/docs/article/grokking-concurrency/all/part-8-ch13-mapreduce.md +377 -377
- package/lib/assets/docs/article/grokking-concurrency/clojure/index.md +116 -116
- package/lib/assets/docs/article/grokking-concurrency/clojure/part-1.md +108 -108
- package/lib/assets/docs/article/grokking-concurrency/clojure/part-2.md +101 -101
- package/lib/assets/docs/article/grokking-concurrency/clojure/part-3.md +122 -122
- package/lib/assets/docs/article/grokking-concurrency/clojure/part-4.md +123 -123
- package/lib/assets/docs/article/grokking-concurrency/clojure/part-5.md +118 -118
- package/lib/assets/docs/article/grokking-concurrency/clojure/part-6.md +89 -89
- package/lib/assets/docs/article/grokking-concurrency/clojure/part-7.md +100 -100
- package/lib/assets/docs/article/grokking-concurrency/clojure/part-8.md +120 -120
- package/lib/assets/docs/article/grokking-concurrency/csharp/index.md +101 -101
- package/lib/assets/docs/article/grokking-concurrency/csharp/part-1.md +97 -97
- package/lib/assets/docs/article/grokking-concurrency/csharp/part-2.md +123 -123
- package/lib/assets/docs/article/grokking-concurrency/csharp/part-3.md +101 -101
- package/lib/assets/docs/article/grokking-concurrency/csharp/part-4.md +112 -112
- package/lib/assets/docs/article/grokking-concurrency/csharp/part-5.md +99 -99
- package/lib/assets/docs/article/grokking-concurrency/csharp/part-6.md +61 -61
- package/lib/assets/docs/article/grokking-concurrency/csharp/part-7.md +84 -84
- package/lib/assets/docs/article/grokking-concurrency/csharp/part-8.md +92 -92
- package/lib/assets/docs/article/grokking-concurrency/fsharp/index.md +65 -65
- package/lib/assets/docs/article/grokking-concurrency/fsharp/part-1.md +80 -80
- package/lib/assets/docs/article/grokking-concurrency/fsharp/part-2.md +103 -103
- package/lib/assets/docs/article/grokking-concurrency/fsharp/part-3.md +94 -94
- package/lib/assets/docs/article/grokking-concurrency/fsharp/part-4.md +110 -110
- package/lib/assets/docs/article/grokking-concurrency/fsharp/part-5.md +104 -104
- package/lib/assets/docs/article/grokking-concurrency/fsharp/part-6.md +93 -93
- package/lib/assets/docs/article/grokking-concurrency/fsharp/part-7.md +121 -121
- package/lib/assets/docs/article/grokking-concurrency/fsharp/part-8.md +107 -107
- package/lib/assets/docs/article/grokking-concurrency/haskell/index.md +248 -248
- package/lib/assets/docs/article/grokking-concurrency/haskell/part-1.md +96 -96
- package/lib/assets/docs/article/grokking-concurrency/haskell/part-2.md +96 -96
- package/lib/assets/docs/article/grokking-concurrency/haskell/part-3.md +91 -91
- package/lib/assets/docs/article/grokking-concurrency/haskell/part-4.md +106 -106
- package/lib/assets/docs/article/grokking-concurrency/haskell/part-5.md +99 -99
- package/lib/assets/docs/article/grokking-concurrency/haskell/part-6.md +95 -95
- package/lib/assets/docs/article/grokking-concurrency/haskell/part-7.md +111 -111
- package/lib/assets/docs/article/grokking-concurrency/haskell/part-8.md +118 -118
- package/lib/assets/docs/article/grokking-concurrency/index.md +66 -66
- package/lib/assets/docs/article/grokking-concurrency/java/index.md +102 -102
- package/lib/assets/docs/article/grokking-concurrency/java/part-1.md +308 -308
- package/lib/assets/docs/article/grokking-concurrency/java/part-2.md +334 -334
- package/lib/assets/docs/article/grokking-concurrency/java/part-3.md +221 -221
- package/lib/assets/docs/article/grokking-concurrency/java/part-4.md +213 -213
- package/lib/assets/docs/article/grokking-concurrency/java/part-5.md +112 -112
- package/lib/assets/docs/article/grokking-concurrency/java/part-6.md +69 -69
- package/lib/assets/docs/article/grokking-concurrency/java/part-7.md +101 -101
- package/lib/assets/docs/article/grokking-concurrency/java/part-8.md +101 -101
- package/lib/assets/docs/article/grokking-concurrency/python/index.md +313 -313
- package/lib/assets/docs/article/grokking-concurrency/python/part-1.md +239 -239
- package/lib/assets/docs/article/grokking-concurrency/python/part-2.md +418 -418
- package/lib/assets/docs/article/grokking-concurrency/python/part-3.md +227 -227
- package/lib/assets/docs/article/grokking-concurrency/python/part-4.md +299 -299
- package/lib/assets/docs/article/grokking-concurrency/python/part-5.md +315 -315
- package/lib/assets/docs/article/grokking-concurrency/python/part-6.md +297 -297
- package/lib/assets/docs/article/grokking-concurrency/python/part-7.md +314 -314
- package/lib/assets/docs/article/grokking-concurrency/python/part-8.md +360 -360
- package/lib/assets/docs/article/grokking-concurrency/rust/index.md +270 -270
- package/lib/assets/docs/article/grokking-concurrency/rust/part-1.md +108 -108
- package/lib/assets/docs/article/grokking-concurrency/rust/part-2.md +120 -120
- package/lib/assets/docs/article/grokking-concurrency/rust/part-3.md +126 -126
- package/lib/assets/docs/article/grokking-concurrency/rust/part-4.md +175 -175
- package/lib/assets/docs/article/grokking-concurrency/rust/part-5.md +158 -158
- package/lib/assets/docs/article/grokking-concurrency/rust/part-6.md +94 -94
- package/lib/assets/docs/article/grokking-concurrency/rust/part-7.md +133 -133
- package/lib/assets/docs/article/grokking-concurrency/rust/part-8.md +155 -155
- package/lib/assets/docs/article/grokking-concurrency/scala/index.md +69 -69
- package/lib/assets/docs/article/grokking-concurrency/scala/part-1.md +78 -78
- package/lib/assets/docs/article/grokking-concurrency/scala/part-2.md +112 -112
- package/lib/assets/docs/article/grokking-concurrency/scala/part-3.md +93 -93
- package/lib/assets/docs/article/grokking-concurrency/scala/part-4.md +110 -110
- package/lib/assets/docs/article/grokking-concurrency/scala/part-5.md +119 -119
- package/lib/assets/docs/article/grokking-concurrency/scala/part-6.md +83 -83
- package/lib/assets/docs/article/grokking-concurrency/scala/part-7.md +131 -131
- package/lib/assets/docs/article/grokking-concurrency/scala/part-8.md +129 -129
- package/lib/assets/docs/article/grokkingfp/all/index.md +368 -368
- package/lib/assets/docs/article/grokkingfp/all/part-1-ch01-fp-introduction.md +530 -530
- package/lib/assets/docs/article/grokkingfp/all/part-1-ch02-pure-functions.md +923 -923
- package/lib/assets/docs/article/grokkingfp/all/part-2-ch03-immutable-data.md +1128 -1128
- package/lib/assets/docs/article/grokkingfp/all/part-2-ch04-higher-order-functions.md +1104 -1104
- package/lib/assets/docs/article/grokkingfp/all/part-2-ch05-flatmap.md +1026 -1026
- package/lib/assets/docs/article/grokkingfp/all/part-3-ch06-option.md +785 -785
- package/lib/assets/docs/article/grokkingfp/all/part-3-ch07-either-adt.md +871 -871
- package/lib/assets/docs/article/grokkingfp/all/part-4-ch08-io-monad.md +972 -972
- package/lib/assets/docs/article/grokkingfp/all/part-4-ch09-streams.md +926 -926
- package/lib/assets/docs/article/grokkingfp/all/part-5-ch10-concurrency.md +870 -870
- package/lib/assets/docs/article/grokkingfp/all/part-6-ch11-application.md +715 -715
- package/lib/assets/docs/article/grokkingfp/all/part-6-ch12-testing.md +626 -626
- package/lib/assets/docs/article/grokkingfp/all/writing-plan.md +712 -712
- package/lib/assets/docs/article/grokkingfp/clojure/index.md +276 -276
- package/lib/assets/docs/article/grokkingfp/clojure/part-1.md +667 -667
- package/lib/assets/docs/article/grokkingfp/clojure/part-2.md +643 -643
- package/lib/assets/docs/article/grokkingfp/clojure/part-3.md +620 -620
- package/lib/assets/docs/article/grokkingfp/clojure/part-4.md +697 -697
- package/lib/assets/docs/article/grokkingfp/clojure/part-5.md +751 -751
- package/lib/assets/docs/article/grokkingfp/clojure/part-6.md +721 -721
- package/lib/assets/docs/article/grokkingfp/csharp/index.md +246 -246
- package/lib/assets/docs/article/grokkingfp/csharp/part-1.md +811 -811
- package/lib/assets/docs/article/grokkingfp/csharp/part-2.md +971 -971
- package/lib/assets/docs/article/grokkingfp/csharp/part-3.md +981 -981
- package/lib/assets/docs/article/grokkingfp/csharp/part-4.md +949 -949
- package/lib/assets/docs/article/grokkingfp/csharp/part-5.md +947 -947
- package/lib/assets/docs/article/grokkingfp/csharp/part-6.md +739 -739
- package/lib/assets/docs/article/grokkingfp/elixir/index.md +203 -203
- package/lib/assets/docs/article/grokkingfp/elixir/part-1.md +712 -712
- package/lib/assets/docs/article/grokkingfp/elixir/part-2.md +838 -838
- package/lib/assets/docs/article/grokkingfp/elixir/part-3.md +985 -985
- package/lib/assets/docs/article/grokkingfp/elixir/part-4.md +974 -974
- package/lib/assets/docs/article/grokkingfp/elixir/part-5.md +1286 -1286
- package/lib/assets/docs/article/grokkingfp/elixir/part-6.md +1049 -1049
- package/lib/assets/docs/article/grokkingfp/fsharp/index.md +210 -210
- package/lib/assets/docs/article/grokkingfp/fsharp/part-1.md +714 -714
- package/lib/assets/docs/article/grokkingfp/fsharp/part-2.md +961 -961
- package/lib/assets/docs/article/grokkingfp/fsharp/part-3.md +972 -972
- package/lib/assets/docs/article/grokkingfp/fsharp/part-4.md +832 -832
- package/lib/assets/docs/article/grokkingfp/fsharp/part-5.md +911 -911
- package/lib/assets/docs/article/grokkingfp/fsharp/part-6.md +922 -922
- package/lib/assets/docs/article/grokkingfp/haskell/index.md +234 -234
- package/lib/assets/docs/article/grokkingfp/haskell/part-1.md +591 -591
- package/lib/assets/docs/article/grokkingfp/haskell/part-2.md +866 -866
- package/lib/assets/docs/article/grokkingfp/haskell/part-3.md +915 -915
- package/lib/assets/docs/article/grokkingfp/haskell/part-4.md +878 -878
- package/lib/assets/docs/article/grokkingfp/haskell/part-5.md +845 -845
- package/lib/assets/docs/article/grokkingfp/haskell/part-6.md +844 -844
- package/lib/assets/docs/article/grokkingfp/index.md +143 -143
- package/lib/assets/docs/article/grokkingfp/java/index.md +211 -211
- package/lib/assets/docs/article/grokkingfp/java/part-1.md +648 -648
- package/lib/assets/docs/article/grokkingfp/java/part-2.md +675 -675
- package/lib/assets/docs/article/grokkingfp/java/part-3.md +672 -672
- package/lib/assets/docs/article/grokkingfp/java/part-4.md +771 -771
- package/lib/assets/docs/article/grokkingfp/java/part-5.md +959 -959
- package/lib/assets/docs/article/grokkingfp/java/part-6.md +1328 -1328
- package/lib/assets/docs/article/grokkingfp/python/index.md +258 -258
- package/lib/assets/docs/article/grokkingfp/python/part-1.md +443 -443
- package/lib/assets/docs/article/grokkingfp/python/part-2.md +958 -958
- package/lib/assets/docs/article/grokkingfp/python/part-3.md +1004 -1004
- package/lib/assets/docs/article/grokkingfp/python/part-4.md +765 -765
- package/lib/assets/docs/article/grokkingfp/python/part-5.md +747 -747
- package/lib/assets/docs/article/grokkingfp/python/part-6.md +861 -861
- package/lib/assets/docs/article/grokkingfp/ruby/index.md +330 -330
- package/lib/assets/docs/article/grokkingfp/ruby/part-1.md +755 -755
- package/lib/assets/docs/article/grokkingfp/ruby/part-2.md +938 -938
- package/lib/assets/docs/article/grokkingfp/ruby/part-3.md +946 -946
- package/lib/assets/docs/article/grokkingfp/ruby/part-4.md +921 -921
- package/lib/assets/docs/article/grokkingfp/ruby/part-5.md +908 -908
- package/lib/assets/docs/article/grokkingfp/ruby/part-6.md +1412 -1412
- package/lib/assets/docs/article/grokkingfp/rust/index.md +242 -242
- package/lib/assets/docs/article/grokkingfp/rust/part-1.md +634 -634
- package/lib/assets/docs/article/grokkingfp/rust/part-2.md +1060 -1060
- package/lib/assets/docs/article/grokkingfp/rust/part-3.md +994 -994
- package/lib/assets/docs/article/grokkingfp/rust/part-4.md +573 -573
- package/lib/assets/docs/article/grokkingfp/rust/part-5.md +705 -705
- package/lib/assets/docs/article/grokkingfp/rust/part-6.md +508 -508
- package/lib/assets/docs/article/grokkingfp/scala/index.md +171 -171
- package/lib/assets/docs/article/grokkingfp/scala/part-1.md +543 -543
- package/lib/assets/docs/article/grokkingfp/scala/part-2.md +946 -946
- package/lib/assets/docs/article/grokkingfp/scala/part-3.md +919 -919
- package/lib/assets/docs/article/grokkingfp/scala/part-4.md +742 -742
- package/lib/assets/docs/article/grokkingfp/scala/part-5.md +722 -722
- package/lib/assets/docs/article/grokkingfp/scala/part-6.md +867 -867
- package/lib/assets/docs/article/grokkingfp/typescript/index.md +273 -273
- package/lib/assets/docs/article/grokkingfp/typescript/part-1.md +561 -561
- package/lib/assets/docs/article/grokkingfp/typescript/part-2.md +1129 -1129
- package/lib/assets/docs/article/grokkingfp/typescript/part-3.md +842 -842
- package/lib/assets/docs/article/grokkingfp/typescript/part-4.md +1087 -1087
- package/lib/assets/docs/article/grokkingfp/typescript/part-5.md +717 -717
- package/lib/assets/docs/article/grokkingfp/typescript/part-6.md +982 -982
- package/lib/assets/docs/article/practical-database-design/index.md +121 -121
- package/lib/assets/docs/article/practical-database-design/part1/chapter01.md +288 -288
- package/lib/assets/docs/article/practical-database-design/part1/chapter02.md +518 -518
- package/lib/assets/docs/article/practical-database-design/part1/chapter03.md +557 -557
- package/lib/assets/docs/article/practical-database-design/part2/chapter04.md +924 -924
- package/lib/assets/docs/article/practical-database-design/part2/chapter05.md +1627 -1627
- package/lib/assets/docs/article/practical-database-design/part2/chapter06.md +2716 -2716
- package/lib/assets/docs/article/practical-database-design/part2/chapter07.md +2082 -2082
- package/lib/assets/docs/article/practical-database-design/part2/chapter08.md +2105 -2105
- package/lib/assets/docs/article/practical-database-design/part2/chapter09.md +2031 -2031
- package/lib/assets/docs/article/practical-database-design/part2/chapter10.md +1387 -1387
- package/lib/assets/docs/article/practical-database-design/part2/chapter11.md +1677 -1677
- package/lib/assets/docs/article/practical-database-design/part2/chapter12.md +1417 -1417
- package/lib/assets/docs/article/practical-database-design/part2/chapter13.md +1434 -1434
- package/lib/assets/docs/article/practical-database-design/part3/chapter14.md +667 -667
- package/lib/assets/docs/article/practical-database-design/part3/chapter15.md +1625 -1625
- package/lib/assets/docs/article/practical-database-design/part3/chapter16.md +1915 -1915
- package/lib/assets/docs/article/practical-database-design/part3/chapter17.md +1708 -1708
- package/lib/assets/docs/article/practical-database-design/part3/chapter18.md +2095 -2095
- package/lib/assets/docs/article/practical-database-design/part3/chapter19.md +1123 -1123
- package/lib/assets/docs/article/practical-database-design/part3/chapter20.md +1031 -1031
- package/lib/assets/docs/article/practical-database-design/part3/chapter21.md +1382 -1382
- package/lib/assets/docs/article/practical-database-design/part3-orm/chapter14-orm.md +991 -991
- package/lib/assets/docs/article/practical-database-design/part3-orm/chapter15-orm.md +1300 -1300
- package/lib/assets/docs/article/practical-database-design/part3-orm/chapter16-orm.md +1166 -1166
- package/lib/assets/docs/article/practical-database-design/part3-orm/chapter17-orm.md +1584 -1584
- package/lib/assets/docs/article/practical-database-design/part3-orm/chapter18-orm.md +1183 -1183
- package/lib/assets/docs/article/practical-database-design/part3-orm/chapter19-orm.md +1016 -1016
- package/lib/assets/docs/article/practical-database-design/part3-orm/chapter20-orm.md +1753 -1753
- package/lib/assets/docs/article/practical-database-design/part3-orm/chapter21-orm.md +1447 -1447
- package/lib/assets/docs/article/practical-database-design/part3-orm/chapter22-orm.md +1878 -1878
- package/lib/assets/docs/article/practical-database-design/part4/chapter22.md +965 -965
- package/lib/assets/docs/article/practical-database-design/part4/chapter23.md +2069 -2069
- package/lib/assets/docs/article/practical-database-design/part4/chapter24.md +2439 -2439
- package/lib/assets/docs/article/practical-database-design/part4/chapter25.md +3661 -3661
- package/lib/assets/docs/article/practical-database-design/part4/chapter26.md +2916 -2916
- package/lib/assets/docs/article/practical-database-design/part4/chapter27.md +3105 -3105
- package/lib/assets/docs/article/practical-database-design/part4/chapter28.md +2697 -2697
- package/lib/assets/docs/article/practical-database-design/part4/chapter29.md +2544 -2544
- package/lib/assets/docs/article/practical-database-design/part4/chapter30.md +2180 -2180
- package/lib/assets/docs/article/practical-database-design/part4/chapter31.md +1192 -1192
- package/lib/assets/docs/article/practical-database-design/part4/chapter32.md +2101 -2101
- package/lib/assets/docs/article/practical-database-design/part5/chapter33.md +1032 -1032
- package/lib/assets/docs/article/practical-database-design/part5/chapter34.md +1609 -1609
- package/lib/assets/docs/article/practical-database-design/part5/chapter35.md +1453 -1453
- package/lib/assets/docs/article/practical-database-design/part5/chapter36.md +1292 -1292
- package/lib/assets/docs/article/practical-database-design/part5/chapter37.md +1470 -1470
- package/lib/assets/docs/article/practical-database-design/part5/chapter38.md +1698 -1698
- package/lib/assets/docs/article/practical-database-design/part5/chapter39.md +2334 -2334
- package/lib/assets/docs/article/practical-database-design/study/study2-1.md +1693 -1693
- package/lib/assets/docs/article/practical-database-design/study/study2-2.md +1347 -1347
- package/lib/assets/docs/article/practical-database-design/study/study2-3.md +2044 -2044
- package/lib/assets/docs/article/practical-database-design/study/study2-4.md +2229 -2229
- package/lib/assets/docs/article/practical-database-design/study/study2-5.md +2418 -2418
- package/lib/assets/docs/article/practical-database-design/study/study3-1.md +2205 -2205
- package/lib/assets/docs/article/practical-database-design/study/study3-2.md +2221 -2221
- package/lib/assets/docs/article/practical-database-design/study/study3-3.md +2253 -2253
- package/lib/assets/docs/article/practical-database-design/study/study3-4.md +2106 -2106
- package/lib/assets/docs/article/practical-database-design/study/study3-5.md +2507 -2507
- package/lib/assets/docs/article/practical-database-design/study/study4-1.md +2587 -2587
- package/lib/assets/docs/article/practical-database-design/study/study4-2.md +2075 -2075
- package/lib/assets/docs/article/practical-database-design/study/study4-3.md +1805 -1805
- package/lib/assets/docs/article/practical-database-design/study/study4-4.md +1895 -1895
- package/lib/assets/docs/article/practical-database-design/study/study4-5.md +2878 -2878
- package/lib/assets/docs/assets/css/extra.css +29 -29
- package/lib/assets/docs/assets/js/extra.js +44 -44
- package/lib/assets/docs/development/index.md +39 -39
- package/lib/assets/docs/operation/index.md +11 -11
- package/lib/assets/docs/reference/CodexCLIMCP/343/202/242/343/203/227/343/203/252/343/202/261/343/203/274/343/202/267/343/203/247/343/203/263/351/226/213/347/231/272/343/203/225/343/203/255/343/203/274.md +532 -532
- package/lib/assets/docs/reference/CodexCLIMCP/343/202/265/343/203/274/343/203/220/343/203/274/350/250/255/345/256/232/346/211/213/351/240/206.md +341 -341
- package/lib/assets/docs/reference/Java/343/202/242/343/203/227/343/203/252/343/202/261/343/203/274/343/202/267/343/203/247/343/203/263/347/222/260/345/242/203/346/247/213/347/257/211/343/202/254/343/202/244/343/203/211.md +581 -580
- package/lib/assets/docs/reference/SonarQube/343/203/255/343/203/274/343/202/253/343/203/253/347/222/260/345/242/203/343/202/273/343/203/203/343/203/210/343/202/242/343/203/203/343/203/227/346/211/213/351/240/206/346/233/270.md +642 -642
- package/lib/assets/docs/reference/TypeScript/343/202/242/343/203/227/343/203/252/343/202/261/343/203/274/343/202/267/343/203/247/343/203/263/347/222/260/345/242/203/346/247/213/347/257/211/343/202/254/343/202/244/343/203/211.md +465 -465
- package/lib/assets/docs/reference/UI/350/250/255/350/250/210/343/202/254/343/202/244/343/203/211.md +450 -450
- package/lib/assets/docs/reference/images/Ansoff.drawio.svg +3 -3
- package/lib/assets/docs/reference/images/BrandBasicStrategy.drawio.svg +3 -3
- package/lib/assets/docs/reference/images/BrandCategorization.drawio.svg +3 -3
- package/lib/assets/docs/reference/images/BrandRecurutementStrategy.drawio.svg +3 -3
- package/lib/assets/docs/reference/images/BrandValue.drawio.svg +3 -3
- package/lib/assets/docs/reference/images/BusinessActivitiy.svg +3 -3
- package/lib/assets/docs/reference/images/HRM.drawio.svg +3 -3
- package/lib/assets/docs/reference/images/MarketingStructure.drawio.svg +3 -3
- package/lib/assets/docs/reference/images/OrganizationElemnts.svg +3 -3
- package/lib/assets/docs/reference/images/PPM.drawio.svg +3 -3
- package/lib/assets/docs/reference/images/PositioningMap.drawio.svg +3 -3
- package/lib/assets/docs/reference/images/ProductLayer.drawio.svg +3 -3
- package/lib/assets/docs/reference/images/ProductMix.drawio.svg +3 -3
- package/lib/assets/docs/reference/images/SWOT.drawio.svg +3 -3
- package/lib/assets/docs/reference/images/TargetMarket.drawio.svg +3 -3
- package/lib/assets/docs/reference/images/ThreeGenericStrategies.drawio.svg +3 -3
- package/lib/assets/docs/reference/images/VRIO.drawio.svg +3 -3
- package/lib/assets/docs/reference/images/ValueChain.drawio.svg +3 -3
- package/lib/assets/docs/reference/index.md +52 -52
- package/lib/assets/docs/reference//343/202/210/343/201/204/343/202/275/343/203/225/343/203/210/343/202/246/343/202/247/343/202/242/343/201/250/343/201/257.md +250 -242
- package/lib/assets/docs/reference//343/202/242/343/203/274/343/202/255/343/203/206/343/202/257/343/203/201/343/203/243/350/250/255/350/250/210/343/202/254/343/202/244/343/203/211.md +2216 -2216
- package/lib/assets/docs/reference//343/202/244/343/203/263/343/203/225/343/203/251/350/250/255/350/250/210/343/202/254/343/202/244/343/203/211.md +1878 -1878
- package/lib/assets/docs/reference//343/202/250/343/202/257/343/202/271/343/203/210/343/203/252/343/203/274/343/203/240/343/203/227/343/203/255/343/202/260/343/203/251/343/203/237/343/203/263/343/202/260.md +550 -544
- package/lib/assets/docs/reference//343/202/263/343/203/274/343/203/207/343/202/243/343/203/263/343/202/260/343/201/250/343/203/206/343/202/271/343/203/210/343/202/254/343/202/244/343/203/211.md +705 -705
- package/lib/assets/docs/reference//343/203/206/343/202/271/343/203/210/346/210/246/347/225/245/343/202/254/343/202/244/343/203/211.md +1313 -1313
- package/lib/assets/docs/reference//343/203/207/343/203/274/343/202/277/343/203/242/343/203/207/343/203/253/350/250/255/350/250/210/343/202/254/343/202/244/343/203/211.md +311 -311
- package/lib/assets/docs/reference//343/203/211/343/203/241/343/202/244/343/203/263/343/203/242/343/203/207/343/203/253/350/250/255/350/250/210/343/202/254/343/202/244/343/203/211.md +599 -599
- package/lib/assets/docs/reference//343/203/223/343/202/270/343/203/215/343/202/271/343/202/242/343/203/274/343/202/255/343/203/206/343/202/257/343/203/201/343/203/243/345/210/206/346/236/220/343/202/254/343/202/244/343/203/211.md +528 -528
- package/lib/assets/docs/reference//343/203/246/343/203/274/343/202/271/343/202/261/343/203/274/343/202/271/344/275/234/346/210/220/343/202/254/343/202/244/343/203/211.md +689 -682
- package/lib/assets/docs/reference//343/203/252/343/203/252/343/203/274/343/202/271/343/202/254/343/202/244/343/203/211.md +461 -461
- package/lib/assets/docs/reference//343/203/252/343/203/252/343/203/274/343/202/271/343/203/273/343/202/244/343/203/206/343/203/254/343/203/274/343/202/267/343/203/247/343/203/263/350/250/210/347/224/273/343/202/254/343/202/244/343/203/211.md +580 -560
- package/lib/assets/docs/reference//343/203/255/343/202/270/343/202/253/343/203/253/343/202/267/343/203/263/343/202/255/343/203/263/343/202/260.md +1367 -1367
- package/lib/assets/docs/reference//344/274/201/346/245/255/347/265/214/345/226/266/350/253/226.md +2637 -2636
- package/lib/assets/docs/reference//347/222/260/345/242/203/345/244/211/346/225/260/347/256/241/347/220/206/343/202/254/343/202/244/343/203/211.md +665 -663
- package/lib/assets/docs/reference//350/246/201/344/273/266/345/256/232/347/276/251/343/202/254/343/202/244/343/203/211.md +1248 -1248
- package/lib/assets/docs/reference//350/250/200/350/252/236/345/210/245/351/226/213/347/231/272/343/202/254/343/202/244/343/203/211.md +28 -0
- package/lib/assets/docs/reference//351/201/213/345/226/266/347/256/241/347/220/206.md +1482 -1482
- package/lib/assets/docs/reference//351/201/213/347/224/250/343/202/271/343/202/257/343/203/252/343/203/227/343/203/210/344/275/234/346/210/220/343/202/254/343/202/244/343/203/211.md +421 -421
- package/lib/assets/docs/reference//351/201/213/347/224/250/350/246/201/344/273/266/345/256/232/347/276/251/343/202/254/343/202/244/343/203/211.md +392 -392
- package/lib/assets/docs/reference//351/226/213/347/231/272/343/202/254/343/202/244/343/203/211.md +299 -299
- package/lib/assets/docs/reference//351/235/236/346/251/237/350/203/275/350/246/201/344/273/266/345/256/232/347/276/251/343/202/254/343/202/244/343/203/211.md +1236 -1236
- package/lib/assets/docs/review/index.md +5 -5
- package/lib/assets/docs/strategy/index.md +1 -1
- package/lib/assets/docs/template/ADR.md +30 -30
- package/lib/assets/docs/template/AWS/343/202/271/343/203/206/343/203/274/343/202/270/343/203/263/343/202/260/347/222/260/345/242/203/343/202/273/343/203/203/343/203/210/343/202/242/343/203/203/343/203/227/346/211/213/351/240/206/346/233/270.md +1366 -1366
- package/lib/assets/docs/template/AWS/343/203/227/343/203/255/343/203/200/343/202/257/343/202/267/343/203/247/343/203/263/347/222/260/345/242/203/343/202/273/343/203/203/343/203/210/343/202/242/343/203/203/343/203/227/346/211/213/351/240/206/346/233/270.md +634 -634
- package/lib/assets/docs/template/README.md +50 -50
- package/lib/assets/docs/template/index.md +23 -23
- package/lib/assets/docs/template//343/201/276/343/201/232/343/201/223/343/202/214/343/202/222/350/252/255/343/202/202/343/201/206/343/203/252/343/202/271/343/203/210.md +12 -12
- package/lib/assets/docs/template//343/202/242/343/203/227/343/203/252/343/202/261/343/203/274/343/202/267/343/203/247/343/203/263/351/226/213/347/231/272/347/222/260/345/242/203/343/202/273/343/203/203/343/203/210/343/202/242/343/203/203/343/203/227/346/211/213/351/240/206/346/233/270.md +547 -547
- package/lib/assets/docs/template//343/202/244/343/203/206/343/203/254/343/203/274/343/202/267/343/203/247/343/203/263/345/256/214/344/272/206/345/240/261/345/221/212/346/233/270.md +58 -58
- package/lib/assets/docs/template//343/202/244/343/203/263/343/202/273/343/203/227/343/202/267/343/203/247/343/203/263/343/203/207/343/203/203/343/202/255.md +13 -13
- package/lib/assets/docs/template//343/203/223/343/202/270/343/203/215/343/202/271/343/202/242/343/203/274/343/202/255/343/203/206/343/202/257/343/203/201/343/203/243.md +379 -379
- package/lib/assets/docs/template//344/274/201/346/245/255/345/210/206/346/236/220.md +573 -573
- package/lib/assets/docs/template//345/256/214/345/205/250/345/275/242/345/274/217/343/201/256/343/203/246/343/203/274/343/202/271/343/202/261/343/203/274/343/202/271.md +69 -68
- package/lib/assets/docs/template//350/246/201/344/273/266/345/256/232/347/276/251.md +669 -669
- package/lib/assets/docs/template//350/250/255/350/250/210.md +173 -173
- package/lib/assets/docs/template//351/226/213/347/231/272/347/222/260/345/242/203/343/202/273/343/203/203/343/203/210/343/202/242/343/203/203/343/203/227/346/211/213/351/240/206/346/233/270.md +688 -688
- package/lib/assets/gulpfile.js +25 -25
- package/lib/assets/mkdocs.yml +136 -135
- package/lib/assets/ops/docker/mkdoc/Dockerfile +19 -19
- package/lib/assets/ops/scripts/journal.js +180 -180
- package/lib/assets/ops/scripts/mkdocs.js +82 -82
- package/lib/assets/ops/scripts/release.js +431 -431
- package/lib/assets/ops/scripts/sonar_local.js +726 -726
- package/lib/assets/ops/scripts/ssh.js +190 -190
- package/lib/assets/ops/scripts/vault.js +299 -299
- package/lib/assets/package-lock.json +1653 -1653
- package/lib/assets/package.json +40 -40
- package/lib/gulpfile.js +37 -37
- package/package.json +41 -41
- package/lib/assets/.claude/agent-memory/xp-programmer/MEMORY.md +0 -6
- package/lib/assets/.claude/agent-memory/xp-programmer/project_cargo_tracker.md +0 -11
- package/lib/assets/.claude/agent-memory/xp-programmer/project_ddd_patterns.md +0 -27
- package/lib/assets/.claude/agent-memory/xp-programmer/project_us07_route_assignment.md +0 -19
|
@@ -1,672 +1,672 @@
|
|
|
1
|
-
# Part III: エラーハンドリングと Option/Either
|
|
2
|
-
|
|
3
|
-
本章では、関数型プログラミングにおける安全なエラーハンドリングを学びます。`null` や例外に頼らず、Vavr の `Option` と `Either` を使って型安全にエラーを扱う方法を習得します。
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## 第6章: Option 型による安全なエラーハンドリング
|
|
8
|
-
|
|
9
|
-
### 6.1 なぜ Option が必要か
|
|
10
|
-
|
|
11
|
-
従来のエラーハンドリングには問題があります。
|
|
12
|
-
|
|
13
|
-
```plantuml
|
|
14
|
-
@startuml
|
|
15
|
-
!theme plain
|
|
16
|
-
|
|
17
|
-
rectangle "従来のエラーハンドリング" {
|
|
18
|
-
rectangle "null を返す" as null_ret {
|
|
19
|
-
card "NullPointerException の危険"
|
|
20
|
-
card "コンパイル時に検出できない"
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
rectangle "例外をスローする" as exception {
|
|
24
|
-
card "制御フローが複雑化"
|
|
25
|
-
card "純粋関数ではなくなる"
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
rectangle "Option による解決" as option {
|
|
30
|
-
card "型で「値がないかもしれない」を表現"
|
|
31
|
-
card "コンパイル時にチェック"
|
|
32
|
-
card "純粋関数のまま"
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
null_ret --> option : 置き換え
|
|
36
|
-
exception --> option : 置き換え
|
|
37
|
-
|
|
38
|
-
@enduml
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
### 6.2 Option の基本
|
|
42
|
-
|
|
43
|
-
`Option<A>` は「`A` 型の値があるか、ないか」を表す型です。
|
|
44
|
-
|
|
45
|
-
```plantuml
|
|
46
|
-
@startuml
|
|
47
|
-
!theme plain
|
|
48
|
-
|
|
49
|
-
rectangle "Option<A>" as option {
|
|
50
|
-
rectangle "Some(value)" as some {
|
|
51
|
-
card "値が存在する"
|
|
52
|
-
card "value: A を保持"
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
rectangle "None" as none {
|
|
56
|
-
card "値が存在しない"
|
|
57
|
-
card "null の代わり"
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
@enduml
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
**ソースファイル**: `app/java/src/main/java/ch06/OptionBasics.java`
|
|
65
|
-
|
|
66
|
-
```java
|
|
67
|
-
import io.vavr.control.Option;
|
|
68
|
-
|
|
69
|
-
// Some: 値が存在する
|
|
70
|
-
Option<Integer> some = Option.some(42);
|
|
71
|
-
|
|
72
|
-
// None: 値が存在しない
|
|
73
|
-
Option<Integer> none = Option.none();
|
|
74
|
-
|
|
75
|
-
// null から安全に作成
|
|
76
|
-
Option<String> fromNull = Option.of(maybeNull); // null なら None
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
### 6.3 TV番組のパース例
|
|
80
|
-
|
|
81
|
-
**ソースファイル**: `app/java/src/main/java/ch06/TvShowParser.java`
|
|
82
|
-
|
|
83
|
-
TV番組の文字列をパースする例で Option の使い方を学びます。
|
|
84
|
-
|
|
85
|
-
```java
|
|
86
|
-
public record TvShow(String title, int start, int end) {}
|
|
87
|
-
|
|
88
|
-
// 入力例: "Breaking Bad (2008-2013)"
|
|
89
|
-
// 期待する出力: TvShow("Breaking Bad", 2008, 2013)
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
#### 例外を使う方法(問題あり)
|
|
93
|
-
|
|
94
|
-
```java
|
|
95
|
-
public static TvShow parseShowUnsafe(String rawShow) {
|
|
96
|
-
int bracketOpen = rawShow.indexOf('(');
|
|
97
|
-
int bracketClose = rawShow.indexOf(')');
|
|
98
|
-
int dash = rawShow.indexOf('-');
|
|
99
|
-
|
|
100
|
-
String name = rawShow.substring(0, bracketOpen).trim();
|
|
101
|
-
int yearStart = Integer.parseInt(rawShow.substring(bracketOpen + 1, dash));
|
|
102
|
-
int yearEnd = Integer.parseInt(rawShow.substring(dash + 1, bracketClose));
|
|
103
|
-
|
|
104
|
-
return new TvShow(name, yearStart, yearEnd);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// 正常ケース
|
|
108
|
-
parseShowUnsafe("Breaking Bad (2008-2013)"); // TvShow("Breaking Bad", 2008, 2013)
|
|
109
|
-
|
|
110
|
-
// 異常ケース → 例外がスローされる!
|
|
111
|
-
parseShowUnsafe("Chernobyl (2019)"); // StringIndexOutOfBoundsException
|
|
112
|
-
parseShowUnsafe("The Wire 2002-2008"); // StringIndexOutOfBoundsException
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
#### Option を使う方法
|
|
116
|
-
|
|
117
|
-
```java
|
|
118
|
-
public static Option<TvShow> parseShow(String rawShow) {
|
|
119
|
-
return extractName(rawShow).flatMap(name ->
|
|
120
|
-
extractYearStart(rawShow).orElse(() -> extractSingleYear(rawShow)).flatMap(yearStart ->
|
|
121
|
-
extractYearEnd(rawShow).orElse(() -> extractSingleYear(rawShow)).map(yearEnd ->
|
|
122
|
-
new TvShow(name, yearStart, yearEnd)
|
|
123
|
-
)
|
|
124
|
-
)
|
|
125
|
-
);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// 正常ケース
|
|
129
|
-
parseShow("Breaking Bad (2008-2013)"); // Some(TvShow("Breaking Bad", 2008, 2013))
|
|
130
|
-
|
|
131
|
-
// 異常ケース → None が返される(例外なし)
|
|
132
|
-
parseShow("The Wire 2002-2008"); // None
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
### 6.4 小さな関数から組み立てる
|
|
136
|
-
|
|
137
|
-
複雑なパース処理を小さな関数に分解します。
|
|
138
|
-
|
|
139
|
-
```java
|
|
140
|
-
// 名前を抽出
|
|
141
|
-
public static Option<String> extractName(String rawShow) {
|
|
142
|
-
int bracketOpen = rawShow.indexOf('(');
|
|
143
|
-
if (bracketOpen > 0) {
|
|
144
|
-
return Option.some(rawShow.substring(0, bracketOpen).trim());
|
|
145
|
-
}
|
|
146
|
-
return Option.none();
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// 開始年を抽出
|
|
150
|
-
public static Option<Integer> extractYearStart(String rawShow) {
|
|
151
|
-
int bracketOpen = rawShow.indexOf('(');
|
|
152
|
-
int dash = rawShow.indexOf('-');
|
|
153
|
-
|
|
154
|
-
if (bracketOpen != -1 && dash > bracketOpen + 1) {
|
|
155
|
-
String yearStr = rawShow.substring(bracketOpen + 1, dash);
|
|
156
|
-
return parseYear(yearStr);
|
|
157
|
-
}
|
|
158
|
-
return Option.none();
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
// 文字列を年(整数)にパース
|
|
162
|
-
public static Option<Integer> parseYear(String yearStr) {
|
|
163
|
-
try {
|
|
164
|
-
return Option.some(Integer.parseInt(yearStr.trim()));
|
|
165
|
-
} catch (NumberFormatException e) {
|
|
166
|
-
return Option.none();
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
### 6.5 orElse によるフォールバック
|
|
172
|
-
|
|
173
|
-
`orElse` を使って、最初の Option が `None` の場合に代替を試すことができます。
|
|
174
|
-
|
|
175
|
-
```java
|
|
176
|
-
Option<Integer> seven = Option.some(7);
|
|
177
|
-
Option<Integer> eight = Option.some(8);
|
|
178
|
-
Option<Integer> none = Option.none();
|
|
179
|
-
|
|
180
|
-
seven.orElse(eight); // Some(7) - 最初が Some なのでそのまま
|
|
181
|
-
none.orElse(eight); // Some(8) - 最初が None なので代替を使用
|
|
182
|
-
seven.orElse(none); // Some(7)
|
|
183
|
-
none.orElse(none); // None
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
#### 単年の番組に対応する
|
|
187
|
-
|
|
188
|
-
「Chernobyl (2019)」のような単年の番組をパースできるようにします。
|
|
189
|
-
|
|
190
|
-
```java
|
|
191
|
-
public static Option<Integer> extractSingleYear(String rawShow) {
|
|
192
|
-
int dash = rawShow.indexOf('-');
|
|
193
|
-
int bracketOpen = rawShow.indexOf('(');
|
|
194
|
-
int bracketClose = rawShow.indexOf(')');
|
|
195
|
-
|
|
196
|
-
if (dash == -1 && bracketOpen != -1 && bracketClose > bracketOpen + 1) {
|
|
197
|
-
String yearStr = rawShow.substring(bracketOpen + 1, bracketClose);
|
|
198
|
-
return parseYear(yearStr);
|
|
199
|
-
}
|
|
200
|
-
return Option.none();
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// これで単年の番組もパースできる
|
|
204
|
-
parseShow("Chernobyl (2019)"); // Some(TvShow("Chernobyl", 2019, 2019))
|
|
205
|
-
```
|
|
206
|
-
|
|
207
|
-
### 6.6 Option の主要メソッド
|
|
208
|
-
|
|
209
|
-
| メソッド | 説明 | 例 |
|
|
210
|
-
|----------|------|-----|
|
|
211
|
-
| `map` | 値があれば変換 | `Some(5).map(x -> x * 2)` → `Some(10)` |
|
|
212
|
-
| `flatMap` | 値があれば Option を返す関数を適用 | `Some(5).flatMap(x -> Some(x * 2))` → `Some(10)` |
|
|
213
|
-
| `filter` | 条件を満たさなければ None | `Some(5).filter(x -> x > 10)` → `None` |
|
|
214
|
-
| `orElse` | None なら代替を使用 | `None.orElse(Some(5))` → `Some(5)` |
|
|
215
|
-
| `getOrElse` | None ならデフォルト値 | `None.getOrElse(0)` → `0` |
|
|
216
|
-
| `toList` | List に変換 | `Some(5).toList()` → `List(5)` |
|
|
217
|
-
| `toEither` | Either に変換 | `Some(5).toEither("error")` → `Right(5)` |
|
|
218
|
-
|
|
219
|
-
```java
|
|
220
|
-
Option<Integer> year = Option.some(996);
|
|
221
|
-
Option<Integer> noYear = Option.none();
|
|
222
|
-
|
|
223
|
-
// map
|
|
224
|
-
year.map(y -> y * 2); // Some(1992)
|
|
225
|
-
noYear.map(y -> y * 2); // None
|
|
226
|
-
|
|
227
|
-
// flatMap
|
|
228
|
-
year.flatMap(y -> Option.some(y * 2)); // Some(1992)
|
|
229
|
-
noYear.flatMap(y -> Option.some(y * 2)); // None
|
|
230
|
-
|
|
231
|
-
// filter
|
|
232
|
-
year.filter(y -> y < 2020); // Some(996)
|
|
233
|
-
year.filter(y -> y > 2020); // None
|
|
234
|
-
|
|
235
|
-
// getOrElse
|
|
236
|
-
year.getOrElse(2020); // 996
|
|
237
|
-
noYear.getOrElse(2020); // 2020
|
|
238
|
-
```
|
|
239
|
-
|
|
240
|
-
### 6.7 エラーハンドリング戦略
|
|
241
|
-
|
|
242
|
-
複数の要素をパースする場合、2つの戦略があります。
|
|
243
|
-
|
|
244
|
-
```plantuml
|
|
245
|
-
@startuml
|
|
246
|
-
!theme plain
|
|
247
|
-
|
|
248
|
-
rectangle "エラーハンドリング戦略" {
|
|
249
|
-
rectangle "Best-effort 戦略" as best {
|
|
250
|
-
card "パースできたものだけ返す"
|
|
251
|
-
card "エラーは無視"
|
|
252
|
-
card "List<TvShow> を返す"
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
rectangle "All-or-nothing 戦略" as all {
|
|
256
|
-
card "全部成功するか、全部失敗"
|
|
257
|
-
card "一つでも失敗したら None"
|
|
258
|
-
card "Option<List<TvShow>> を返す"
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
@enduml
|
|
263
|
-
```
|
|
264
|
-
|
|
265
|
-
#### Best-effort 戦略
|
|
266
|
-
|
|
267
|
-
```java
|
|
268
|
-
public static List<TvShow> parseShowsBestEffort(List<String> rawShows) {
|
|
269
|
-
return rawShows
|
|
270
|
-
.map(TvShowParser::parseShow)
|
|
271
|
-
.flatMap(Option::toList);
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
List<String> rawShows = List.of(
|
|
275
|
-
"Breaking Bad (2008-2013)",
|
|
276
|
-
"The Wire 2002 2008", // 無効な形式
|
|
277
|
-
"Mad Men (2007-2015)"
|
|
278
|
-
);
|
|
279
|
-
|
|
280
|
-
parseShowsBestEffort(rawShows);
|
|
281
|
-
// List(TvShow("Breaking Bad", 2008, 2013), TvShow("Mad Men", 2007, 2015))
|
|
282
|
-
// 無効なものは無視される
|
|
283
|
-
```
|
|
284
|
-
|
|
285
|
-
#### All-or-nothing 戦略
|
|
286
|
-
|
|
287
|
-
```java
|
|
288
|
-
public static Option<List<TvShow>> parseShowsAllOrNothing(List<String> rawShows) {
|
|
289
|
-
Option<List<TvShow>> initial = Option.some(List.empty());
|
|
290
|
-
|
|
291
|
-
return rawShows
|
|
292
|
-
.map(TvShowParser::parseShow)
|
|
293
|
-
.foldLeft(initial, (acc, optShow) ->
|
|
294
|
-
acc.flatMap(shows ->
|
|
295
|
-
optShow.map(shows::append)
|
|
296
|
-
)
|
|
297
|
-
);
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
// 全部成功 → Some(List(...))
|
|
301
|
-
parseShowsAllOrNothing(List.of("Breaking Bad (2008-2013)", "Mad Men (2007-2015)"));
|
|
302
|
-
// Some(List(TvShow(...), TvShow(...)))
|
|
303
|
-
|
|
304
|
-
// 一つでも失敗 → None
|
|
305
|
-
parseShowsAllOrNothing(List.of("Breaking Bad (2008-2013)", "Invalid"));
|
|
306
|
-
// None
|
|
307
|
-
```
|
|
308
|
-
|
|
309
|
-
### 6.8 forall と exists
|
|
310
|
-
|
|
311
|
-
Option で条件判定をする際に便利なメソッドです。
|
|
312
|
-
|
|
313
|
-
| メソッド | Option での動作 |
|
|
314
|
-
|----------|-----------------|
|
|
315
|
-
| `forAll(p)` | None → true, Some(x) → p(x) |
|
|
316
|
-
| `exists(p)` | None → false, Some(x) → p(x) |
|
|
317
|
-
|
|
318
|
-
```java
|
|
319
|
-
Option<Integer> year = Option.some(996);
|
|
320
|
-
Option<Integer> noYear = Option.none();
|
|
321
|
-
|
|
322
|
-
// forAll - 「全て」または「存在しない」
|
|
323
|
-
year.forAll(y -> y < 2020); // true (996 < 2020)
|
|
324
|
-
noYear.forAll(y -> y < 2020); // true (値がないので「全て」が自明に真)
|
|
325
|
-
year.forAll(y -> y > 2020); // false
|
|
326
|
-
|
|
327
|
-
// exists - 「存在して条件を満たす」
|
|
328
|
-
year.exists(y -> y < 2020); // true
|
|
329
|
-
noYear.exists(y -> y < 2020); // false (値がないので存在しない)
|
|
330
|
-
```
|
|
331
|
-
|
|
332
|
-
---
|
|
333
|
-
|
|
334
|
-
## 第7章: Either 型と複合的なエラー処理
|
|
335
|
-
|
|
336
|
-
### 7.1 Option の限界
|
|
337
|
-
|
|
338
|
-
`Option` は「値があるかないか」しか表現できません。**なぜ失敗したのか**を伝えられません。
|
|
339
|
-
|
|
340
|
-
```plantuml
|
|
341
|
-
@startuml
|
|
342
|
-
!theme plain
|
|
343
|
-
|
|
344
|
-
rectangle "Option vs Either" {
|
|
345
|
-
rectangle "Option<A>" as opt {
|
|
346
|
-
card "Some(value)" as some
|
|
347
|
-
card "None" as none
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
rectangle "Either<E, A>" as either {
|
|
351
|
-
card "Right(value)" as right
|
|
352
|
-
card "Left(error)" as left
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
opt --> either : 進化
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
note bottom of none
|
|
359
|
-
失敗理由が分からない
|
|
360
|
-
end note
|
|
361
|
-
|
|
362
|
-
note bottom of left
|
|
363
|
-
エラー情報を保持できる
|
|
364
|
-
end note
|
|
365
|
-
|
|
366
|
-
@enduml
|
|
367
|
-
```
|
|
368
|
-
|
|
369
|
-
### 7.2 Either の基本
|
|
370
|
-
|
|
371
|
-
**ソースファイル**: `app/java/src/main/java/ch07/EitherBasics.java`
|
|
372
|
-
|
|
373
|
-
`Either<E, A>` は「`E` 型のエラーか、`A` 型の成功値か」を表す型です。
|
|
374
|
-
|
|
375
|
-
- `Right(value)`: 成功(慣例的に「正しい」= right)
|
|
376
|
-
- `Left(error)`: 失敗(エラー情報を保持)
|
|
377
|
-
|
|
378
|
-
```java
|
|
379
|
-
import io.vavr.control.Either;
|
|
380
|
-
|
|
381
|
-
public static Either<String, String> extractName(String show) {
|
|
382
|
-
int bracketOpen = show.indexOf('(');
|
|
383
|
-
if (bracketOpen > 0) {
|
|
384
|
-
return Either.right(show.substring(0, bracketOpen).trim());
|
|
385
|
-
}
|
|
386
|
-
return Either.left("Can't extract name from: " + show);
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
extractName("The Wire (2002-2008)"); // Right("The Wire")
|
|
390
|
-
extractName("(2022)"); // Left("Can't extract name from: (2022)")
|
|
391
|
-
```
|
|
392
|
-
|
|
393
|
-
### 7.3 Either を使ったパース
|
|
394
|
-
|
|
395
|
-
**ソースファイル**: `app/java/src/main/java/ch07/TvShowParserEither.java`
|
|
396
|
-
|
|
397
|
-
```java
|
|
398
|
-
public static Either<String, Integer> extractYearStart(String rawShow) {
|
|
399
|
-
int bracketOpen = rawShow.indexOf('(');
|
|
400
|
-
int dash = rawShow.indexOf('-');
|
|
401
|
-
|
|
402
|
-
if (bracketOpen != -1 && dash > bracketOpen + 1) {
|
|
403
|
-
String yearStr = rawShow.substring(bracketOpen + 1, dash);
|
|
404
|
-
return parseYear(yearStr, "start year");
|
|
405
|
-
}
|
|
406
|
-
return Either.left("Can't extract start year from: " + rawShow);
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
extractYearStart("The Wire (2002-2008)"); // Right(2002)
|
|
410
|
-
extractYearStart("The Wire (-2008)"); // Left("Can't extract start year from: The Wire (-2008)")
|
|
411
|
-
extractYearStart("The Wire (oops-2008)"); // Left("Can't parse start year: 'oops'")
|
|
412
|
-
```
|
|
413
|
-
|
|
414
|
-
### 7.4 Option から Either への変換
|
|
415
|
-
|
|
416
|
-
`toEither` メソッドで `Option` を `Either` に変換できます。
|
|
417
|
-
|
|
418
|
-
```java
|
|
419
|
-
Option<Integer> year = Option.some(996);
|
|
420
|
-
Option<Integer> noYear = Option.none();
|
|
421
|
-
|
|
422
|
-
year.toEither("no year given"); // Right(996)
|
|
423
|
-
noYear.toEither("no year given"); // Left("no year given")
|
|
424
|
-
```
|
|
425
|
-
|
|
426
|
-
### 7.5 Either の主要メソッド
|
|
427
|
-
|
|
428
|
-
| メソッド | 説明 | 例 |
|
|
429
|
-
|----------|------|-----|
|
|
430
|
-
| `map` | Right の値を変換 | `Right(5).map(x -> x * 2)` → `Right(10)` |
|
|
431
|
-
| `flatMap` | Right なら Either を返す関数を適用 | `Right(5).flatMap(x -> Right(x * 2))` → `Right(10)` |
|
|
432
|
-
| `orElse` | Left なら代替を使用 | `Left("err").orElse(Right(5))` → `Right(5)` |
|
|
433
|
-
| `toOption` | Option に変換 | `Right(5).toOption()` → `Some(5)` |
|
|
434
|
-
| `fold` | 両方のケースを処理 | `either.fold(err -> ..., val -> ...)` |
|
|
435
|
-
|
|
436
|
-
```java
|
|
437
|
-
Either<String, Integer> year = Either.right(996);
|
|
438
|
-
Either<String, Integer> noYear = Either.left("no year");
|
|
439
|
-
|
|
440
|
-
// map
|
|
441
|
-
year.map(y -> y * 2); // Right(1992)
|
|
442
|
-
noYear.map(y -> y * 2); // Left("no year")
|
|
443
|
-
|
|
444
|
-
// flatMap
|
|
445
|
-
year.flatMap(y -> Either.right(y * 2)); // Right(1992)
|
|
446
|
-
noYear.flatMap(y -> Either.right(y * 2)); // Left("no year")
|
|
447
|
-
|
|
448
|
-
// fold
|
|
449
|
-
year.fold(
|
|
450
|
-
error -> "Error: " + error,
|
|
451
|
-
value -> "Success: " + value
|
|
452
|
-
); // "Success: 996"
|
|
453
|
-
```
|
|
454
|
-
|
|
455
|
-
### 7.6 バリデーションの例
|
|
456
|
-
|
|
457
|
-
```java
|
|
458
|
-
public static Either<String, Integer> validateAge(int age) {
|
|
459
|
-
if (age < 0) {
|
|
460
|
-
return Either.left("Age cannot be negative");
|
|
461
|
-
} else if (age > 150) {
|
|
462
|
-
return Either.left("Age cannot be greater than 150");
|
|
463
|
-
}
|
|
464
|
-
return Either.right(age);
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
validateAge(25); // Right(25)
|
|
468
|
-
validateAge(-5); // Left("Age cannot be negative")
|
|
469
|
-
validateAge(200); // Left("Age cannot be greater than 150")
|
|
470
|
-
```
|
|
471
|
-
|
|
472
|
-
#### 複合バリデーション
|
|
473
|
-
|
|
474
|
-
```java
|
|
475
|
-
public record User(String username, String email, int age) {}
|
|
476
|
-
|
|
477
|
-
public static Either<String, User> validateUser(String username, String email, int age) {
|
|
478
|
-
return validateUsername(username).flatMap(validUsername ->
|
|
479
|
-
validateEmail(email).flatMap(validEmail ->
|
|
480
|
-
validateAge(age).map(validAge ->
|
|
481
|
-
new User(validUsername, validEmail, validAge)
|
|
482
|
-
)
|
|
483
|
-
)
|
|
484
|
-
);
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
validateUser("alice", "alice@example.com", 25); // Right(User(...))
|
|
488
|
-
validateUser("ab", "alice@example.com", 25); // Left("Username must be at least 3 characters")
|
|
489
|
-
```
|
|
490
|
-
|
|
491
|
-
### 7.7 代数的データ型(ADT)
|
|
492
|
-
|
|
493
|
-
**ソースファイル**: `app/java/src/main/java/ch07/MusicArtist.java`
|
|
494
|
-
|
|
495
|
-
Java 17+ の sealed interface と record を使って ADT を表現します。
|
|
496
|
-
|
|
497
|
-
#### 直和型(Sum Type)- sealed interface
|
|
498
|
-
|
|
499
|
-
```java
|
|
500
|
-
// 活動期間を表す直和型
|
|
501
|
-
public sealed interface YearsActive permits StillActive, ActiveBetween {}
|
|
502
|
-
|
|
503
|
-
public record StillActive(int since) implements YearsActive {}
|
|
504
|
-
public record ActiveBetween(int start, int end) implements YearsActive {}
|
|
505
|
-
```
|
|
506
|
-
|
|
507
|
-
#### 直積型(Product Type)- record
|
|
508
|
-
|
|
509
|
-
```java
|
|
510
|
-
public record Artist(
|
|
511
|
-
String name,
|
|
512
|
-
MusicGenre genre,
|
|
513
|
-
String origin,
|
|
514
|
-
YearsActive yearsActive
|
|
515
|
-
) {}
|
|
516
|
-
```
|
|
517
|
-
|
|
518
|
-
```plantuml
|
|
519
|
-
@startuml
|
|
520
|
-
!theme plain
|
|
521
|
-
|
|
522
|
-
rectangle "代数的データ型(ADT)" {
|
|
523
|
-
rectangle "直積型(Product Type)" as product {
|
|
524
|
-
card "record"
|
|
525
|
-
card "A AND B AND C"
|
|
526
|
-
card "フィールドの組み合わせ"
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
rectangle "直和型(Sum Type)" as sum {
|
|
530
|
-
card "sealed interface"
|
|
531
|
-
card "A OR B OR C"
|
|
532
|
-
card "選択肢のいずれか"
|
|
533
|
-
}
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
note bottom of product
|
|
537
|
-
Artist = name AND genre AND origin AND yearsActive
|
|
538
|
-
end note
|
|
539
|
-
|
|
540
|
-
note bottom of sum
|
|
541
|
-
YearsActive = StillActive OR ActiveBetween
|
|
542
|
-
end note
|
|
543
|
-
|
|
544
|
-
@enduml
|
|
545
|
-
```
|
|
546
|
-
|
|
547
|
-
### 7.8 パターンマッチング
|
|
548
|
-
|
|
549
|
-
Java 21 の拡張 switch を使ってパターンマッチングを行います。
|
|
550
|
-
|
|
551
|
-
```java
|
|
552
|
-
public static boolean wasArtistActive(Artist artist, int yearStart, int yearEnd) {
|
|
553
|
-
return switch (artist.yearsActive()) {
|
|
554
|
-
case StillActive(int since) -> since <= yearEnd;
|
|
555
|
-
case ActiveBetween(int start, int end) -> start <= yearEnd && end >= yearStart;
|
|
556
|
-
};
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
public static int activeLength(Artist artist, int currentYear) {
|
|
560
|
-
return switch (artist.yearsActive()) {
|
|
561
|
-
case StillActive(int since) -> currentYear - since;
|
|
562
|
-
case ActiveBetween(int start, int end) -> end - start;
|
|
563
|
-
};
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
// 使用例
|
|
567
|
-
Artist metallica = new Artist("Metallica", MusicGenre.HEAVY_METAL, "U.S.", new StillActive(1981));
|
|
568
|
-
Artist ledZeppelin = new Artist("Led Zeppelin", MusicGenre.HARD_ROCK, "England", new ActiveBetween(1968, 1980));
|
|
569
|
-
|
|
570
|
-
activeLength(metallica, 2024); // 43 (2024 - 1981)
|
|
571
|
-
activeLength(ledZeppelin, 2024); // 12 (1980 - 1968)
|
|
572
|
-
```
|
|
573
|
-
|
|
574
|
-
### 7.9 検索条件のモデリング
|
|
575
|
-
|
|
576
|
-
検索条件も ADT でモデリングできます。
|
|
577
|
-
|
|
578
|
-
```java
|
|
579
|
-
public sealed interface SearchCondition permits SearchByGenre, SearchByOrigin, SearchByActiveYears {}
|
|
580
|
-
|
|
581
|
-
public record SearchByGenre(List<MusicGenre> genres) implements SearchCondition {}
|
|
582
|
-
public record SearchByOrigin(List<String> locations) implements SearchCondition {}
|
|
583
|
-
public record SearchByActiveYears(int start, int end) implements SearchCondition {}
|
|
584
|
-
|
|
585
|
-
public static List<Artist> searchArtists(
|
|
586
|
-
List<Artist> artists,
|
|
587
|
-
List<SearchCondition> requiredConditions) {
|
|
588
|
-
return artists.filter(artist ->
|
|
589
|
-
requiredConditions.forAll(condition -> matchesCondition(artist, condition))
|
|
590
|
-
);
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
public static boolean matchesCondition(Artist artist, SearchCondition condition) {
|
|
594
|
-
return switch (condition) {
|
|
595
|
-
case SearchByGenre(List<MusicGenre> genres) ->
|
|
596
|
-
genres.contains(artist.genre());
|
|
597
|
-
case SearchByOrigin(List<String> locations) ->
|
|
598
|
-
locations.contains(artist.origin());
|
|
599
|
-
case SearchByActiveYears(int start, int end) ->
|
|
600
|
-
wasArtistActive(artist, start, end);
|
|
601
|
-
};
|
|
602
|
-
}
|
|
603
|
-
```
|
|
604
|
-
|
|
605
|
-
### 7.10 支払い方法の例
|
|
606
|
-
|
|
607
|
-
**ソースファイル**: `app/java/src/main/java/ch07/PaymentMethod.java`
|
|
608
|
-
|
|
609
|
-
```java
|
|
610
|
-
public sealed interface Payment permits CreditCard, BankTransfer, Cash {}
|
|
611
|
-
|
|
612
|
-
public record CreditCard(String number, String expiry) implements Payment {}
|
|
613
|
-
public record BankTransfer(String accountNumber) implements Payment {}
|
|
614
|
-
public record Cash() implements Payment {}
|
|
615
|
-
|
|
616
|
-
public static String describePayment(Payment method) {
|
|
617
|
-
return switch (method) {
|
|
618
|
-
case CreditCard(String number, String expiry) ->
|
|
619
|
-
"Credit card ending in " + number.substring(number.length() - 4);
|
|
620
|
-
case BankTransfer(String account) ->
|
|
621
|
-
"Bank transfer to account " + account;
|
|
622
|
-
case Cash() ->
|
|
623
|
-
"Cash payment";
|
|
624
|
-
};
|
|
625
|
-
}
|
|
626
|
-
```
|
|
627
|
-
|
|
628
|
-
---
|
|
629
|
-
|
|
630
|
-
## まとめ
|
|
631
|
-
|
|
632
|
-
### Part III で学んだこと
|
|
633
|
-
|
|
634
|
-
1. **Option**: 値の有無を型で表現する
|
|
635
|
-
2. **Either**: 成功/失敗とエラー情報を型で表現する
|
|
636
|
-
3. **flatMap**: Option/Either を組み合わせて使う
|
|
637
|
-
4. **orElse**: フォールバックを提供する
|
|
638
|
-
5. **ADT**: 直積型と直和型でドメインを正確にモデリング
|
|
639
|
-
6. **パターンマッチング**: 直和型を安全に処理する
|
|
640
|
-
|
|
641
|
-
### Option vs Either の使い分け
|
|
642
|
-
|
|
643
|
-
| 状況 | 使用する型 |
|
|
644
|
-
|------|------------|
|
|
645
|
-
| 値があるかないかだけが重要 | `Option<A>` |
|
|
646
|
-
| 失敗理由を伝える必要がある | `Either<E, A>` |
|
|
647
|
-
| 検索結果が見つからない | `Option<A>` |
|
|
648
|
-
| バリデーションエラーを伝える | `Either<String, A>` |
|
|
649
|
-
| 複数のエラー種別がある | `Either<ErrorType, A>` |
|
|
650
|
-
|
|
651
|
-
### Scala との対応
|
|
652
|
-
|
|
653
|
-
| Scala | Java + Vavr |
|
|
654
|
-
|-------|-------------|
|
|
655
|
-
| `Option[A]` | `io.vavr.control.Option<A>` |
|
|
656
|
-
| `Some(x)` | `Option.some(x)` |
|
|
657
|
-
| `None` | `Option.none()` |
|
|
658
|
-
| `Either[E, A]` | `io.vavr.control.Either<E, A>` |
|
|
659
|
-
| `Right(x)` | `Either.right(x)` |
|
|
660
|
-
| `Left(e)` | `Either.left(e)` |
|
|
661
|
-
| `toRight(e)` | `toEither(e)` |
|
|
662
|
-
| `case class` | `record` |
|
|
663
|
-
| `sealed trait` | `sealed interface` |
|
|
664
|
-
| `match { case ... }` | `switch (expr) { case ... -> }` |
|
|
665
|
-
|
|
666
|
-
### 次のパート
|
|
667
|
-
|
|
668
|
-
Part IV では、以下のトピックを学びます:
|
|
669
|
-
|
|
670
|
-
- IO モナドの導入
|
|
671
|
-
- 副作用の管理
|
|
672
|
-
- ストリーム処理
|
|
1
|
+
# Part III: エラーハンドリングと Option/Either
|
|
2
|
+
|
|
3
|
+
本章では、関数型プログラミングにおける安全なエラーハンドリングを学びます。`null` や例外に頼らず、Vavr の `Option` と `Either` を使って型安全にエラーを扱う方法を習得します。
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 第6章: Option 型による安全なエラーハンドリング
|
|
8
|
+
|
|
9
|
+
### 6.1 なぜ Option が必要か
|
|
10
|
+
|
|
11
|
+
従来のエラーハンドリングには問題があります。
|
|
12
|
+
|
|
13
|
+
```plantuml
|
|
14
|
+
@startuml
|
|
15
|
+
!theme plain
|
|
16
|
+
|
|
17
|
+
rectangle "従来のエラーハンドリング" {
|
|
18
|
+
rectangle "null を返す" as null_ret {
|
|
19
|
+
card "NullPointerException の危険"
|
|
20
|
+
card "コンパイル時に検出できない"
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
rectangle "例外をスローする" as exception {
|
|
24
|
+
card "制御フローが複雑化"
|
|
25
|
+
card "純粋関数ではなくなる"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
rectangle "Option による解決" as option {
|
|
30
|
+
card "型で「値がないかもしれない」を表現"
|
|
31
|
+
card "コンパイル時にチェック"
|
|
32
|
+
card "純粋関数のまま"
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
null_ret --> option : 置き換え
|
|
36
|
+
exception --> option : 置き換え
|
|
37
|
+
|
|
38
|
+
@enduml
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### 6.2 Option の基本
|
|
42
|
+
|
|
43
|
+
`Option<A>` は「`A` 型の値があるか、ないか」を表す型です。
|
|
44
|
+
|
|
45
|
+
```plantuml
|
|
46
|
+
@startuml
|
|
47
|
+
!theme plain
|
|
48
|
+
|
|
49
|
+
rectangle "Option<A>" as option {
|
|
50
|
+
rectangle "Some(value)" as some {
|
|
51
|
+
card "値が存在する"
|
|
52
|
+
card "value: A を保持"
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
rectangle "None" as none {
|
|
56
|
+
card "値が存在しない"
|
|
57
|
+
card "null の代わり"
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
@enduml
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**ソースファイル**: `app/java/src/main/java/ch06/OptionBasics.java`
|
|
65
|
+
|
|
66
|
+
```java
|
|
67
|
+
import io.vavr.control.Option;
|
|
68
|
+
|
|
69
|
+
// Some: 値が存在する
|
|
70
|
+
Option<Integer> some = Option.some(42);
|
|
71
|
+
|
|
72
|
+
// None: 値が存在しない
|
|
73
|
+
Option<Integer> none = Option.none();
|
|
74
|
+
|
|
75
|
+
// null から安全に作成
|
|
76
|
+
Option<String> fromNull = Option.of(maybeNull); // null なら None
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### 6.3 TV番組のパース例
|
|
80
|
+
|
|
81
|
+
**ソースファイル**: `app/java/src/main/java/ch06/TvShowParser.java`
|
|
82
|
+
|
|
83
|
+
TV番組の文字列をパースする例で Option の使い方を学びます。
|
|
84
|
+
|
|
85
|
+
```java
|
|
86
|
+
public record TvShow(String title, int start, int end) {}
|
|
87
|
+
|
|
88
|
+
// 入力例: "Breaking Bad (2008-2013)"
|
|
89
|
+
// 期待する出力: TvShow("Breaking Bad", 2008, 2013)
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
#### 例外を使う方法(問題あり)
|
|
93
|
+
|
|
94
|
+
```java
|
|
95
|
+
public static TvShow parseShowUnsafe(String rawShow) {
|
|
96
|
+
int bracketOpen = rawShow.indexOf('(');
|
|
97
|
+
int bracketClose = rawShow.indexOf(')');
|
|
98
|
+
int dash = rawShow.indexOf('-');
|
|
99
|
+
|
|
100
|
+
String name = rawShow.substring(0, bracketOpen).trim();
|
|
101
|
+
int yearStart = Integer.parseInt(rawShow.substring(bracketOpen + 1, dash));
|
|
102
|
+
int yearEnd = Integer.parseInt(rawShow.substring(dash + 1, bracketClose));
|
|
103
|
+
|
|
104
|
+
return new TvShow(name, yearStart, yearEnd);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// 正常ケース
|
|
108
|
+
parseShowUnsafe("Breaking Bad (2008-2013)"); // TvShow("Breaking Bad", 2008, 2013)
|
|
109
|
+
|
|
110
|
+
// 異常ケース → 例外がスローされる!
|
|
111
|
+
parseShowUnsafe("Chernobyl (2019)"); // StringIndexOutOfBoundsException
|
|
112
|
+
parseShowUnsafe("The Wire 2002-2008"); // StringIndexOutOfBoundsException
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
#### Option を使う方法
|
|
116
|
+
|
|
117
|
+
```java
|
|
118
|
+
public static Option<TvShow> parseShow(String rawShow) {
|
|
119
|
+
return extractName(rawShow).flatMap(name ->
|
|
120
|
+
extractYearStart(rawShow).orElse(() -> extractSingleYear(rawShow)).flatMap(yearStart ->
|
|
121
|
+
extractYearEnd(rawShow).orElse(() -> extractSingleYear(rawShow)).map(yearEnd ->
|
|
122
|
+
new TvShow(name, yearStart, yearEnd)
|
|
123
|
+
)
|
|
124
|
+
)
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// 正常ケース
|
|
129
|
+
parseShow("Breaking Bad (2008-2013)"); // Some(TvShow("Breaking Bad", 2008, 2013))
|
|
130
|
+
|
|
131
|
+
// 異常ケース → None が返される(例外なし)
|
|
132
|
+
parseShow("The Wire 2002-2008"); // None
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### 6.4 小さな関数から組み立てる
|
|
136
|
+
|
|
137
|
+
複雑なパース処理を小さな関数に分解します。
|
|
138
|
+
|
|
139
|
+
```java
|
|
140
|
+
// 名前を抽出
|
|
141
|
+
public static Option<String> extractName(String rawShow) {
|
|
142
|
+
int bracketOpen = rawShow.indexOf('(');
|
|
143
|
+
if (bracketOpen > 0) {
|
|
144
|
+
return Option.some(rawShow.substring(0, bracketOpen).trim());
|
|
145
|
+
}
|
|
146
|
+
return Option.none();
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// 開始年を抽出
|
|
150
|
+
public static Option<Integer> extractYearStart(String rawShow) {
|
|
151
|
+
int bracketOpen = rawShow.indexOf('(');
|
|
152
|
+
int dash = rawShow.indexOf('-');
|
|
153
|
+
|
|
154
|
+
if (bracketOpen != -1 && dash > bracketOpen + 1) {
|
|
155
|
+
String yearStr = rawShow.substring(bracketOpen + 1, dash);
|
|
156
|
+
return parseYear(yearStr);
|
|
157
|
+
}
|
|
158
|
+
return Option.none();
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// 文字列を年(整数)にパース
|
|
162
|
+
public static Option<Integer> parseYear(String yearStr) {
|
|
163
|
+
try {
|
|
164
|
+
return Option.some(Integer.parseInt(yearStr.trim()));
|
|
165
|
+
} catch (NumberFormatException e) {
|
|
166
|
+
return Option.none();
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### 6.5 orElse によるフォールバック
|
|
172
|
+
|
|
173
|
+
`orElse` を使って、最初の Option が `None` の場合に代替を試すことができます。
|
|
174
|
+
|
|
175
|
+
```java
|
|
176
|
+
Option<Integer> seven = Option.some(7);
|
|
177
|
+
Option<Integer> eight = Option.some(8);
|
|
178
|
+
Option<Integer> none = Option.none();
|
|
179
|
+
|
|
180
|
+
seven.orElse(eight); // Some(7) - 最初が Some なのでそのまま
|
|
181
|
+
none.orElse(eight); // Some(8) - 最初が None なので代替を使用
|
|
182
|
+
seven.orElse(none); // Some(7)
|
|
183
|
+
none.orElse(none); // None
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
#### 単年の番組に対応する
|
|
187
|
+
|
|
188
|
+
「Chernobyl (2019)」のような単年の番組をパースできるようにします。
|
|
189
|
+
|
|
190
|
+
```java
|
|
191
|
+
public static Option<Integer> extractSingleYear(String rawShow) {
|
|
192
|
+
int dash = rawShow.indexOf('-');
|
|
193
|
+
int bracketOpen = rawShow.indexOf('(');
|
|
194
|
+
int bracketClose = rawShow.indexOf(')');
|
|
195
|
+
|
|
196
|
+
if (dash == -1 && bracketOpen != -1 && bracketClose > bracketOpen + 1) {
|
|
197
|
+
String yearStr = rawShow.substring(bracketOpen + 1, bracketClose);
|
|
198
|
+
return parseYear(yearStr);
|
|
199
|
+
}
|
|
200
|
+
return Option.none();
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// これで単年の番組もパースできる
|
|
204
|
+
parseShow("Chernobyl (2019)"); // Some(TvShow("Chernobyl", 2019, 2019))
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### 6.6 Option の主要メソッド
|
|
208
|
+
|
|
209
|
+
| メソッド | 説明 | 例 |
|
|
210
|
+
|----------|------|-----|
|
|
211
|
+
| `map` | 値があれば変換 | `Some(5).map(x -> x * 2)` → `Some(10)` |
|
|
212
|
+
| `flatMap` | 値があれば Option を返す関数を適用 | `Some(5).flatMap(x -> Some(x * 2))` → `Some(10)` |
|
|
213
|
+
| `filter` | 条件を満たさなければ None | `Some(5).filter(x -> x > 10)` → `None` |
|
|
214
|
+
| `orElse` | None なら代替を使用 | `None.orElse(Some(5))` → `Some(5)` |
|
|
215
|
+
| `getOrElse` | None ならデフォルト値 | `None.getOrElse(0)` → `0` |
|
|
216
|
+
| `toList` | List に変換 | `Some(5).toList()` → `List(5)` |
|
|
217
|
+
| `toEither` | Either に変換 | `Some(5).toEither("error")` → `Right(5)` |
|
|
218
|
+
|
|
219
|
+
```java
|
|
220
|
+
Option<Integer> year = Option.some(996);
|
|
221
|
+
Option<Integer> noYear = Option.none();
|
|
222
|
+
|
|
223
|
+
// map
|
|
224
|
+
year.map(y -> y * 2); // Some(1992)
|
|
225
|
+
noYear.map(y -> y * 2); // None
|
|
226
|
+
|
|
227
|
+
// flatMap
|
|
228
|
+
year.flatMap(y -> Option.some(y * 2)); // Some(1992)
|
|
229
|
+
noYear.flatMap(y -> Option.some(y * 2)); // None
|
|
230
|
+
|
|
231
|
+
// filter
|
|
232
|
+
year.filter(y -> y < 2020); // Some(996)
|
|
233
|
+
year.filter(y -> y > 2020); // None
|
|
234
|
+
|
|
235
|
+
// getOrElse
|
|
236
|
+
year.getOrElse(2020); // 996
|
|
237
|
+
noYear.getOrElse(2020); // 2020
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### 6.7 エラーハンドリング戦略
|
|
241
|
+
|
|
242
|
+
複数の要素をパースする場合、2つの戦略があります。
|
|
243
|
+
|
|
244
|
+
```plantuml
|
|
245
|
+
@startuml
|
|
246
|
+
!theme plain
|
|
247
|
+
|
|
248
|
+
rectangle "エラーハンドリング戦略" {
|
|
249
|
+
rectangle "Best-effort 戦略" as best {
|
|
250
|
+
card "パースできたものだけ返す"
|
|
251
|
+
card "エラーは無視"
|
|
252
|
+
card "List<TvShow> を返す"
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
rectangle "All-or-nothing 戦略" as all {
|
|
256
|
+
card "全部成功するか、全部失敗"
|
|
257
|
+
card "一つでも失敗したら None"
|
|
258
|
+
card "Option<List<TvShow>> を返す"
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
@enduml
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
#### Best-effort 戦略
|
|
266
|
+
|
|
267
|
+
```java
|
|
268
|
+
public static List<TvShow> parseShowsBestEffort(List<String> rawShows) {
|
|
269
|
+
return rawShows
|
|
270
|
+
.map(TvShowParser::parseShow)
|
|
271
|
+
.flatMap(Option::toList);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
List<String> rawShows = List.of(
|
|
275
|
+
"Breaking Bad (2008-2013)",
|
|
276
|
+
"The Wire 2002 2008", // 無効な形式
|
|
277
|
+
"Mad Men (2007-2015)"
|
|
278
|
+
);
|
|
279
|
+
|
|
280
|
+
parseShowsBestEffort(rawShows);
|
|
281
|
+
// List(TvShow("Breaking Bad", 2008, 2013), TvShow("Mad Men", 2007, 2015))
|
|
282
|
+
// 無効なものは無視される
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
#### All-or-nothing 戦略
|
|
286
|
+
|
|
287
|
+
```java
|
|
288
|
+
public static Option<List<TvShow>> parseShowsAllOrNothing(List<String> rawShows) {
|
|
289
|
+
Option<List<TvShow>> initial = Option.some(List.empty());
|
|
290
|
+
|
|
291
|
+
return rawShows
|
|
292
|
+
.map(TvShowParser::parseShow)
|
|
293
|
+
.foldLeft(initial, (acc, optShow) ->
|
|
294
|
+
acc.flatMap(shows ->
|
|
295
|
+
optShow.map(shows::append)
|
|
296
|
+
)
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// 全部成功 → Some(List(...))
|
|
301
|
+
parseShowsAllOrNothing(List.of("Breaking Bad (2008-2013)", "Mad Men (2007-2015)"));
|
|
302
|
+
// Some(List(TvShow(...), TvShow(...)))
|
|
303
|
+
|
|
304
|
+
// 一つでも失敗 → None
|
|
305
|
+
parseShowsAllOrNothing(List.of("Breaking Bad (2008-2013)", "Invalid"));
|
|
306
|
+
// None
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### 6.8 forall と exists
|
|
310
|
+
|
|
311
|
+
Option で条件判定をする際に便利なメソッドです。
|
|
312
|
+
|
|
313
|
+
| メソッド | Option での動作 |
|
|
314
|
+
|----------|-----------------|
|
|
315
|
+
| `forAll(p)` | None → true, Some(x) → p(x) |
|
|
316
|
+
| `exists(p)` | None → false, Some(x) → p(x) |
|
|
317
|
+
|
|
318
|
+
```java
|
|
319
|
+
Option<Integer> year = Option.some(996);
|
|
320
|
+
Option<Integer> noYear = Option.none();
|
|
321
|
+
|
|
322
|
+
// forAll - 「全て」または「存在しない」
|
|
323
|
+
year.forAll(y -> y < 2020); // true (996 < 2020)
|
|
324
|
+
noYear.forAll(y -> y < 2020); // true (値がないので「全て」が自明に真)
|
|
325
|
+
year.forAll(y -> y > 2020); // false
|
|
326
|
+
|
|
327
|
+
// exists - 「存在して条件を満たす」
|
|
328
|
+
year.exists(y -> y < 2020); // true
|
|
329
|
+
noYear.exists(y -> y < 2020); // false (値がないので存在しない)
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
---
|
|
333
|
+
|
|
334
|
+
## 第7章: Either 型と複合的なエラー処理
|
|
335
|
+
|
|
336
|
+
### 7.1 Option の限界
|
|
337
|
+
|
|
338
|
+
`Option` は「値があるかないか」しか表現できません。**なぜ失敗したのか**を伝えられません。
|
|
339
|
+
|
|
340
|
+
```plantuml
|
|
341
|
+
@startuml
|
|
342
|
+
!theme plain
|
|
343
|
+
|
|
344
|
+
rectangle "Option vs Either" {
|
|
345
|
+
rectangle "Option<A>" as opt {
|
|
346
|
+
card "Some(value)" as some
|
|
347
|
+
card "None" as none
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
rectangle "Either<E, A>" as either {
|
|
351
|
+
card "Right(value)" as right
|
|
352
|
+
card "Left(error)" as left
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
opt --> either : 進化
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
note bottom of none
|
|
359
|
+
失敗理由が分からない
|
|
360
|
+
end note
|
|
361
|
+
|
|
362
|
+
note bottom of left
|
|
363
|
+
エラー情報を保持できる
|
|
364
|
+
end note
|
|
365
|
+
|
|
366
|
+
@enduml
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
### 7.2 Either の基本
|
|
370
|
+
|
|
371
|
+
**ソースファイル**: `app/java/src/main/java/ch07/EitherBasics.java`
|
|
372
|
+
|
|
373
|
+
`Either<E, A>` は「`E` 型のエラーか、`A` 型の成功値か」を表す型です。
|
|
374
|
+
|
|
375
|
+
- `Right(value)`: 成功(慣例的に「正しい」= right)
|
|
376
|
+
- `Left(error)`: 失敗(エラー情報を保持)
|
|
377
|
+
|
|
378
|
+
```java
|
|
379
|
+
import io.vavr.control.Either;
|
|
380
|
+
|
|
381
|
+
public static Either<String, String> extractName(String show) {
|
|
382
|
+
int bracketOpen = show.indexOf('(');
|
|
383
|
+
if (bracketOpen > 0) {
|
|
384
|
+
return Either.right(show.substring(0, bracketOpen).trim());
|
|
385
|
+
}
|
|
386
|
+
return Either.left("Can't extract name from: " + show);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
extractName("The Wire (2002-2008)"); // Right("The Wire")
|
|
390
|
+
extractName("(2022)"); // Left("Can't extract name from: (2022)")
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
### 7.3 Either を使ったパース
|
|
394
|
+
|
|
395
|
+
**ソースファイル**: `app/java/src/main/java/ch07/TvShowParserEither.java`
|
|
396
|
+
|
|
397
|
+
```java
|
|
398
|
+
public static Either<String, Integer> extractYearStart(String rawShow) {
|
|
399
|
+
int bracketOpen = rawShow.indexOf('(');
|
|
400
|
+
int dash = rawShow.indexOf('-');
|
|
401
|
+
|
|
402
|
+
if (bracketOpen != -1 && dash > bracketOpen + 1) {
|
|
403
|
+
String yearStr = rawShow.substring(bracketOpen + 1, dash);
|
|
404
|
+
return parseYear(yearStr, "start year");
|
|
405
|
+
}
|
|
406
|
+
return Either.left("Can't extract start year from: " + rawShow);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
extractYearStart("The Wire (2002-2008)"); // Right(2002)
|
|
410
|
+
extractYearStart("The Wire (-2008)"); // Left("Can't extract start year from: The Wire (-2008)")
|
|
411
|
+
extractYearStart("The Wire (oops-2008)"); // Left("Can't parse start year: 'oops'")
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
### 7.4 Option から Either への変換
|
|
415
|
+
|
|
416
|
+
`toEither` メソッドで `Option` を `Either` に変換できます。
|
|
417
|
+
|
|
418
|
+
```java
|
|
419
|
+
Option<Integer> year = Option.some(996);
|
|
420
|
+
Option<Integer> noYear = Option.none();
|
|
421
|
+
|
|
422
|
+
year.toEither("no year given"); // Right(996)
|
|
423
|
+
noYear.toEither("no year given"); // Left("no year given")
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
### 7.5 Either の主要メソッド
|
|
427
|
+
|
|
428
|
+
| メソッド | 説明 | 例 |
|
|
429
|
+
|----------|------|-----|
|
|
430
|
+
| `map` | Right の値を変換 | `Right(5).map(x -> x * 2)` → `Right(10)` |
|
|
431
|
+
| `flatMap` | Right なら Either を返す関数を適用 | `Right(5).flatMap(x -> Right(x * 2))` → `Right(10)` |
|
|
432
|
+
| `orElse` | Left なら代替を使用 | `Left("err").orElse(Right(5))` → `Right(5)` |
|
|
433
|
+
| `toOption` | Option に変換 | `Right(5).toOption()` → `Some(5)` |
|
|
434
|
+
| `fold` | 両方のケースを処理 | `either.fold(err -> ..., val -> ...)` |
|
|
435
|
+
|
|
436
|
+
```java
|
|
437
|
+
Either<String, Integer> year = Either.right(996);
|
|
438
|
+
Either<String, Integer> noYear = Either.left("no year");
|
|
439
|
+
|
|
440
|
+
// map
|
|
441
|
+
year.map(y -> y * 2); // Right(1992)
|
|
442
|
+
noYear.map(y -> y * 2); // Left("no year")
|
|
443
|
+
|
|
444
|
+
// flatMap
|
|
445
|
+
year.flatMap(y -> Either.right(y * 2)); // Right(1992)
|
|
446
|
+
noYear.flatMap(y -> Either.right(y * 2)); // Left("no year")
|
|
447
|
+
|
|
448
|
+
// fold
|
|
449
|
+
year.fold(
|
|
450
|
+
error -> "Error: " + error,
|
|
451
|
+
value -> "Success: " + value
|
|
452
|
+
); // "Success: 996"
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
### 7.6 バリデーションの例
|
|
456
|
+
|
|
457
|
+
```java
|
|
458
|
+
public static Either<String, Integer> validateAge(int age) {
|
|
459
|
+
if (age < 0) {
|
|
460
|
+
return Either.left("Age cannot be negative");
|
|
461
|
+
} else if (age > 150) {
|
|
462
|
+
return Either.left("Age cannot be greater than 150");
|
|
463
|
+
}
|
|
464
|
+
return Either.right(age);
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
validateAge(25); // Right(25)
|
|
468
|
+
validateAge(-5); // Left("Age cannot be negative")
|
|
469
|
+
validateAge(200); // Left("Age cannot be greater than 150")
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
#### 複合バリデーション
|
|
473
|
+
|
|
474
|
+
```java
|
|
475
|
+
public record User(String username, String email, int age) {}
|
|
476
|
+
|
|
477
|
+
public static Either<String, User> validateUser(String username, String email, int age) {
|
|
478
|
+
return validateUsername(username).flatMap(validUsername ->
|
|
479
|
+
validateEmail(email).flatMap(validEmail ->
|
|
480
|
+
validateAge(age).map(validAge ->
|
|
481
|
+
new User(validUsername, validEmail, validAge)
|
|
482
|
+
)
|
|
483
|
+
)
|
|
484
|
+
);
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
validateUser("alice", "alice@example.com", 25); // Right(User(...))
|
|
488
|
+
validateUser("ab", "alice@example.com", 25); // Left("Username must be at least 3 characters")
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
### 7.7 代数的データ型(ADT)
|
|
492
|
+
|
|
493
|
+
**ソースファイル**: `app/java/src/main/java/ch07/MusicArtist.java`
|
|
494
|
+
|
|
495
|
+
Java 17+ の sealed interface と record を使って ADT を表現します。
|
|
496
|
+
|
|
497
|
+
#### 直和型(Sum Type)- sealed interface
|
|
498
|
+
|
|
499
|
+
```java
|
|
500
|
+
// 活動期間を表す直和型
|
|
501
|
+
public sealed interface YearsActive permits StillActive, ActiveBetween {}
|
|
502
|
+
|
|
503
|
+
public record StillActive(int since) implements YearsActive {}
|
|
504
|
+
public record ActiveBetween(int start, int end) implements YearsActive {}
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
#### 直積型(Product Type)- record
|
|
508
|
+
|
|
509
|
+
```java
|
|
510
|
+
public record Artist(
|
|
511
|
+
String name,
|
|
512
|
+
MusicGenre genre,
|
|
513
|
+
String origin,
|
|
514
|
+
YearsActive yearsActive
|
|
515
|
+
) {}
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
```plantuml
|
|
519
|
+
@startuml
|
|
520
|
+
!theme plain
|
|
521
|
+
|
|
522
|
+
rectangle "代数的データ型(ADT)" {
|
|
523
|
+
rectangle "直積型(Product Type)" as product {
|
|
524
|
+
card "record"
|
|
525
|
+
card "A AND B AND C"
|
|
526
|
+
card "フィールドの組み合わせ"
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
rectangle "直和型(Sum Type)" as sum {
|
|
530
|
+
card "sealed interface"
|
|
531
|
+
card "A OR B OR C"
|
|
532
|
+
card "選択肢のいずれか"
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
note bottom of product
|
|
537
|
+
Artist = name AND genre AND origin AND yearsActive
|
|
538
|
+
end note
|
|
539
|
+
|
|
540
|
+
note bottom of sum
|
|
541
|
+
YearsActive = StillActive OR ActiveBetween
|
|
542
|
+
end note
|
|
543
|
+
|
|
544
|
+
@enduml
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
### 7.8 パターンマッチング
|
|
548
|
+
|
|
549
|
+
Java 21 の拡張 switch を使ってパターンマッチングを行います。
|
|
550
|
+
|
|
551
|
+
```java
|
|
552
|
+
public static boolean wasArtistActive(Artist artist, int yearStart, int yearEnd) {
|
|
553
|
+
return switch (artist.yearsActive()) {
|
|
554
|
+
case StillActive(int since) -> since <= yearEnd;
|
|
555
|
+
case ActiveBetween(int start, int end) -> start <= yearEnd && end >= yearStart;
|
|
556
|
+
};
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
public static int activeLength(Artist artist, int currentYear) {
|
|
560
|
+
return switch (artist.yearsActive()) {
|
|
561
|
+
case StillActive(int since) -> currentYear - since;
|
|
562
|
+
case ActiveBetween(int start, int end) -> end - start;
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
// 使用例
|
|
567
|
+
Artist metallica = new Artist("Metallica", MusicGenre.HEAVY_METAL, "U.S.", new StillActive(1981));
|
|
568
|
+
Artist ledZeppelin = new Artist("Led Zeppelin", MusicGenre.HARD_ROCK, "England", new ActiveBetween(1968, 1980));
|
|
569
|
+
|
|
570
|
+
activeLength(metallica, 2024); // 43 (2024 - 1981)
|
|
571
|
+
activeLength(ledZeppelin, 2024); // 12 (1980 - 1968)
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
### 7.9 検索条件のモデリング
|
|
575
|
+
|
|
576
|
+
検索条件も ADT でモデリングできます。
|
|
577
|
+
|
|
578
|
+
```java
|
|
579
|
+
public sealed interface SearchCondition permits SearchByGenre, SearchByOrigin, SearchByActiveYears {}
|
|
580
|
+
|
|
581
|
+
public record SearchByGenre(List<MusicGenre> genres) implements SearchCondition {}
|
|
582
|
+
public record SearchByOrigin(List<String> locations) implements SearchCondition {}
|
|
583
|
+
public record SearchByActiveYears(int start, int end) implements SearchCondition {}
|
|
584
|
+
|
|
585
|
+
public static List<Artist> searchArtists(
|
|
586
|
+
List<Artist> artists,
|
|
587
|
+
List<SearchCondition> requiredConditions) {
|
|
588
|
+
return artists.filter(artist ->
|
|
589
|
+
requiredConditions.forAll(condition -> matchesCondition(artist, condition))
|
|
590
|
+
);
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
public static boolean matchesCondition(Artist artist, SearchCondition condition) {
|
|
594
|
+
return switch (condition) {
|
|
595
|
+
case SearchByGenre(List<MusicGenre> genres) ->
|
|
596
|
+
genres.contains(artist.genre());
|
|
597
|
+
case SearchByOrigin(List<String> locations) ->
|
|
598
|
+
locations.contains(artist.origin());
|
|
599
|
+
case SearchByActiveYears(int start, int end) ->
|
|
600
|
+
wasArtistActive(artist, start, end);
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
```
|
|
604
|
+
|
|
605
|
+
### 7.10 支払い方法の例
|
|
606
|
+
|
|
607
|
+
**ソースファイル**: `app/java/src/main/java/ch07/PaymentMethod.java`
|
|
608
|
+
|
|
609
|
+
```java
|
|
610
|
+
public sealed interface Payment permits CreditCard, BankTransfer, Cash {}
|
|
611
|
+
|
|
612
|
+
public record CreditCard(String number, String expiry) implements Payment {}
|
|
613
|
+
public record BankTransfer(String accountNumber) implements Payment {}
|
|
614
|
+
public record Cash() implements Payment {}
|
|
615
|
+
|
|
616
|
+
public static String describePayment(Payment method) {
|
|
617
|
+
return switch (method) {
|
|
618
|
+
case CreditCard(String number, String expiry) ->
|
|
619
|
+
"Credit card ending in " + number.substring(number.length() - 4);
|
|
620
|
+
case BankTransfer(String account) ->
|
|
621
|
+
"Bank transfer to account " + account;
|
|
622
|
+
case Cash() ->
|
|
623
|
+
"Cash payment";
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
```
|
|
627
|
+
|
|
628
|
+
---
|
|
629
|
+
|
|
630
|
+
## まとめ
|
|
631
|
+
|
|
632
|
+
### Part III で学んだこと
|
|
633
|
+
|
|
634
|
+
1. **Option**: 値の有無を型で表現する
|
|
635
|
+
2. **Either**: 成功/失敗とエラー情報を型で表現する
|
|
636
|
+
3. **flatMap**: Option/Either を組み合わせて使う
|
|
637
|
+
4. **orElse**: フォールバックを提供する
|
|
638
|
+
5. **ADT**: 直積型と直和型でドメインを正確にモデリング
|
|
639
|
+
6. **パターンマッチング**: 直和型を安全に処理する
|
|
640
|
+
|
|
641
|
+
### Option vs Either の使い分け
|
|
642
|
+
|
|
643
|
+
| 状況 | 使用する型 |
|
|
644
|
+
|------|------------|
|
|
645
|
+
| 値があるかないかだけが重要 | `Option<A>` |
|
|
646
|
+
| 失敗理由を伝える必要がある | `Either<E, A>` |
|
|
647
|
+
| 検索結果が見つからない | `Option<A>` |
|
|
648
|
+
| バリデーションエラーを伝える | `Either<String, A>` |
|
|
649
|
+
| 複数のエラー種別がある | `Either<ErrorType, A>` |
|
|
650
|
+
|
|
651
|
+
### Scala との対応
|
|
652
|
+
|
|
653
|
+
| Scala | Java + Vavr |
|
|
654
|
+
|-------|-------------|
|
|
655
|
+
| `Option[A]` | `io.vavr.control.Option<A>` |
|
|
656
|
+
| `Some(x)` | `Option.some(x)` |
|
|
657
|
+
| `None` | `Option.none()` |
|
|
658
|
+
| `Either[E, A]` | `io.vavr.control.Either<E, A>` |
|
|
659
|
+
| `Right(x)` | `Either.right(x)` |
|
|
660
|
+
| `Left(e)` | `Either.left(e)` |
|
|
661
|
+
| `toRight(e)` | `toEither(e)` |
|
|
662
|
+
| `case class` | `record` |
|
|
663
|
+
| `sealed trait` | `sealed interface` |
|
|
664
|
+
| `match { case ... }` | `switch (expr) { case ... -> }` |
|
|
665
|
+
|
|
666
|
+
### 次のパート
|
|
667
|
+
|
|
668
|
+
Part IV では、以下のトピックを学びます:
|
|
669
|
+
|
|
670
|
+
- IO モナドの導入
|
|
671
|
+
- 副作用の管理
|
|
672
|
+
- ストリーム処理
|