@k2works/claude-code-booster 3.0.0 → 3.1.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/lib/assets/docs/article/functional-desgin-ppp/all/01-immutability-and-data-transformation.md +475 -0
- package/lib/assets/docs/article/functional-desgin-ppp/all/02-function-composition.md +519 -0
- package/lib/assets/docs/article/functional-desgin-ppp/all/03-polymorphism.md +537 -0
- package/lib/assets/docs/article/functional-desgin-ppp/all/04-data-validation.md +300 -0
- package/lib/assets/docs/article/functional-desgin-ppp/all/05-property-based-testing.md +320 -0
- package/lib/assets/docs/article/functional-desgin-ppp/all/06-tdd-and-functional.md +498 -0
- package/lib/assets/docs/article/functional-desgin-ppp/all/07-composite-pattern.md +298 -0
- package/lib/assets/docs/article/functional-desgin-ppp/all/08-decorator-pattern.md +291 -0
- package/lib/assets/docs/article/functional-desgin-ppp/all/09-adapter-pattern.md +336 -0
- package/lib/assets/docs/article/functional-desgin-ppp/all/10-strategy-pattern.md +303 -0
- package/lib/assets/docs/article/functional-desgin-ppp/all/11-command-pattern.md +286 -0
- package/lib/assets/docs/article/functional-desgin-ppp/all/12-visitor-pattern.md +322 -0
- package/lib/assets/docs/article/functional-desgin-ppp/all/13-abstract-factory-pattern.md +319 -0
- package/lib/assets/docs/article/functional-desgin-ppp/all/14-abstract-server-pattern.md +365 -0
- package/lib/assets/docs/article/functional-desgin-ppp/all/15-gossiping-bus-drivers.md +156 -0
- package/lib/assets/docs/article/functional-desgin-ppp/all/16-payroll-system.md +178 -0
- package/lib/assets/docs/article/functional-desgin-ppp/all/17-video-rental-system.md +312 -0
- package/lib/assets/docs/article/functional-desgin-ppp/all/18-concurrency-system.md +287 -0
- package/lib/assets/docs/article/functional-desgin-ppp/all/19-wa-tor-simulation.md +286 -0
- package/lib/assets/docs/article/functional-desgin-ppp/all/20-pattern-interactions.md +274 -0
- package/lib/assets/docs/article/functional-desgin-ppp/all/21-best-practices.md +294 -0
- package/lib/assets/docs/article/functional-desgin-ppp/all/22-oo-to-fp-migration.md +337 -0
- package/lib/assets/docs/article/functional-desgin-ppp/all/index.md +388 -0
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/01-immutability-and-data-transformation.md +271 -0
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/02-function-composition.md +380 -0
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/03-polymorphism.md +384 -0
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/04-clojure-spec.md +350 -0
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/05-property-based-testing.md +352 -0
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/06-tdd-in-functional.md +383 -0
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/07-composite-pattern.md +529 -0
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/08-decorator-pattern.md +395 -0
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/09-adapter-pattern.md +399 -0
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/10-strategy-pattern.md +485 -0
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/11-command-pattern.md +566 -0
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/12-visitor-pattern.md +567 -0
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/13-abstract-factory-pattern.md +475 -0
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/14-abstract-server-pattern.md +462 -0
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/15-gossiping-bus-drivers.md +323 -0
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/16-payroll-system.md +401 -0
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/17-video-rental-system.md +450 -0
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/18-concurrency-system.md +475 -0
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/19-wator-simulation.md +739 -0
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/20-pattern-interactions.md +562 -0
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/21-best-practices.md +506 -0
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/22-oo-to-fp-migration.md +526 -0
- package/lib/assets/docs/article/functional-desgin-ppp/clojure/index.md +197 -0
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/01-immutability-and-data-transformation.md +381 -0
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/02-function-composition.md +374 -0
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/03-polymorphism.md +375 -0
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/04-data-validation.md +195 -0
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/05-property-based-testing.md +268 -0
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/06-tdd-and-fp.md +294 -0
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/07-effects-and-pure-functions.md +164 -0
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/08-error-handling-strategies.md +168 -0
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/09-io-and-external-systems.md +254 -0
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/10-concurrency-patterns.md +269 -0
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/11-command-pattern.md +148 -0
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/12-visitor-pattern.md +176 -0
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/13-abstract-factory-pattern.md +604 -0
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/14-abstract-server-pattern.md +729 -0
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/15-gossiping-bus-drivers.md +291 -0
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/16-payroll-system.md +420 -0
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/17-video-rental-system.md +319 -0
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/18-concurrency-system.md +466 -0
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/19-wator-simulation.md +523 -0
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/20-pattern-interactions.md +287 -0
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/21-best-practices.md +340 -0
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/22-oo-to-fp-migration.md +395 -0
- package/lib/assets/docs/article/functional-desgin-ppp/elixir/index.md +204 -0
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/01-immutability-and-data-transformation.md +382 -0
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/02-function-composition.md +452 -0
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/03-polymorphism.md +495 -0
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/04-data-validation.md +416 -0
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/05-property-based-testing.md +382 -0
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/06-tdd-functional.md +687 -0
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/07-composite-pattern.md +442 -0
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/08-decorator-pattern.md +479 -0
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/09-adapter-pattern.md +479 -0
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/10-strategy-pattern.md +427 -0
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/11-command-pattern.md +428 -0
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/12-visitor-pattern.md +339 -0
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/13-abstract-factory-pattern.md +309 -0
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/14-abstract-server-pattern.md +596 -0
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/15-gossiping-bus-drivers.md +353 -0
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/16-payroll-system.md +350 -0
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/17-video-rental-system.md +412 -0
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/18-concurrency-system.md +367 -0
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/19-wator-simulation.md +401 -0
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/20-pattern-interactions.md +291 -0
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/21-best-practices.md +320 -0
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/22-oo-to-fp-migration.md +322 -0
- package/lib/assets/docs/article/functional-desgin-ppp/fsharp/index.md +230 -0
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/01-immutability-and-data-transformation.md +298 -0
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/02-function-composition.md +304 -0
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/03-polymorphism.md +362 -0
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/04-data-validation.md +257 -0
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/05-property-based-testing.md +254 -0
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/06-tdd-functional.md +283 -0
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/07-composite-pattern.md +395 -0
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/08-decorator-pattern.md +319 -0
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/09-adapter-pattern.md +382 -0
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/10-strategy-pattern.md +287 -0
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/11-command-pattern.md +303 -0
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/12-visitor-pattern.md +326 -0
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/13-abstract-factory-pattern.md +332 -0
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/14-abstract-server-pattern.md +379 -0
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/15-gossiping-bus-drivers.md +175 -0
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/16-payroll-system.md +219 -0
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/17-video-rental-system.md +244 -0
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/18-concurrency-system.md +363 -0
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/19-wator-simulation.md +438 -0
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/20-pattern-interactions.md +323 -0
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/21-best-practices.md +403 -0
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/22-oo-to-fp-migration.md +469 -0
- package/lib/assets/docs/article/functional-desgin-ppp/haskell/index.md +174 -0
- package/lib/assets/docs/article/functional-desgin-ppp/index.md +90 -0
- package/lib/assets/docs/article/functional-desgin-ppp/rust/01-immutability-and-data-transformation.md +448 -0
- package/lib/assets/docs/article/functional-desgin-ppp/rust/02-function-composition.md +463 -0
- package/lib/assets/docs/article/functional-desgin-ppp/rust/03-polymorphism.md +425 -0
- package/lib/assets/docs/article/functional-desgin-ppp/rust/04-data-validation.md +273 -0
- package/lib/assets/docs/article/functional-desgin-ppp/rust/05-property-based-testing.md +247 -0
- package/lib/assets/docs/article/functional-desgin-ppp/rust/06-tdd-and-functional.md +841 -0
- package/lib/assets/docs/article/functional-desgin-ppp/rust/07-composite-pattern.md +384 -0
- package/lib/assets/docs/article/functional-desgin-ppp/rust/08-decorator-pattern.md +383 -0
- package/lib/assets/docs/article/functional-desgin-ppp/rust/09-adapter-pattern.md +339 -0
- package/lib/assets/docs/article/functional-desgin-ppp/rust/10-strategy-pattern.md +331 -0
- package/lib/assets/docs/article/functional-desgin-ppp/rust/11-command-pattern.md +356 -0
- package/lib/assets/docs/article/functional-desgin-ppp/rust/12-visitor-pattern.md +379 -0
- package/lib/assets/docs/article/functional-desgin-ppp/rust/13-abstract-factory-pattern.md +361 -0
- package/lib/assets/docs/article/functional-desgin-ppp/rust/14-abstract-server-pattern.md +392 -0
- package/lib/assets/docs/article/functional-desgin-ppp/rust/15-gossiping-bus-drivers.md +300 -0
- package/lib/assets/docs/article/functional-desgin-ppp/rust/16-payroll-system.md +297 -0
- package/lib/assets/docs/article/functional-desgin-ppp/rust/17-video-rental-system.md +304 -0
- package/lib/assets/docs/article/functional-desgin-ppp/rust/18-concurrency-system.md +315 -0
- package/lib/assets/docs/article/functional-desgin-ppp/rust/19-wator-simulation.md +311 -0
- package/lib/assets/docs/article/functional-desgin-ppp/rust/20-pattern-interactions.md +304 -0
- package/lib/assets/docs/article/functional-desgin-ppp/rust/21-best-practices.md +336 -0
- package/lib/assets/docs/article/functional-desgin-ppp/rust/22-oo-to-fp-migration.md +349 -0
- package/lib/assets/docs/article/functional-desgin-ppp/rust/index.md +199 -0
- package/lib/assets/docs/article/functional-desgin-ppp/scala/01-immutability-and-data-transformation.md +326 -0
- package/lib/assets/docs/article/functional-desgin-ppp/scala/02-function-composition.md +348 -0
- package/lib/assets/docs/article/functional-desgin-ppp/scala/03-polymorphism.md +357 -0
- package/lib/assets/docs/article/functional-desgin-ppp/scala/04-data-validation.md +364 -0
- package/lib/assets/docs/article/functional-desgin-ppp/scala/05-property-based-testing.md +515 -0
- package/lib/assets/docs/article/functional-desgin-ppp/scala/06-tdd-functional.md +557 -0
- package/lib/assets/docs/article/functional-desgin-ppp/scala/07-composite-pattern.md +363 -0
- package/lib/assets/docs/article/functional-desgin-ppp/scala/08-decorator-pattern.md +327 -0
- package/lib/assets/docs/article/functional-desgin-ppp/scala/09-adapter-pattern.md +517 -0
- package/lib/assets/docs/article/functional-desgin-ppp/scala/10-strategy-pattern.md +441 -0
- package/lib/assets/docs/article/functional-desgin-ppp/scala/11-command-pattern.md +407 -0
- package/lib/assets/docs/article/functional-desgin-ppp/scala/12-visitor-pattern.md +379 -0
- package/lib/assets/docs/article/functional-desgin-ppp/scala/13-abstract-factory-pattern.md +398 -0
- package/lib/assets/docs/article/functional-desgin-ppp/scala/14-abstract-server-pattern.md +476 -0
- package/lib/assets/docs/article/functional-desgin-ppp/scala/15-gossiping-bus-drivers.md +389 -0
- package/lib/assets/docs/article/functional-desgin-ppp/scala/16-payroll-system.md +342 -0
- package/lib/assets/docs/article/functional-desgin-ppp/scala/17-video-rental-system.md +324 -0
- package/lib/assets/docs/article/functional-desgin-ppp/scala/18-concurrency-system.md +730 -0
- package/lib/assets/docs/article/functional-desgin-ppp/scala/19-wator-simulation.md +624 -0
- package/lib/assets/docs/article/functional-desgin-ppp/scala/20-pattern-interactions.md +512 -0
- package/lib/assets/docs/article/functional-desgin-ppp/scala/21-best-practices.md +427 -0
- package/lib/assets/docs/article/functional-desgin-ppp/scala/22-oo-to-fp-migration.md +682 -0
- package/lib/assets/docs/article/functional-desgin-ppp/scala/index.md +199 -0
- package/lib/assets/docs/article/getting-start-tdd/clojure/01-todo-list-and-first-test.md +166 -0
- package/lib/assets/docs/article/getting-start-tdd/clojure/02-fake-it-and-triangulation.md +162 -0
- package/lib/assets/docs/article/getting-start-tdd/clojure/03-obvious-implementation-and-refactoring.md +135 -0
- package/lib/assets/docs/article/getting-start-tdd/clojure/04-version-control-and-conventional-commits.md +88 -0
- package/lib/assets/docs/article/getting-start-tdd/clojure/05-package-management-and-static-analysis.md +299 -0
- package/lib/assets/docs/article/getting-start-tdd/clojure/06-task-runner-and-ci-cd.md +241 -0
- package/lib/assets/docs/article/getting-start-tdd/clojure/07-protocols-and-records.md +131 -0
- package/lib/assets/docs/article/getting-start-tdd/clojure/08-multimethods-and-design-patterns.md +130 -0
- package/lib/assets/docs/article/getting-start-tdd/clojure/09-namespaces-and-module-design.md +127 -0
- package/lib/assets/docs/article/getting-start-tdd/clojure/10-higher-order-functions-and-composition.md +114 -0
- package/lib/assets/docs/article/getting-start-tdd/clojure/11-persistent-data-and-pipeline.md +138 -0
- package/lib/assets/docs/article/getting-start-tdd/clojure/12-error-handling-and-spec.md +161 -0
- package/lib/assets/docs/article/getting-start-tdd/clojure/index.md +65 -0
- package/lib/assets/docs/article/getting-start-tdd/csharp/chapter01.md +232 -0
- package/lib/assets/docs/article/getting-start-tdd/csharp/chapter02.md +244 -0
- package/lib/assets/docs/article/getting-start-tdd/csharp/chapter03.md +202 -0
- package/lib/assets/docs/article/getting-start-tdd/csharp/chapter04.md +92 -0
- package/lib/assets/docs/article/getting-start-tdd/csharp/chapter05.md +256 -0
- package/lib/assets/docs/article/getting-start-tdd/csharp/chapter06.md +195 -0
- package/lib/assets/docs/article/getting-start-tdd/csharp/chapter07.md +214 -0
- package/lib/assets/docs/article/getting-start-tdd/csharp/chapter08.md +249 -0
- package/lib/assets/docs/article/getting-start-tdd/csharp/chapter09.md +174 -0
- package/lib/assets/docs/article/getting-start-tdd/csharp/chapter10.md +166 -0
- package/lib/assets/docs/article/getting-start-tdd/csharp/chapter11.md +192 -0
- package/lib/assets/docs/article/getting-start-tdd/csharp/chapter12.md +211 -0
- package/lib/assets/docs/article/getting-start-tdd/csharp/index.md +83 -0
- package/lib/assets/docs/article/getting-start-tdd/elixir/01-todo-list-and-first-test.md +87 -0
- package/lib/assets/docs/article/getting-start-tdd/elixir/02-fake-it-and-triangulation.md +95 -0
- package/lib/assets/docs/article/getting-start-tdd/elixir/03-obvious-implementation-and-refactoring.md +109 -0
- package/lib/assets/docs/article/getting-start-tdd/elixir/04-version-control-and-conventional-commits.md +96 -0
- package/lib/assets/docs/article/getting-start-tdd/elixir/05-package-management-and-static-analysis.md +88 -0
- package/lib/assets/docs/article/getting-start-tdd/elixir/06-task-runner-and-ci-cd.md +71 -0
- package/lib/assets/docs/article/getting-start-tdd/elixir/07-structs-and-protocols.md +110 -0
- package/lib/assets/docs/article/getting-start-tdd/elixir/08-pattern-matching-and-guards.md +108 -0
- package/lib/assets/docs/article/getting-start-tdd/elixir/09-module-design-and-behaviours.md +104 -0
- package/lib/assets/docs/article/getting-start-tdd/elixir/10-higher-order-functions-and-pipeline.md +178 -0
- package/lib/assets/docs/article/getting-start-tdd/elixir/11-stream-and-lazy-evaluation.md +142 -0
- package/lib/assets/docs/article/getting-start-tdd/elixir/12-error-handling-and-with.md +145 -0
- package/lib/assets/docs/article/getting-start-tdd/elixir/index.md +35 -0
- package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter01.md +202 -0
- package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter02.md +246 -0
- package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter03.md +218 -0
- package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter04.md +179 -0
- package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter05.md +267 -0
- package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter06.md +190 -0
- package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter07.md +161 -0
- package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter08.md +175 -0
- package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter09.md +222 -0
- package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter10.md +189 -0
- package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter11.md +212 -0
- package/lib/assets/docs/article/getting-start-tdd/fsharp/chapter12.md +215 -0
- package/lib/assets/docs/article/getting-start-tdd/fsharp/index.md +71 -0
- package/lib/assets/docs/article/getting-start-tdd/go/01-todo-list-and-first-test.md +213 -0
- package/lib/assets/docs/article/getting-start-tdd/go/02-fake-it-and-triangulation.md +302 -0
- package/lib/assets/docs/article/getting-start-tdd/go/03-obvious-implementation-and-refactoring.md +339 -0
- package/lib/assets/docs/article/getting-start-tdd/go/04-version-control-and-conventional-commits.md +112 -0
- package/lib/assets/docs/article/getting-start-tdd/go/05-package-management-and-static-analysis.md +272 -0
- package/lib/assets/docs/article/getting-start-tdd/go/06-task-runner-and-ci-cd.md +233 -0
- package/lib/assets/docs/article/getting-start-tdd/go/07-encapsulation-and-polymorphism.md +394 -0
- package/lib/assets/docs/article/getting-start-tdd/go/08-design-patterns.md +422 -0
- package/lib/assets/docs/article/getting-start-tdd/go/09-solid-principles-and-module-design.md +400 -0
- package/lib/assets/docs/article/getting-start-tdd/go/10-higher-order-functions-and-composition.md +226 -0
- package/lib/assets/docs/article/getting-start-tdd/go/11-immutable-data-and-pipeline.md +296 -0
- package/lib/assets/docs/article/getting-start-tdd/go/12-error-handling-and-type-safety.md +411 -0
- package/lib/assets/docs/article/getting-start-tdd/go/index.md +83 -0
- package/lib/assets/docs/article/getting-start-tdd/haskell/01-todo-list-and-first-test.md +279 -0
- package/lib/assets/docs/article/getting-start-tdd/haskell/02-fake-it-and-triangulation.md +337 -0
- package/lib/assets/docs/article/getting-start-tdd/haskell/03-obvious-implementation-and-refactoring.md +257 -0
- package/lib/assets/docs/article/getting-start-tdd/haskell/04-version-control-and-conventional-commits.md +182 -0
- package/lib/assets/docs/article/getting-start-tdd/haskell/05-package-management-and-static-analysis.md +313 -0
- package/lib/assets/docs/article/getting-start-tdd/haskell/06-task-runner-and-ci-cd.md +309 -0
- package/lib/assets/docs/article/getting-start-tdd/haskell/07-algebraic-data-types-and-type-classes.md +412 -0
- package/lib/assets/docs/article/getting-start-tdd/haskell/08-pattern-matching-and-guards.md +390 -0
- package/lib/assets/docs/article/getting-start-tdd/haskell/09-module-design-and-smart-constructors.md +461 -0
- package/lib/assets/docs/article/getting-start-tdd/haskell/10-higher-order-functions-and-currying.md +434 -0
- package/lib/assets/docs/article/getting-start-tdd/haskell/11-function-composition-and-point-free.md +392 -0
- package/lib/assets/docs/article/getting-start-tdd/haskell/12-monad-and-error-handling.md +631 -0
- package/lib/assets/docs/article/getting-start-tdd/haskell/index.md +49 -0
- package/lib/assets/docs/article/getting-start-tdd/index.md +93 -0
- package/lib/assets/docs/article/getting-start-tdd/integration/01-language-overview.md +375 -0
- package/lib/assets/docs/article/getting-start-tdd/integration/02-test-framework-comparison.md +349 -0
- package/lib/assets/docs/article/getting-start-tdd/integration/03-tdd-pattern-comparison.md +445 -0
- package/lib/assets/docs/article/getting-start-tdd/integration/04-type-system-comparison.md +405 -0
- package/lib/assets/docs/article/getting-start-tdd/integration/05-dev-environment-comparison.md +330 -0
- package/lib/assets/docs/article/getting-start-tdd/integration/06-learning-roadmap.md +274 -0
- package/lib/assets/docs/article/getting-start-tdd/integration/index.md +69 -0
- package/lib/assets/docs/article/getting-start-tdd/java/01-todo-list-and-first-test.md +234 -0
- package/lib/assets/docs/article/getting-start-tdd/java/02-fake-it-and-triangulation.md +261 -0
- package/lib/assets/docs/article/getting-start-tdd/java/03-obvious-implementation-and-refactoring.md +185 -0
- package/lib/assets/docs/article/getting-start-tdd/java/04-version-control-and-conventional-commits.md +115 -0
- package/lib/assets/docs/article/getting-start-tdd/java/05-package-management-and-static-analysis.md +382 -0
- package/lib/assets/docs/article/getting-start-tdd/java/06-task-runner-and-ci-cd.md +272 -0
- package/lib/assets/docs/article/getting-start-tdd/java/07-encapsulation-and-polymorphism.md +626 -0
- package/lib/assets/docs/article/getting-start-tdd/java/08-design-patterns.md +393 -0
- package/lib/assets/docs/article/getting-start-tdd/java/09-solid-principles-and-module-design.md +310 -0
- package/lib/assets/docs/article/getting-start-tdd/java/10-higher-order-functions-and-composition.md +188 -0
- package/lib/assets/docs/article/getting-start-tdd/java/11-immutable-data-and-pipeline.md +167 -0
- package/lib/assets/docs/article/getting-start-tdd/java/12-error-handling-and-type-safety.md +205 -0
- package/lib/assets/docs/article/getting-start-tdd/java/index.md +61 -0
- package/lib/assets/docs/article/getting-start-tdd/node/01-todo-list-and-first-test.md +244 -0
- package/lib/assets/docs/article/getting-start-tdd/node/02-fake-it-and-triangulation.md +262 -0
- package/lib/assets/docs/article/getting-start-tdd/node/03-obvious-implementation-and-refactoring.md +169 -0
- package/lib/assets/docs/article/getting-start-tdd/node/04-version-control-and-conventional-commits.md +112 -0
- package/lib/assets/docs/article/getting-start-tdd/node/05-package-management-and-static-analysis.md +314 -0
- package/lib/assets/docs/article/getting-start-tdd/node/06-task-runner-and-ci-cd.md +235 -0
- package/lib/assets/docs/article/getting-start-tdd/node/07-encapsulation-and-polymorphism.md +327 -0
- package/lib/assets/docs/article/getting-start-tdd/node/08-design-patterns.md +322 -0
- package/lib/assets/docs/article/getting-start-tdd/node/09-solid-principles-and-module-design.md +285 -0
- package/lib/assets/docs/article/getting-start-tdd/node/10-higher-order-functions-and-composition.md +199 -0
- package/lib/assets/docs/article/getting-start-tdd/node/11-immutable-data-and-pipeline.md +207 -0
- package/lib/assets/docs/article/getting-start-tdd/node/12-error-handling-and-type-safety.md +295 -0
- package/lib/assets/docs/article/getting-start-tdd/node/index.md +56 -0
- package/lib/assets/docs/article/getting-start-tdd/php/01-todo-list-and-first-test.md +259 -0
- package/lib/assets/docs/article/getting-start-tdd/php/02-fake-it-and-triangulation.md +200 -0
- package/lib/assets/docs/article/getting-start-tdd/php/03-obvious-implementation-and-refactoring.md +248 -0
- package/lib/assets/docs/article/getting-start-tdd/php/04-version-control-and-conventional-commits.md +141 -0
- package/lib/assets/docs/article/getting-start-tdd/php/05-package-management-and-static-analysis.md +410 -0
- package/lib/assets/docs/article/getting-start-tdd/php/06-task-runner-and-ci-cd.md +321 -0
- package/lib/assets/docs/article/getting-start-tdd/php/07-encapsulation-and-polymorphism.md +372 -0
- package/lib/assets/docs/article/getting-start-tdd/php/08-design-patterns.md +453 -0
- package/lib/assets/docs/article/getting-start-tdd/php/09-solid-principles-and-module-design.md +460 -0
- package/lib/assets/docs/article/getting-start-tdd/php/10-higher-order-functions-and-composition.md +182 -0
- package/lib/assets/docs/article/getting-start-tdd/php/11-immutable-data-and-pipeline.md +266 -0
- package/lib/assets/docs/article/getting-start-tdd/php/12-error-handling-and-type-safety.md +308 -0
- package/lib/assets/docs/article/getting-start-tdd/php/index.md +84 -0
- package/lib/assets/docs/article/getting-start-tdd/python/01-todo-list-and-first-test.md +201 -0
- package/lib/assets/docs/article/getting-start-tdd/python/02-fake-it-and-triangulation.md +247 -0
- package/lib/assets/docs/article/getting-start-tdd/python/03-obvious-implementation-and-refactoring.md +199 -0
- package/lib/assets/docs/article/getting-start-tdd/python/04-version-control-and-conventional-commits.md +87 -0
- package/lib/assets/docs/article/getting-start-tdd/python/05-package-management-and-static-analysis.md +274 -0
- package/lib/assets/docs/article/getting-start-tdd/python/06-task-runner-and-ci-cd.md +190 -0
- package/lib/assets/docs/article/getting-start-tdd/python/07-encapsulation-and-polymorphism.md +208 -0
- package/lib/assets/docs/article/getting-start-tdd/python/08-design-patterns.md +172 -0
- package/lib/assets/docs/article/getting-start-tdd/python/09-solid-principles-and-module-design.md +130 -0
- package/lib/assets/docs/article/getting-start-tdd/python/10-higher-order-functions-and-composition.md +122 -0
- package/lib/assets/docs/article/getting-start-tdd/python/11-immutable-data-and-pipeline.md +116 -0
- package/lib/assets/docs/article/getting-start-tdd/python/12-error-handling-and-type-safety.md +126 -0
- package/lib/assets/docs/article/getting-start-tdd/python/index.md +55 -0
- package/lib/assets/docs/article/getting-start-tdd/ruby/01-todo-list-and-first-test.md +231 -0
- package/lib/assets/docs/article/getting-start-tdd/ruby/02-fake-it-and-triangulation.md +238 -0
- package/lib/assets/docs/article/getting-start-tdd/ruby/03-obvious-implementation-and-refactoring.md +228 -0
- package/lib/assets/docs/article/getting-start-tdd/ruby/04-version-control-and-conventional-commits.md +112 -0
- package/lib/assets/docs/article/getting-start-tdd/ruby/05-package-management-and-static-analysis.md +287 -0
- package/lib/assets/docs/article/getting-start-tdd/ruby/06-task-runner-and-ci-cd.md +248 -0
- package/lib/assets/docs/article/getting-start-tdd/ruby/07-encapsulation-and-polymorphism.md +279 -0
- package/lib/assets/docs/article/getting-start-tdd/ruby/08-design-patterns.md +329 -0
- package/lib/assets/docs/article/getting-start-tdd/ruby/09-solid-principles-and-module-design.md +196 -0
- package/lib/assets/docs/article/getting-start-tdd/ruby/10-higher-order-functions-and-composition.md +175 -0
- package/lib/assets/docs/article/getting-start-tdd/ruby/11-immutable-data-and-pipeline.md +233 -0
- package/lib/assets/docs/article/getting-start-tdd/ruby/12-error-handling-and-type-safety.md +398 -0
- package/lib/assets/docs/article/getting-start-tdd/ruby/index.md +83 -0
- package/lib/assets/docs/article/getting-start-tdd/rust/01-todo-list-and-first-test.md +211 -0
- package/lib/assets/docs/article/getting-start-tdd/rust/02-fake-it-and-triangulation.md +264 -0
- package/lib/assets/docs/article/getting-start-tdd/rust/03-obvious-implementation-and-refactoring.md +233 -0
- package/lib/assets/docs/article/getting-start-tdd/rust/04-version-control-and-conventional-commits.md +92 -0
- package/lib/assets/docs/article/getting-start-tdd/rust/05-package-management-and-static-analysis.md +212 -0
- package/lib/assets/docs/article/getting-start-tdd/rust/06-task-runner-and-ci-cd.md +164 -0
- package/lib/assets/docs/article/getting-start-tdd/rust/07-encapsulation-and-polymorphism.md +142 -0
- package/lib/assets/docs/article/getting-start-tdd/rust/08-design-patterns.md +145 -0
- package/lib/assets/docs/article/getting-start-tdd/rust/09-solid-principles-and-module-design.md +110 -0
- package/lib/assets/docs/article/getting-start-tdd/rust/10-higher-order-functions-and-composition.md +94 -0
- package/lib/assets/docs/article/getting-start-tdd/rust/11-immutable-data-and-pipeline.md +105 -0
- package/lib/assets/docs/article/getting-start-tdd/rust/12-error-handling-and-type-safety.md +112 -0
- package/lib/assets/docs/article/getting-start-tdd/rust/index.md +83 -0
- package/lib/assets/docs/article/getting-start-tdd/scala/01-todo-list-and-first-test.md +111 -0
- package/lib/assets/docs/article/getting-start-tdd/scala/02-fake-it-and-triangulation.md +107 -0
- package/lib/assets/docs/article/getting-start-tdd/scala/03-obvious-implementation-and-refactoring.md +99 -0
- package/lib/assets/docs/article/getting-start-tdd/scala/04-version-control-and-conventional-commits.md +123 -0
- package/lib/assets/docs/article/getting-start-tdd/scala/05-package-management-and-static-analysis.md +196 -0
- package/lib/assets/docs/article/getting-start-tdd/scala/06-task-runner-and-ci-cd.md +186 -0
- package/lib/assets/docs/article/getting-start-tdd/scala/07-case-classes-and-traits.md +139 -0
- package/lib/assets/docs/article/getting-start-tdd/scala/08-pattern-matching-and-sealed-traits.md +106 -0
- package/lib/assets/docs/article/getting-start-tdd/scala/09-packages-and-module-design.md +75 -0
- package/lib/assets/docs/article/getting-start-tdd/scala/10-higher-order-functions-and-composition.md +104 -0
- package/lib/assets/docs/article/getting-start-tdd/scala/11-collections-and-lazy-evaluation.md +94 -0
- package/lib/assets/docs/article/getting-start-tdd/scala/12-error-handling-and-type-safety.md +92 -0
- package/lib/assets/docs/article/getting-start-tdd/scala/index.md +65 -0
- package/lib/assets/docs/article/grokking-concurrency/all/index.md +404 -0
- package/lib/assets/docs/article/grokking-concurrency/all/part-1-ch02-sequential.md +554 -0
- package/lib/assets/docs/article/grokking-concurrency/all/part-2-ch04-05-threads.md +469 -0
- package/lib/assets/docs/article/grokking-concurrency/all/part-3-ch06-multitasking.md +520 -0
- package/lib/assets/docs/article/grokking-concurrency/all/part-4-ch07-parallel-patterns.md +420 -0
- package/lib/assets/docs/article/grokking-concurrency/all/part-5-ch08-09-synchronization.md +510 -0
- package/lib/assets/docs/article/grokking-concurrency/all/part-6-ch10-11-nonblocking-io.md +435 -0
- package/lib/assets/docs/article/grokking-concurrency/all/part-7-ch12-async.md +465 -0
- package/lib/assets/docs/article/grokking-concurrency/all/part-8-ch13-mapreduce.md +377 -0
- package/lib/assets/docs/article/grokking-concurrency/clojure/index.md +116 -0
- package/lib/assets/docs/article/grokking-concurrency/clojure/part-1.md +108 -0
- package/lib/assets/docs/article/grokking-concurrency/clojure/part-2.md +101 -0
- package/lib/assets/docs/article/grokking-concurrency/clojure/part-3.md +122 -0
- package/lib/assets/docs/article/grokking-concurrency/clojure/part-4.md +123 -0
- package/lib/assets/docs/article/grokking-concurrency/clojure/part-5.md +118 -0
- package/lib/assets/docs/article/grokking-concurrency/clojure/part-6.md +89 -0
- package/lib/assets/docs/article/grokking-concurrency/clojure/part-7.md +100 -0
- package/lib/assets/docs/article/grokking-concurrency/clojure/part-8.md +120 -0
- package/lib/assets/docs/article/grokking-concurrency/csharp/index.md +101 -0
- package/lib/assets/docs/article/grokking-concurrency/csharp/part-1.md +97 -0
- package/lib/assets/docs/article/grokking-concurrency/csharp/part-2.md +123 -0
- package/lib/assets/docs/article/grokking-concurrency/csharp/part-3.md +101 -0
- package/lib/assets/docs/article/grokking-concurrency/csharp/part-4.md +112 -0
- package/lib/assets/docs/article/grokking-concurrency/csharp/part-5.md +99 -0
- package/lib/assets/docs/article/grokking-concurrency/csharp/part-6.md +61 -0
- package/lib/assets/docs/article/grokking-concurrency/csharp/part-7.md +84 -0
- package/lib/assets/docs/article/grokking-concurrency/csharp/part-8.md +92 -0
- package/lib/assets/docs/article/grokking-concurrency/fsharp/index.md +65 -0
- package/lib/assets/docs/article/grokking-concurrency/fsharp/part-1.md +80 -0
- package/lib/assets/docs/article/grokking-concurrency/fsharp/part-2.md +103 -0
- package/lib/assets/docs/article/grokking-concurrency/fsharp/part-3.md +94 -0
- package/lib/assets/docs/article/grokking-concurrency/fsharp/part-4.md +110 -0
- package/lib/assets/docs/article/grokking-concurrency/fsharp/part-5.md +104 -0
- package/lib/assets/docs/article/grokking-concurrency/fsharp/part-6.md +93 -0
- package/lib/assets/docs/article/grokking-concurrency/fsharp/part-7.md +121 -0
- package/lib/assets/docs/article/grokking-concurrency/fsharp/part-8.md +107 -0
- package/lib/assets/docs/article/grokking-concurrency/haskell/index.md +248 -0
- package/lib/assets/docs/article/grokking-concurrency/haskell/part-1.md +96 -0
- package/lib/assets/docs/article/grokking-concurrency/haskell/part-2.md +96 -0
- package/lib/assets/docs/article/grokking-concurrency/haskell/part-3.md +91 -0
- package/lib/assets/docs/article/grokking-concurrency/haskell/part-4.md +106 -0
- package/lib/assets/docs/article/grokking-concurrency/haskell/part-5.md +99 -0
- package/lib/assets/docs/article/grokking-concurrency/haskell/part-6.md +95 -0
- package/lib/assets/docs/article/grokking-concurrency/haskell/part-7.md +111 -0
- package/lib/assets/docs/article/grokking-concurrency/haskell/part-8.md +118 -0
- package/lib/assets/docs/article/grokking-concurrency/index.md +66 -0
- package/lib/assets/docs/article/grokking-concurrency/java/index.md +102 -0
- package/lib/assets/docs/article/grokking-concurrency/java/part-1.md +308 -0
- package/lib/assets/docs/article/grokking-concurrency/java/part-2.md +334 -0
- package/lib/assets/docs/article/grokking-concurrency/java/part-3.md +221 -0
- package/lib/assets/docs/article/grokking-concurrency/java/part-4.md +213 -0
- package/lib/assets/docs/article/grokking-concurrency/java/part-5.md +112 -0
- package/lib/assets/docs/article/grokking-concurrency/java/part-6.md +69 -0
- package/lib/assets/docs/article/grokking-concurrency/java/part-7.md +101 -0
- package/lib/assets/docs/article/grokking-concurrency/java/part-8.md +101 -0
- package/lib/assets/docs/article/grokking-concurrency/python/index.md +313 -0
- package/lib/assets/docs/article/grokking-concurrency/python/part-1.md +239 -0
- package/lib/assets/docs/article/grokking-concurrency/python/part-2.md +418 -0
- package/lib/assets/docs/article/grokking-concurrency/python/part-3.md +227 -0
- package/lib/assets/docs/article/grokking-concurrency/python/part-4.md +299 -0
- package/lib/assets/docs/article/grokking-concurrency/python/part-5.md +315 -0
- package/lib/assets/docs/article/grokking-concurrency/python/part-6.md +297 -0
- package/lib/assets/docs/article/grokking-concurrency/python/part-7.md +314 -0
- package/lib/assets/docs/article/grokking-concurrency/python/part-8.md +360 -0
- package/lib/assets/docs/article/grokking-concurrency/rust/index.md +270 -0
- package/lib/assets/docs/article/grokking-concurrency/rust/part-1.md +108 -0
- package/lib/assets/docs/article/grokking-concurrency/rust/part-2.md +120 -0
- package/lib/assets/docs/article/grokking-concurrency/rust/part-3.md +126 -0
- package/lib/assets/docs/article/grokking-concurrency/rust/part-4.md +175 -0
- package/lib/assets/docs/article/grokking-concurrency/rust/part-5.md +158 -0
- package/lib/assets/docs/article/grokking-concurrency/rust/part-6.md +94 -0
- package/lib/assets/docs/article/grokking-concurrency/rust/part-7.md +133 -0
- package/lib/assets/docs/article/grokking-concurrency/rust/part-8.md +155 -0
- package/lib/assets/docs/article/grokking-concurrency/scala/index.md +69 -0
- package/lib/assets/docs/article/grokking-concurrency/scala/part-1.md +78 -0
- package/lib/assets/docs/article/grokking-concurrency/scala/part-2.md +112 -0
- package/lib/assets/docs/article/grokking-concurrency/scala/part-3.md +93 -0
- package/lib/assets/docs/article/grokking-concurrency/scala/part-4.md +110 -0
- package/lib/assets/docs/article/grokking-concurrency/scala/part-5.md +119 -0
- package/lib/assets/docs/article/grokking-concurrency/scala/part-6.md +83 -0
- package/lib/assets/docs/article/grokking-concurrency/scala/part-7.md +131 -0
- package/lib/assets/docs/article/grokking-concurrency/scala/part-8.md +129 -0
- package/lib/assets/docs/article/grokkingfp/all/index.md +368 -0
- package/lib/assets/docs/article/grokkingfp/all/part-1-ch01-fp-introduction.md +530 -0
- package/lib/assets/docs/article/grokkingfp/all/part-1-ch02-pure-functions.md +923 -0
- package/lib/assets/docs/article/grokkingfp/all/part-2-ch03-immutable-data.md +1122 -0
- package/lib/assets/docs/article/grokkingfp/all/part-2-ch04-higher-order-functions.md +1104 -0
- package/lib/assets/docs/article/grokkingfp/all/part-2-ch05-flatmap.md +1026 -0
- package/lib/assets/docs/article/grokkingfp/all/part-3-ch06-option.md +777 -0
- package/lib/assets/docs/article/grokkingfp/all/part-3-ch07-either-adt.md +871 -0
- package/lib/assets/docs/article/grokkingfp/all/part-4-ch08-io-monad.md +972 -0
- package/lib/assets/docs/article/grokkingfp/all/part-4-ch09-streams.md +926 -0
- package/lib/assets/docs/article/grokkingfp/all/part-5-ch10-concurrency.md +870 -0
- package/lib/assets/docs/article/grokkingfp/all/part-6-ch11-application.md +715 -0
- package/lib/assets/docs/article/grokkingfp/all/part-6-ch12-testing.md +626 -0
- package/lib/assets/docs/article/grokkingfp/all/writing-plan.md +696 -0
- package/lib/assets/docs/article/grokkingfp/clojure/index.md +276 -0
- package/lib/assets/docs/article/grokkingfp/clojure/part-1.md +667 -0
- package/lib/assets/docs/article/grokkingfp/clojure/part-2.md +643 -0
- package/lib/assets/docs/article/grokkingfp/clojure/part-3.md +620 -0
- package/lib/assets/docs/article/grokkingfp/clojure/part-4.md +697 -0
- package/lib/assets/docs/article/grokkingfp/clojure/part-5.md +751 -0
- package/lib/assets/docs/article/grokkingfp/clojure/part-6.md +721 -0
- package/lib/assets/docs/article/grokkingfp/csharp/index.md +246 -0
- package/lib/assets/docs/article/grokkingfp/csharp/part-1.md +811 -0
- package/lib/assets/docs/article/grokkingfp/csharp/part-2.md +971 -0
- package/lib/assets/docs/article/grokkingfp/csharp/part-3.md +981 -0
- package/lib/assets/docs/article/grokkingfp/csharp/part-4.md +949 -0
- package/lib/assets/docs/article/grokkingfp/csharp/part-5.md +947 -0
- package/lib/assets/docs/article/grokkingfp/csharp/part-6.md +739 -0
- package/lib/assets/docs/article/grokkingfp/elixir/index.md +203 -0
- package/lib/assets/docs/article/grokkingfp/elixir/part-1.md +710 -0
- package/lib/assets/docs/article/grokkingfp/elixir/part-2.md +838 -0
- package/lib/assets/docs/article/grokkingfp/elixir/part-3.md +985 -0
- package/lib/assets/docs/article/grokkingfp/elixir/part-4.md +974 -0
- package/lib/assets/docs/article/grokkingfp/elixir/part-5.md +1284 -0
- package/lib/assets/docs/article/grokkingfp/elixir/part-6.md +1047 -0
- package/lib/assets/docs/article/grokkingfp/fsharp/index.md +210 -0
- package/lib/assets/docs/article/grokkingfp/fsharp/part-1.md +714 -0
- package/lib/assets/docs/article/grokkingfp/fsharp/part-2.md +961 -0
- package/lib/assets/docs/article/grokkingfp/fsharp/part-3.md +972 -0
- package/lib/assets/docs/article/grokkingfp/fsharp/part-4.md +832 -0
- package/lib/assets/docs/article/grokkingfp/fsharp/part-5.md +911 -0
- package/lib/assets/docs/article/grokkingfp/fsharp/part-6.md +920 -0
- package/lib/assets/docs/article/grokkingfp/haskell/index.md +234 -0
- package/lib/assets/docs/article/grokkingfp/haskell/part-1.md +591 -0
- package/lib/assets/docs/article/grokkingfp/haskell/part-2.md +866 -0
- package/lib/assets/docs/article/grokkingfp/haskell/part-3.md +915 -0
- package/lib/assets/docs/article/grokkingfp/haskell/part-4.md +876 -0
- package/lib/assets/docs/article/grokkingfp/haskell/part-5.md +845 -0
- package/lib/assets/docs/article/grokkingfp/haskell/part-6.md +842 -0
- package/lib/assets/docs/article/grokkingfp/index.md +143 -0
- package/lib/assets/docs/article/grokkingfp/java/index.md +211 -0
- package/lib/assets/docs/article/grokkingfp/java/part-1.md +646 -0
- package/lib/assets/docs/article/grokkingfp/java/part-2.md +667 -0
- package/lib/assets/docs/article/grokkingfp/java/part-3.md +672 -0
- package/lib/assets/docs/article/grokkingfp/java/part-4.md +771 -0
- package/lib/assets/docs/article/grokkingfp/java/part-5.md +959 -0
- package/lib/assets/docs/article/grokkingfp/java/part-6.md +1324 -0
- package/lib/assets/docs/article/grokkingfp/python/index.md +258 -0
- package/lib/assets/docs/article/grokkingfp/python/part-1.md +437 -0
- package/lib/assets/docs/article/grokkingfp/python/part-2.md +958 -0
- package/lib/assets/docs/article/grokkingfp/python/part-3.md +1004 -0
- package/lib/assets/docs/article/grokkingfp/python/part-4.md +765 -0
- package/lib/assets/docs/article/grokkingfp/python/part-5.md +747 -0
- package/lib/assets/docs/article/grokkingfp/python/part-6.md +861 -0
- package/lib/assets/docs/article/grokkingfp/ruby/index.md +330 -0
- package/lib/assets/docs/article/grokkingfp/ruby/part-1.md +753 -0
- package/lib/assets/docs/article/grokkingfp/ruby/part-2.md +938 -0
- package/lib/assets/docs/article/grokkingfp/ruby/part-3.md +946 -0
- package/lib/assets/docs/article/grokkingfp/ruby/part-4.md +921 -0
- package/lib/assets/docs/article/grokkingfp/ruby/part-5.md +908 -0
- package/lib/assets/docs/article/grokkingfp/ruby/part-6.md +1410 -0
- package/lib/assets/docs/article/grokkingfp/rust/index.md +242 -0
- package/lib/assets/docs/article/grokkingfp/rust/part-1.md +634 -0
- package/lib/assets/docs/article/grokkingfp/rust/part-2.md +1060 -0
- package/lib/assets/docs/article/grokkingfp/rust/part-3.md +994 -0
- package/lib/assets/docs/article/grokkingfp/rust/part-4.md +571 -0
- package/lib/assets/docs/article/grokkingfp/rust/part-5.md +705 -0
- package/lib/assets/docs/article/grokkingfp/rust/part-6.md +508 -0
- package/lib/assets/docs/article/grokkingfp/scala/index.md +171 -0
- package/lib/assets/docs/article/grokkingfp/scala/part-1.md +541 -0
- package/lib/assets/docs/article/grokkingfp/scala/part-2.md +946 -0
- package/lib/assets/docs/article/grokkingfp/scala/part-3.md +917 -0
- package/lib/assets/docs/article/grokkingfp/scala/part-4.md +742 -0
- package/lib/assets/docs/article/grokkingfp/scala/part-5.md +722 -0
- package/lib/assets/docs/article/grokkingfp/scala/part-6.md +865 -0
- package/lib/assets/docs/article/grokkingfp/typescript/index.md +273 -0
- package/lib/assets/docs/article/grokkingfp/typescript/part-1.md +559 -0
- package/lib/assets/docs/article/grokkingfp/typescript/part-2.md +1129 -0
- package/lib/assets/docs/article/grokkingfp/typescript/part-3.md +842 -0
- package/lib/assets/docs/article/grokkingfp/typescript/part-4.md +1085 -0
- package/lib/assets/docs/article/grokkingfp/typescript/part-5.md +717 -0
- package/lib/assets/docs/article/grokkingfp/typescript/part-6.md +980 -0
- package/lib/assets/docs/article/index.md +36 -0
- package/lib/assets/docs/design/index.md +39 -27
- package/lib/assets/docs/development/index.md +11 -1
- package/lib/assets/docs/index.md +33 -106
- package/lib/assets/docs/operation/index.md +16 -6
- package/lib/assets/docs/reference/index.md +5 -4
- package/lib/assets/docs/requirements/index.md +13 -6
- package/lib/assets/docs/strategy/index.md +11 -4
- package/lib/assets/docs/template/index.md +9 -5
- package/lib/assets/mkdocs.yml +29 -17
- package/package.json +1 -1
|
@@ -0,0 +1,972 @@
|
|
|
1
|
+
# Part III: エラーハンドリングと Option/Result
|
|
2
|
+
|
|
3
|
+
本章では、関数型プログラミングにおける安全なエラーハンドリングを学びます。`null` や例外に頼らず、`Option` と `Result` を使って型安全にエラーを扱う方法を習得します。
|
|
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 "NullReferenceException の危険"
|
|
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
|
+
`'a option` は「`'a` 型の値があるか、ないか」を表す型です。
|
|
44
|
+
|
|
45
|
+
```plantuml
|
|
46
|
+
@startuml
|
|
47
|
+
!theme plain
|
|
48
|
+
|
|
49
|
+
rectangle "'a option" 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/fsharp/src/Ch06/OptionHandling.fs`
|
|
65
|
+
|
|
66
|
+
### 6.3 TV番組のパース例
|
|
67
|
+
|
|
68
|
+
TV番組の文字列をパースする例で Option の使い方を学びます。
|
|
69
|
+
|
|
70
|
+
```fsharp
|
|
71
|
+
type TvShow = {
|
|
72
|
+
Title: string
|
|
73
|
+
Start: int
|
|
74
|
+
End: int
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// 入力例: "Breaking Bad (2008-2013)"
|
|
78
|
+
// 期待する出力: { Title = "Breaking Bad"; Start = 2008; End = 2013 }
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
#### 例外を使う方法(問題あり)
|
|
82
|
+
|
|
83
|
+
```fsharp
|
|
84
|
+
let parseShowUnsafe (rawShow: string) : TvShow =
|
|
85
|
+
let bracketOpen = rawShow.IndexOf('(')
|
|
86
|
+
let bracketClose = rawShow.IndexOf(')')
|
|
87
|
+
let dash = rawShow.IndexOf('-')
|
|
88
|
+
|
|
89
|
+
let name = rawShow.Substring(0, bracketOpen).Trim()
|
|
90
|
+
let yearStart = int (rawShow.Substring(bracketOpen + 1, dash - bracketOpen - 1))
|
|
91
|
+
let yearEnd = int (rawShow.Substring(dash + 1, bracketClose - dash - 1))
|
|
92
|
+
|
|
93
|
+
{ Title = name; Start = yearStart; End = yearEnd }
|
|
94
|
+
|
|
95
|
+
// 正常ケース
|
|
96
|
+
parseShowUnsafe "Breaking Bad (2008-2013)"
|
|
97
|
+
// { Title = "Breaking Bad"; Start = 2008; End = 2013 }
|
|
98
|
+
|
|
99
|
+
// 異常ケース → 例外がスローされる!
|
|
100
|
+
parseShowUnsafe "Chernobyl (2019)" // ArgumentOutOfRangeException
|
|
101
|
+
parseShowUnsafe "The Wire 2002-2008" // ArgumentOutOfRangeException
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
#### Option を使う方法
|
|
105
|
+
|
|
106
|
+
```fsharp
|
|
107
|
+
let parseShow (rawShow: string) : TvShow option =
|
|
108
|
+
match extractName rawShow with
|
|
109
|
+
| None -> None
|
|
110
|
+
| Some name ->
|
|
111
|
+
let yearStart =
|
|
112
|
+
extractYearStart rawShow
|
|
113
|
+
|> Option.orElse (extractSingleYear rawShow)
|
|
114
|
+
let yearEnd =
|
|
115
|
+
extractYearEnd rawShow
|
|
116
|
+
|> Option.orElse (extractSingleYear rawShow)
|
|
117
|
+
match yearStart, yearEnd with
|
|
118
|
+
| Some start, Some endYear ->
|
|
119
|
+
Some { Title = name; Start = start; End = endYear }
|
|
120
|
+
| _ -> None
|
|
121
|
+
|
|
122
|
+
// 正常ケース
|
|
123
|
+
parseShow "Breaking Bad (2008-2013)"
|
|
124
|
+
// Some { Title = "Breaking Bad"; Start = 2008; End = 2013 }
|
|
125
|
+
|
|
126
|
+
// 異常ケース → None が返される(例外なし)
|
|
127
|
+
parseShow "The Wire 2002-2008" // None
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### 6.4 小さな関数から組み立てる
|
|
131
|
+
|
|
132
|
+
複雑なパース処理を小さな関数に分解します。
|
|
133
|
+
|
|
134
|
+
```fsharp
|
|
135
|
+
/// 文字列を安全に整数に変換
|
|
136
|
+
let tryParseInt (s: string) : int option =
|
|
137
|
+
match System.Int32.TryParse(s) with
|
|
138
|
+
| true, value -> Some value
|
|
139
|
+
| false, _ -> None
|
|
140
|
+
|
|
141
|
+
/// 名前を抽出
|
|
142
|
+
let extractName (rawShow: string) : string option =
|
|
143
|
+
let bracketOpen = rawShow.IndexOf('(')
|
|
144
|
+
if bracketOpen > 0 then
|
|
145
|
+
Some (rawShow.Substring(0, bracketOpen).Trim())
|
|
146
|
+
else
|
|
147
|
+
None
|
|
148
|
+
|
|
149
|
+
/// 開始年を抽出
|
|
150
|
+
let extractYearStart (rawShow: string) : int option =
|
|
151
|
+
let bracketOpen = rawShow.IndexOf('(')
|
|
152
|
+
let dash = rawShow.IndexOf('-')
|
|
153
|
+
if bracketOpen <> -1 && dash > bracketOpen + 1 then
|
|
154
|
+
let yearStr = rawShow.Substring(bracketOpen + 1, dash - bracketOpen - 1)
|
|
155
|
+
tryParseInt yearStr
|
|
156
|
+
else
|
|
157
|
+
None
|
|
158
|
+
|
|
159
|
+
/// 終了年を抽出
|
|
160
|
+
let extractYearEnd (rawShow: string) : int option =
|
|
161
|
+
let dash = rawShow.IndexOf('-')
|
|
162
|
+
let bracketClose = rawShow.IndexOf(')')
|
|
163
|
+
if dash <> -1 && bracketClose > dash + 1 then
|
|
164
|
+
let yearStr = rawShow.Substring(dash + 1, bracketClose - dash - 1)
|
|
165
|
+
tryParseInt yearStr
|
|
166
|
+
else
|
|
167
|
+
None
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
```plantuml
|
|
171
|
+
@startuml
|
|
172
|
+
!theme plain
|
|
173
|
+
|
|
174
|
+
rectangle "パース処理の分解" {
|
|
175
|
+
card "\"Breaking Bad (2008-2013)\"" as input
|
|
176
|
+
|
|
177
|
+
rectangle "小さな関数" as funcs {
|
|
178
|
+
card "extractName" as name
|
|
179
|
+
card "extractYearStart" as start
|
|
180
|
+
card "extractYearEnd" as end
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
card "TvShow" as output
|
|
184
|
+
|
|
185
|
+
input --> funcs
|
|
186
|
+
funcs --> output : パターンマッチで合成
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
@enduml
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### 6.5 Option.orElse によるフォールバック
|
|
193
|
+
|
|
194
|
+
`Option.orElse` を使って、最初の Option が `None` の場合に代替を試すことができます。
|
|
195
|
+
|
|
196
|
+
```fsharp
|
|
197
|
+
let seven = Some 7
|
|
198
|
+
let eight = Some 8
|
|
199
|
+
let none: int option = None
|
|
200
|
+
|
|
201
|
+
seven |> Option.orElse eight // Some 7 - 最初が Some なのでそのまま
|
|
202
|
+
none |> Option.orElse eight // Some 8 - 最初が None なので代替を使用
|
|
203
|
+
seven |> Option.orElse none // Some 7
|
|
204
|
+
none |> Option.orElse none // None
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
#### 単年の番組に対応する
|
|
208
|
+
|
|
209
|
+
「Chernobyl (2019)」のような単年の番組をパースできるようにします。
|
|
210
|
+
|
|
211
|
+
```fsharp
|
|
212
|
+
/// 単年を抽出
|
|
213
|
+
let extractSingleYear (rawShow: string) : int option =
|
|
214
|
+
let dash = rawShow.IndexOf('-')
|
|
215
|
+
let bracketOpen = rawShow.IndexOf('(')
|
|
216
|
+
let bracketClose = rawShow.IndexOf(')')
|
|
217
|
+
if dash = -1 && bracketOpen <> -1 && bracketClose > bracketOpen + 1 then
|
|
218
|
+
let yearStr = rawShow.Substring(bracketOpen + 1, bracketClose - bracketOpen - 1)
|
|
219
|
+
tryParseInt yearStr
|
|
220
|
+
else
|
|
221
|
+
None
|
|
222
|
+
|
|
223
|
+
let parseShow (rawShow: string) : TvShow option =
|
|
224
|
+
match extractName rawShow with
|
|
225
|
+
| None -> None
|
|
226
|
+
| Some name ->
|
|
227
|
+
let yearStart =
|
|
228
|
+
extractYearStart rawShow
|
|
229
|
+
|> Option.orElse (extractSingleYear rawShow)
|
|
230
|
+
let yearEnd =
|
|
231
|
+
extractYearEnd rawShow
|
|
232
|
+
|> Option.orElse (extractSingleYear rawShow)
|
|
233
|
+
match yearStart, yearEnd with
|
|
234
|
+
| Some start, Some endYear ->
|
|
235
|
+
Some { Title = name; Start = start; End = endYear }
|
|
236
|
+
| _ -> None
|
|
237
|
+
|
|
238
|
+
// これで単年の番組もパースできる
|
|
239
|
+
parseShow "Chernobyl (2019)"
|
|
240
|
+
// Some { Title = "Chernobyl"; Start = 2019; End = 2019 }
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
```plantuml
|
|
244
|
+
@startuml
|
|
245
|
+
!theme plain
|
|
246
|
+
|
|
247
|
+
rectangle "Option.orElse のフロー" {
|
|
248
|
+
card "extractYearStart rawShow" as first
|
|
249
|
+
card "extractSingleYear rawShow" as second
|
|
250
|
+
card "結果" as result
|
|
251
|
+
|
|
252
|
+
first --> result : Some の場合
|
|
253
|
+
first --> second : None の場合
|
|
254
|
+
second --> result
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
@enduml
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### 6.6 Option の主要メソッド
|
|
261
|
+
|
|
262
|
+
| 関数 | 説明 | 例 |
|
|
263
|
+
|------|------|-----|
|
|
264
|
+
| `Option.map` | 値があれば変換 | `Some 5 \|> Option.map ((*) 2)` → `Some 10` |
|
|
265
|
+
| `Option.bind` | 値があれば Option を返す関数を適用 | `Some 5 \|> Option.bind (fun x -> Some (x * 2))` → `Some 10` |
|
|
266
|
+
| `Option.filter` | 条件を満たさなければ None | `Some 5 \|> Option.filter (fun x -> x > 10)` → `None` |
|
|
267
|
+
| `Option.orElse` | None なら代替を使用 | `None \|> Option.orElse (Some 5)` → `Some 5` |
|
|
268
|
+
| `Option.defaultValue` | None ならデフォルト値 | `None \|> Option.defaultValue 0` → `0` |
|
|
269
|
+
| `Option.toList` | List に変換 | `Some 5 \|> Option.toList` → `[5]` |
|
|
270
|
+
|
|
271
|
+
```fsharp
|
|
272
|
+
let year: int option = Some 996
|
|
273
|
+
let noYear: int option = None
|
|
274
|
+
|
|
275
|
+
// map
|
|
276
|
+
year |> Option.map (fun x -> x * 2) // Some 1992
|
|
277
|
+
noYear |> Option.map (fun x -> x * 2) // None
|
|
278
|
+
|
|
279
|
+
// bind
|
|
280
|
+
year |> Option.bind (fun y -> Some (y * 2)) // Some 1992
|
|
281
|
+
noYear |> Option.bind (fun y -> Some (y * 2)) // None
|
|
282
|
+
|
|
283
|
+
// filter
|
|
284
|
+
year |> Option.filter (fun x -> x < 2020) // Some 996
|
|
285
|
+
year |> Option.filter (fun x -> x > 2020) // None
|
|
286
|
+
|
|
287
|
+
// orElse
|
|
288
|
+
year |> Option.orElse (Some 2020) // Some 996
|
|
289
|
+
noYear |> Option.orElse (Some 2020) // Some 2020
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
### 6.7 エラーハンドリング戦略
|
|
293
|
+
|
|
294
|
+
複数の要素をパースする場合、2つの戦略があります。
|
|
295
|
+
|
|
296
|
+
```plantuml
|
|
297
|
+
@startuml
|
|
298
|
+
!theme plain
|
|
299
|
+
|
|
300
|
+
rectangle "エラーハンドリング戦略" {
|
|
301
|
+
rectangle "Best-effort 戦略" as best {
|
|
302
|
+
card "パースできたものだけ返す"
|
|
303
|
+
card "エラーは無視"
|
|
304
|
+
card "TvShow list を返す"
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
rectangle "All-or-nothing 戦略" as all {
|
|
308
|
+
card "全部成功するか、全部失敗"
|
|
309
|
+
card "一つでも失敗したら None"
|
|
310
|
+
card "TvShow list option を返す"
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
@enduml
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
#### Best-effort 戦略
|
|
318
|
+
|
|
319
|
+
```fsharp
|
|
320
|
+
let parseShowsBestEffort (rawShows: string list) : TvShow list =
|
|
321
|
+
rawShows
|
|
322
|
+
|> List.choose parseShow // Some のものだけ抽出
|
|
323
|
+
|
|
324
|
+
let rawShows = [
|
|
325
|
+
"Breaking Bad (2008-2013)"
|
|
326
|
+
"The Wire 2002 2008" // 無効な形式
|
|
327
|
+
"Mad Men (2007-2015)"
|
|
328
|
+
]
|
|
329
|
+
|
|
330
|
+
parseShowsBestEffort rawShows
|
|
331
|
+
// [{ Title = "Breaking Bad"; ... }; { Title = "Mad Men"; ... }]
|
|
332
|
+
// 無効なものは無視される
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
#### All-or-nothing 戦略
|
|
336
|
+
|
|
337
|
+
```fsharp
|
|
338
|
+
let parseShowsAllOrNothing (rawShows: string list) : TvShow list option =
|
|
339
|
+
let parsed = rawShows |> List.map parseShow
|
|
340
|
+
if List.forall Option.isSome parsed then
|
|
341
|
+
Some (parsed |> List.choose id)
|
|
342
|
+
else
|
|
343
|
+
None
|
|
344
|
+
|
|
345
|
+
// 全部成功 → Some [...]
|
|
346
|
+
parseShowsAllOrNothing ["Breaking Bad (2008-2013)"; "Mad Men (2007-2015)"]
|
|
347
|
+
// Some [{ Title = "Breaking Bad"; ... }; { Title = "Mad Men"; ... }]
|
|
348
|
+
|
|
349
|
+
// 一つでも失敗 → None
|
|
350
|
+
parseShowsAllOrNothing ["Breaking Bad (2008-2013)"; "Invalid"]
|
|
351
|
+
// None
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
### 6.8 forall と exists
|
|
355
|
+
|
|
356
|
+
Option で条件判定をする際に便利な関数です。
|
|
357
|
+
|
|
358
|
+
| 関数 | 動作 |
|
|
359
|
+
|------|------|
|
|
360
|
+
| `Option.forall p` | None → true, Some x → p(x) |
|
|
361
|
+
| `Option.exists p` | None → false, Some x → p(x) |
|
|
362
|
+
| `Option.contains x` | Some x と等しいか |
|
|
363
|
+
|
|
364
|
+
```fsharp
|
|
365
|
+
let year = Some 996
|
|
366
|
+
let noYear: int option = None
|
|
367
|
+
|
|
368
|
+
// forall - 「全て」または「存在しない」
|
|
369
|
+
year |> Option.forall (fun x -> x < 2020) // true (996 < 2020)
|
|
370
|
+
noYear |> Option.forall (fun x -> x < 2020) // true (値がないので「全て」が自明に真)
|
|
371
|
+
year |> Option.forall (fun x -> x > 2020) // false
|
|
372
|
+
|
|
373
|
+
// exists - 「存在して条件を満たす」
|
|
374
|
+
year |> Option.exists (fun x -> x < 2020) // true
|
|
375
|
+
noYear |> Option.exists (fun x -> x < 2020) // false (値がないので存在しない)
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
#### 実用例: ユーザーのメールフィルタリング
|
|
379
|
+
|
|
380
|
+
```fsharp
|
|
381
|
+
type User = {
|
|
382
|
+
Name: string
|
|
383
|
+
Email: string option
|
|
384
|
+
Age: int
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
let users = [
|
|
388
|
+
{ Name = "Alice"; Email = Some "alice@example.com"; Age = 25 }
|
|
389
|
+
{ Name = "Bob"; Email = None; Age = 30 }
|
|
390
|
+
{ Name = "Charlie"; Email = Some "charlie@test.com"; Age = 17 }
|
|
391
|
+
]
|
|
392
|
+
|
|
393
|
+
// メールアドレスが設定されていないか、example.com ドメイン
|
|
394
|
+
let filterByDomain domain users =
|
|
395
|
+
users
|
|
396
|
+
|> List.filter (fun user ->
|
|
397
|
+
user.Email |> Option.forall (fun email -> email.EndsWith(domain)))
|
|
398
|
+
|
|
399
|
+
filterByDomain "@example.com" users
|
|
400
|
+
// [Alice; Bob] - Bob は None なので forall = true
|
|
401
|
+
|
|
402
|
+
// メールアドレスが設定されていて、test.com ドメイン
|
|
403
|
+
let filterByDomainExists domain users =
|
|
404
|
+
users
|
|
405
|
+
|> List.filter (fun user ->
|
|
406
|
+
user.Email |> Option.exists (fun email -> email.EndsWith(domain)))
|
|
407
|
+
|
|
408
|
+
filterByDomainExists "@test.com" users
|
|
409
|
+
// [Charlie]
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
---
|
|
413
|
+
|
|
414
|
+
## 第7章: Result 型と複合的なエラー処理
|
|
415
|
+
|
|
416
|
+
### 7.1 Option の限界
|
|
417
|
+
|
|
418
|
+
`Option` は「値があるかないか」しか表現できません。**なぜ失敗したのか**を伝えられません。
|
|
419
|
+
|
|
420
|
+
```plantuml
|
|
421
|
+
@startuml
|
|
422
|
+
!theme plain
|
|
423
|
+
|
|
424
|
+
rectangle "Option vs Result" {
|
|
425
|
+
rectangle "'a option" as opt {
|
|
426
|
+
card "Some value" as some
|
|
427
|
+
card "None" as none
|
|
428
|
+
none -[hidden]-> some
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
rectangle "Result<'a, 'e>" as result {
|
|
432
|
+
card "Ok value" as ok
|
|
433
|
+
card "Error error" as error
|
|
434
|
+
error -[hidden]-> ok
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
opt --> result : 進化
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
note bottom of none
|
|
441
|
+
失敗理由が分からない
|
|
442
|
+
end note
|
|
443
|
+
|
|
444
|
+
note bottom of error
|
|
445
|
+
エラー情報を保持できる
|
|
446
|
+
end note
|
|
447
|
+
|
|
448
|
+
@enduml
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
### 7.2 Result の基本
|
|
452
|
+
|
|
453
|
+
`Result<'T, 'Error>` は「`'T` 型の成功値か、`'Error` 型のエラーか」を表す型です。
|
|
454
|
+
|
|
455
|
+
- `Ok value`: 成功
|
|
456
|
+
- `Error error`: 失敗(エラー情報を保持)
|
|
457
|
+
|
|
458
|
+
**ソースファイル**: `app/fsharp/src/Ch07/ResultHandling.fs`
|
|
459
|
+
|
|
460
|
+
```fsharp
|
|
461
|
+
let extractName (show: string) : Result<string, string> =
|
|
462
|
+
let bracketOpen = show.IndexOf('(')
|
|
463
|
+
if bracketOpen > 0 then
|
|
464
|
+
Ok (show.Substring(0, bracketOpen).Trim())
|
|
465
|
+
else
|
|
466
|
+
Error $"Can't extract name from '{show}'"
|
|
467
|
+
|
|
468
|
+
extractName "The Wire (2002-2008)" // Ok "The Wire"
|
|
469
|
+
extractName "(2022)" // Error "Can't extract name from '(2022)'"
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
### 7.3 Result を使ったパース
|
|
473
|
+
|
|
474
|
+
```fsharp
|
|
475
|
+
let extractYearStart (rawShow: string) : Result<int, string> =
|
|
476
|
+
let bracketOpen = rawShow.IndexOf('(')
|
|
477
|
+
let dash = rawShow.IndexOf('-')
|
|
478
|
+
if bracketOpen <> -1 && dash > bracketOpen + 1 then
|
|
479
|
+
let yearStr = rawShow.Substring(bracketOpen + 1, dash - bracketOpen - 1)
|
|
480
|
+
tryParseInt yearStr
|
|
481
|
+
else
|
|
482
|
+
Error $"Can't extract start year from '{rawShow}'"
|
|
483
|
+
|
|
484
|
+
extractYearStart "The Wire (2002-2008)" // Ok 2002
|
|
485
|
+
extractYearStart "The Wire (-2008)" // Error "Can't extract start year from 'The Wire (-2008)'"
|
|
486
|
+
extractYearStart "The Wire (oops-2008)" // Error "Can't parse 'oops' as integer"
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
### 7.4 Option から Result への変換
|
|
490
|
+
|
|
491
|
+
`Option.toResult` 相当の変換ができます。
|
|
492
|
+
|
|
493
|
+
```fsharp
|
|
494
|
+
let year = Some 996
|
|
495
|
+
let noYear: int option = None
|
|
496
|
+
|
|
497
|
+
// Option を Result に変換
|
|
498
|
+
let toResult errorMsg opt =
|
|
499
|
+
match opt with
|
|
500
|
+
| Some x -> Ok x
|
|
501
|
+
| None -> Error errorMsg
|
|
502
|
+
|
|
503
|
+
year |> toResult "no year given" // Ok 996
|
|
504
|
+
noYear |> toResult "no year given" // Error "no year given"
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
```plantuml
|
|
508
|
+
@startuml
|
|
509
|
+
!theme plain
|
|
510
|
+
|
|
511
|
+
rectangle "Option → Result の変換" {
|
|
512
|
+
card "Some value" as some
|
|
513
|
+
card "None" as none
|
|
514
|
+
|
|
515
|
+
card "Ok value" as ok
|
|
516
|
+
card "Error errorMsg" as error
|
|
517
|
+
|
|
518
|
+
some --> ok : toResult errorMsg
|
|
519
|
+
none --> error : toResult errorMsg
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
@enduml
|
|
523
|
+
```
|
|
524
|
+
|
|
525
|
+
### 7.5 Result による完全なパーサー
|
|
526
|
+
|
|
527
|
+
```fsharp
|
|
528
|
+
let parseShow (rawShow: string) : Result<TvShow, string> =
|
|
529
|
+
match extractName rawShow with
|
|
530
|
+
| Error e -> Error e
|
|
531
|
+
| Ok name ->
|
|
532
|
+
let yearStart =
|
|
533
|
+
extractYearStart rawShow
|
|
534
|
+
|> orElse (extractSingleYear rawShow)
|
|
535
|
+
let yearEnd =
|
|
536
|
+
extractYearEnd rawShow
|
|
537
|
+
|> orElse (extractSingleYear rawShow)
|
|
538
|
+
match yearStart, yearEnd with
|
|
539
|
+
| Ok start, Ok endYear ->
|
|
540
|
+
Ok { Title = name; Start = start; End = endYear }
|
|
541
|
+
| Error e, _ -> Error e
|
|
542
|
+
| _, Error e -> Error e
|
|
543
|
+
|
|
544
|
+
parseShow "The Wire (2002-2008)" // Ok { Title = "The Wire"; Start = 2002; End = 2008 }
|
|
545
|
+
parseShow "Mad Men ()" // Error "Can't extract single year from 'Mad Men ()'"
|
|
546
|
+
parseShow "(2002-2008)" // Error "Can't extract name from '(2002-2008)'"
|
|
547
|
+
```
|
|
548
|
+
|
|
549
|
+
### 7.6 バリデーション
|
|
550
|
+
|
|
551
|
+
Result はバリデーションに最適です。
|
|
552
|
+
|
|
553
|
+
```fsharp
|
|
554
|
+
let validateAge (age: int) : Result<int, string> =
|
|
555
|
+
if age < 0 then Error "Age cannot be negative"
|
|
556
|
+
elif age > 150 then Error "Age cannot be greater than 150"
|
|
557
|
+
else Ok age
|
|
558
|
+
|
|
559
|
+
let validateName (name: string) : Result<string, string> =
|
|
560
|
+
if System.String.IsNullOrWhiteSpace(name) then
|
|
561
|
+
Error "Name cannot be empty"
|
|
562
|
+
elif name.Length > 100 then
|
|
563
|
+
Error "Name cannot be longer than 100 characters"
|
|
564
|
+
else
|
|
565
|
+
Ok name
|
|
566
|
+
|
|
567
|
+
let validateEmail (email: string) : Result<string, string> =
|
|
568
|
+
if System.String.IsNullOrWhiteSpace(email) then
|
|
569
|
+
Error "Email cannot be empty"
|
|
570
|
+
elif not (email.Contains("@")) then
|
|
571
|
+
Error "Email must contain @"
|
|
572
|
+
else
|
|
573
|
+
Ok email
|
|
574
|
+
```
|
|
575
|
+
|
|
576
|
+
### 7.7 代数的データ型(ADT)
|
|
577
|
+
|
|
578
|
+
F# では、判別共用体(Discriminated Union)で直和型を表現します。
|
|
579
|
+
|
|
580
|
+
#### 直積型(Product Type)
|
|
581
|
+
|
|
582
|
+
複数のフィールドを**AND**で組み合わせる型です。
|
|
583
|
+
|
|
584
|
+
```fsharp
|
|
585
|
+
/// アーティスト(直積型)
|
|
586
|
+
type Artist = {
|
|
587
|
+
Name: string
|
|
588
|
+
Genre: MusicGenre
|
|
589
|
+
Origin: Location
|
|
590
|
+
YearsActive: YearsActive
|
|
591
|
+
}
|
|
592
|
+
```
|
|
593
|
+
|
|
594
|
+
#### 直和型(Sum Type)
|
|
595
|
+
|
|
596
|
+
複数の選択肢を**OR**で表す型です。
|
|
597
|
+
|
|
598
|
+
```fsharp
|
|
599
|
+
/// 音楽ジャンル(直和型)
|
|
600
|
+
type MusicGenre =
|
|
601
|
+
| HeavyMetal
|
|
602
|
+
| Pop
|
|
603
|
+
| HardRock
|
|
604
|
+
| Jazz
|
|
605
|
+
| Classical
|
|
606
|
+
|
|
607
|
+
/// 活動期間(直和型)
|
|
608
|
+
type YearsActive =
|
|
609
|
+
| StillActive of since: int
|
|
610
|
+
| ActiveBetween of start: int * endYear: int
|
|
611
|
+
|
|
612
|
+
/// 地域(直和型)
|
|
613
|
+
type Location =
|
|
614
|
+
| US
|
|
615
|
+
| UK
|
|
616
|
+
| England
|
|
617
|
+
| Japan
|
|
618
|
+
| Germany
|
|
619
|
+
| Other of string
|
|
620
|
+
```
|
|
621
|
+
|
|
622
|
+
```plantuml
|
|
623
|
+
@startuml
|
|
624
|
+
!theme plain
|
|
625
|
+
|
|
626
|
+
rectangle "代数的データ型(ADT)" {
|
|
627
|
+
rectangle "直積型(Product Type)" as product {
|
|
628
|
+
card "レコード型"
|
|
629
|
+
card "A AND B AND C"
|
|
630
|
+
card "フィールドの組み合わせ"
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
rectangle "直和型(Sum Type)" as sum {
|
|
634
|
+
card "判別共用体"
|
|
635
|
+
card "A OR B OR C"
|
|
636
|
+
card "選択肢のいずれか"
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
note bottom of product
|
|
641
|
+
Artist = Name AND Genre AND Origin AND YearsActive
|
|
642
|
+
end note
|
|
643
|
+
|
|
644
|
+
note bottom of sum
|
|
645
|
+
YearsActive = StillActive OR ActiveBetween
|
|
646
|
+
end note
|
|
647
|
+
|
|
648
|
+
@enduml
|
|
649
|
+
```
|
|
650
|
+
|
|
651
|
+
### 7.8 パターンマッチング
|
|
652
|
+
|
|
653
|
+
直和型は**パターンマッチング**で処理します。
|
|
654
|
+
|
|
655
|
+
```fsharp
|
|
656
|
+
let wasArtistActive (artist: Artist) (yearStart: int) (yearEnd: int) : bool =
|
|
657
|
+
match artist.YearsActive with
|
|
658
|
+
| StillActive since -> since <= yearEnd
|
|
659
|
+
| ActiveBetween (start, endY) -> start <= yearEnd && endY >= yearStart
|
|
660
|
+
|
|
661
|
+
let activeLength (artist: Artist) (currentYear: int) : int =
|
|
662
|
+
match artist.YearsActive with
|
|
663
|
+
| StillActive since -> currentYear - since
|
|
664
|
+
| ActiveBetween (start, endY) -> endY - start
|
|
665
|
+
|
|
666
|
+
let describeActivity (yearsActive: YearsActive) : string =
|
|
667
|
+
match yearsActive with
|
|
668
|
+
| StillActive since -> $"Active since {since}"
|
|
669
|
+
| ActiveBetween (start, endY) -> $"Active from {start} to {endY}"
|
|
670
|
+
```
|
|
671
|
+
|
|
672
|
+
```plantuml
|
|
673
|
+
@startuml
|
|
674
|
+
!theme plain
|
|
675
|
+
|
|
676
|
+
rectangle "パターンマッチング" {
|
|
677
|
+
card "artist.YearsActive" as input
|
|
678
|
+
|
|
679
|
+
rectangle "match ... with" as match_block {
|
|
680
|
+
card "| StillActive since -> ..." as case1
|
|
681
|
+
card "| ActiveBetween (start, end) -> ..." as case2
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
card "結果" as result
|
|
685
|
+
|
|
686
|
+
input --> match_block
|
|
687
|
+
match_block --> result
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
note bottom
|
|
691
|
+
全てのケースを網羅する必要がある
|
|
692
|
+
(コンパイラがチェック)
|
|
693
|
+
end note
|
|
694
|
+
|
|
695
|
+
@enduml
|
|
696
|
+
```
|
|
697
|
+
|
|
698
|
+
### 7.9 検索条件のモデリング
|
|
699
|
+
|
|
700
|
+
検索条件も ADT でモデリングできます。
|
|
701
|
+
|
|
702
|
+
```fsharp
|
|
703
|
+
type SearchCondition =
|
|
704
|
+
| SearchByGenre of genres: MusicGenre list
|
|
705
|
+
| SearchByOrigin of locations: Location list
|
|
706
|
+
| SearchByActiveYears of start: int * endYear: int
|
|
707
|
+
|
|
708
|
+
let matchesCondition (artist: Artist) (condition: SearchCondition) : bool =
|
|
709
|
+
match condition with
|
|
710
|
+
| SearchByGenre genres -> List.contains artist.Genre genres
|
|
711
|
+
| SearchByOrigin locations -> List.contains artist.Origin locations
|
|
712
|
+
| SearchByActiveYears (start, endY) -> wasArtistActive artist start endY
|
|
713
|
+
|
|
714
|
+
let searchArtists (artists: Artist list) (conditions: SearchCondition list) : Artist list =
|
|
715
|
+
artists
|
|
716
|
+
|> List.filter (fun artist ->
|
|
717
|
+
conditions |> List.forall (matchesCondition artist))
|
|
718
|
+
```
|
|
719
|
+
|
|
720
|
+
### 7.10 支払い方法の例
|
|
721
|
+
|
|
722
|
+
```fsharp
|
|
723
|
+
type PaymentMethod =
|
|
724
|
+
| CreditCard of number: string * expiry: string
|
|
725
|
+
| BankTransfer of accountNumber: string
|
|
726
|
+
| Cash
|
|
727
|
+
|
|
728
|
+
let describePayment (method: PaymentMethod) : string =
|
|
729
|
+
match method with
|
|
730
|
+
| CreditCard (number, _) -> $"Credit card ending in {number}"
|
|
731
|
+
| BankTransfer account -> $"Bank transfer to account {account}"
|
|
732
|
+
| Cash -> "Cash payment"
|
|
733
|
+
|
|
734
|
+
let isValidPayment (method: PaymentMethod) : bool =
|
|
735
|
+
match method with
|
|
736
|
+
| CreditCard (number, expiry) ->
|
|
737
|
+
number.Length >= 4 && expiry.Contains("/")
|
|
738
|
+
| BankTransfer account ->
|
|
739
|
+
account.Length > 0
|
|
740
|
+
| Cash -> true
|
|
741
|
+
```
|
|
742
|
+
|
|
743
|
+
---
|
|
744
|
+
|
|
745
|
+
## まとめ
|
|
746
|
+
|
|
747
|
+
### Part III で学んだこと
|
|
748
|
+
|
|
749
|
+
```plantuml
|
|
750
|
+
@startuml
|
|
751
|
+
!theme plain
|
|
752
|
+
|
|
753
|
+
rectangle "Part III: エラーハンドリング" {
|
|
754
|
+
rectangle "第6章" as ch6 {
|
|
755
|
+
card "Option<'a>"
|
|
756
|
+
card "Some / None"
|
|
757
|
+
card "Option.orElse"
|
|
758
|
+
card "Option.forall / exists"
|
|
759
|
+
card "エラーハンドリング戦略"
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
rectangle "第7章" as ch7 {
|
|
763
|
+
card "Result<'T, 'Error>"
|
|
764
|
+
card "Ok / Error"
|
|
765
|
+
card "エラーメッセージの保持"
|
|
766
|
+
card "ADT(判別共用体)"
|
|
767
|
+
card "パターンマッチング"
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
ch6 --> ch7
|
|
772
|
+
|
|
773
|
+
@enduml
|
|
774
|
+
```
|
|
775
|
+
|
|
776
|
+
### Option vs Result の使い分け
|
|
777
|
+
|
|
778
|
+
| 状況 | 使用する型 |
|
|
779
|
+
|------|------------|
|
|
780
|
+
| 値があるかないかだけが重要 | `'a option` |
|
|
781
|
+
| 失敗理由を伝える必要がある | `Result<'a, string>` |
|
|
782
|
+
| 検索結果が見つからない | `'a option` |
|
|
783
|
+
| バリデーションエラーを伝える | `Result<'a, string>` |
|
|
784
|
+
| 複数のエラー種別がある | `Result<'a, ErrorType>` |
|
|
785
|
+
|
|
786
|
+
### F# と Scala の対応
|
|
787
|
+
|
|
788
|
+
| 概念 | F# | Scala |
|
|
789
|
+
|------|-----|-------|
|
|
790
|
+
| 値の有無 | `option` | `Option` |
|
|
791
|
+
| 成功/失敗 | `Result` | `Either` |
|
|
792
|
+
| 値あり | `Some` | `Some` |
|
|
793
|
+
| 値なし | `None` | `None` |
|
|
794
|
+
| 成功 | `Ok` | `Right` |
|
|
795
|
+
| 失敗 | `Error` | `Left` |
|
|
796
|
+
| 直和型 | 判別共用体 | enum / sealed trait |
|
|
797
|
+
|
|
798
|
+
### キーポイント
|
|
799
|
+
|
|
800
|
+
1. **Option**: 値の有無を型で表現する
|
|
801
|
+
2. **Result**: 成功/失敗とエラー情報を型で表現する
|
|
802
|
+
3. **パターンマッチング**: Option/Result を安全に処理する
|
|
803
|
+
4. **Option.orElse**: フォールバックを提供する
|
|
804
|
+
5. **ADT**: 直積型と直和型でドメインを正確にモデリング
|
|
805
|
+
6. **forall/exists**: Option での条件判定に便利
|
|
806
|
+
|
|
807
|
+
### 次のステップ
|
|
808
|
+
|
|
809
|
+
Part IV では、以下のトピックを学びます:
|
|
810
|
+
|
|
811
|
+
- 副作用の管理
|
|
812
|
+
- 非同期処理
|
|
813
|
+
- ストリーム処理
|
|
814
|
+
|
|
815
|
+
---
|
|
816
|
+
|
|
817
|
+
## 演習問題
|
|
818
|
+
|
|
819
|
+
### 問題 1: Option の基本
|
|
820
|
+
|
|
821
|
+
以下の関数を実装してください。
|
|
822
|
+
|
|
823
|
+
```fsharp
|
|
824
|
+
let safeDivide (a: int) (b: int) : int option = ???
|
|
825
|
+
|
|
826
|
+
// 期待される動作
|
|
827
|
+
assert (safeDivide 10 2 = Some 5)
|
|
828
|
+
assert (safeDivide 10 0 = None)
|
|
829
|
+
assert (safeDivide 7 2 = Some 3)
|
|
830
|
+
```
|
|
831
|
+
|
|
832
|
+
<details>
|
|
833
|
+
<summary>解答</summary>
|
|
834
|
+
|
|
835
|
+
```fsharp
|
|
836
|
+
let safeDivide (a: int) (b: int) : int option =
|
|
837
|
+
if b = 0 then None
|
|
838
|
+
else Some (a / b)
|
|
839
|
+
```
|
|
840
|
+
|
|
841
|
+
</details>
|
|
842
|
+
|
|
843
|
+
### 問題 2: Option の合成
|
|
844
|
+
|
|
845
|
+
以下の関数を実装してください。2つの数値文字列を受け取り、その合計を返します。
|
|
846
|
+
|
|
847
|
+
```fsharp
|
|
848
|
+
let addStrings (a: string) (b: string) : int option = ???
|
|
849
|
+
|
|
850
|
+
// 期待される動作
|
|
851
|
+
assert (addStrings "10" "20" = Some 30)
|
|
852
|
+
assert (addStrings "10" "abc" = None)
|
|
853
|
+
assert (addStrings "xyz" "20" = None)
|
|
854
|
+
```
|
|
855
|
+
|
|
856
|
+
<details>
|
|
857
|
+
<summary>解答</summary>
|
|
858
|
+
|
|
859
|
+
```fsharp
|
|
860
|
+
let tryParseInt (s: string) : int option =
|
|
861
|
+
match System.Int32.TryParse(s) with
|
|
862
|
+
| true, value -> Some value
|
|
863
|
+
| false, _ -> None
|
|
864
|
+
|
|
865
|
+
let addStrings (a: string) (b: string) : int option =
|
|
866
|
+
match tryParseInt a, tryParseInt b with
|
|
867
|
+
| Some x, Some y -> Some (x + y)
|
|
868
|
+
| _ -> None
|
|
869
|
+
```
|
|
870
|
+
|
|
871
|
+
</details>
|
|
872
|
+
|
|
873
|
+
### 問題 3: Result によるバリデーション
|
|
874
|
+
|
|
875
|
+
以下の関数を実装してください。年齢を検証し、エラーメッセージを返します。
|
|
876
|
+
|
|
877
|
+
```fsharp
|
|
878
|
+
let validateAge (age: int) : Result<int, string> = ???
|
|
879
|
+
|
|
880
|
+
// 期待される動作
|
|
881
|
+
assert (validateAge 25 = Ok 25)
|
|
882
|
+
assert (validateAge -5 = Error "Age cannot be negative")
|
|
883
|
+
assert (validateAge 200 = Error "Age cannot be greater than 150")
|
|
884
|
+
```
|
|
885
|
+
|
|
886
|
+
<details>
|
|
887
|
+
<summary>解答</summary>
|
|
888
|
+
|
|
889
|
+
```fsharp
|
|
890
|
+
let validateAge (age: int) : Result<int, string> =
|
|
891
|
+
if age < 0 then Error "Age cannot be negative"
|
|
892
|
+
elif age > 150 then Error "Age cannot be greater than 150"
|
|
893
|
+
else Ok age
|
|
894
|
+
```
|
|
895
|
+
|
|
896
|
+
</details>
|
|
897
|
+
|
|
898
|
+
### 問題 4: パターンマッチング
|
|
899
|
+
|
|
900
|
+
以下の判別共用体とパターンマッチングを使った関数を実装してください。
|
|
901
|
+
|
|
902
|
+
```fsharp
|
|
903
|
+
type PaymentMethod =
|
|
904
|
+
| CreditCard of number: string * expiry: string
|
|
905
|
+
| BankTransfer of accountNumber: string
|
|
906
|
+
| Cash
|
|
907
|
+
|
|
908
|
+
let describePayment (method: PaymentMethod) : string = ???
|
|
909
|
+
|
|
910
|
+
// 期待される動作
|
|
911
|
+
assert (describePayment (CreditCard ("1234", "12/25")) = "Credit card ending in 1234")
|
|
912
|
+
assert (describePayment (BankTransfer "9876") = "Bank transfer to account 9876")
|
|
913
|
+
assert (describePayment Cash = "Cash payment")
|
|
914
|
+
```
|
|
915
|
+
|
|
916
|
+
<details>
|
|
917
|
+
<summary>解答</summary>
|
|
918
|
+
|
|
919
|
+
```fsharp
|
|
920
|
+
let describePayment (method: PaymentMethod) : string =
|
|
921
|
+
match method with
|
|
922
|
+
| CreditCard (number, _) -> $"Credit card ending in {number}"
|
|
923
|
+
| BankTransfer account -> $"Bank transfer to account {account}"
|
|
924
|
+
| Cash -> "Cash payment"
|
|
925
|
+
```
|
|
926
|
+
|
|
927
|
+
</details>
|
|
928
|
+
|
|
929
|
+
### 問題 5: forall と exists
|
|
930
|
+
|
|
931
|
+
以下の条件に合うユーザーを抽出する関数を実装してください。
|
|
932
|
+
|
|
933
|
+
```fsharp
|
|
934
|
+
type User = {
|
|
935
|
+
Name: string
|
|
936
|
+
Email: string option
|
|
937
|
+
Age: int
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
let users = [
|
|
941
|
+
{ Name = "Alice"; Email = Some "alice@example.com"; Age = 25 }
|
|
942
|
+
{ Name = "Bob"; Email = None; Age = 30 }
|
|
943
|
+
{ Name = "Charlie"; Email = Some "charlie@test.com"; Age = 17 }
|
|
944
|
+
]
|
|
945
|
+
|
|
946
|
+
// 1. メールアドレスが設定されていないか、example.com ドメインのユーザー
|
|
947
|
+
let f1 (users: User list) : User list = ???
|
|
948
|
+
|
|
949
|
+
// 2. メールアドレスが設定されていて、test.com ドメインのユーザー
|
|
950
|
+
let f2 (users: User list) : User list = ???
|
|
951
|
+
```
|
|
952
|
+
|
|
953
|
+
<details>
|
|
954
|
+
<summary>解答</summary>
|
|
955
|
+
|
|
956
|
+
```fsharp
|
|
957
|
+
// 1. メールアドレスが設定されていないか、example.com ドメイン
|
|
958
|
+
let f1 (users: User list) : User list =
|
|
959
|
+
users
|
|
960
|
+
|> List.filter (fun user ->
|
|
961
|
+
user.Email |> Option.forall (fun email -> email.EndsWith("@example.com")))
|
|
962
|
+
// [Alice; Bob]
|
|
963
|
+
|
|
964
|
+
// 2. メールアドレスが設定されていて、test.com ドメイン
|
|
965
|
+
let f2 (users: User list) : User list =
|
|
966
|
+
users
|
|
967
|
+
|> List.filter (fun user ->
|
|
968
|
+
user.Email |> Option.exists (fun email -> email.EndsWith("@test.com")))
|
|
969
|
+
// [Charlie]
|
|
970
|
+
```
|
|
971
|
+
|
|
972
|
+
</details>
|