@k2works/claude-code-booster 2.7.1 → 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 -103
- 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/review/index.md +5 -0
- package/lib/assets/docs/strategy/index.md +15 -0
- package/lib/assets/docs/template/index.md +9 -5
- package/lib/assets/mkdocs.yml +33 -19
- package/package.json +1 -1
- package/lib/assets/docs/analysis/index.md +0 -8
- /package/lib/assets/docs/{analysis → strategy}/slide/.gitkeep +0 -0
|
@@ -0,0 +1,981 @@
|
|
|
1
|
+
# Part III: エラーハンドリングと Option/Either
|
|
2
|
+
|
|
3
|
+
本章では、関数型プログラミングにおける安全なエラーハンドリングを学びます。`null` や例外に頼らず、`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 "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
|
+
LanguageExt の `Option<T>` は「`T` 型の値があるか、ないか」を表す型です。
|
|
44
|
+
|
|
45
|
+
```plantuml
|
|
46
|
+
@startuml
|
|
47
|
+
!theme plain
|
|
48
|
+
|
|
49
|
+
rectangle "Option<T>" as option {
|
|
50
|
+
rectangle "Some(value)" as some {
|
|
51
|
+
card "値が存在する"
|
|
52
|
+
card "value: T を保持"
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
rectangle "None" as none {
|
|
56
|
+
card "値が存在しない"
|
|
57
|
+
card "null の代わり"
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
@enduml
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**ソースファイル**: `app/csharp/src/Ch06/OptionHandling.cs`
|
|
65
|
+
|
|
66
|
+
### 6.3 TV番組のパース例
|
|
67
|
+
|
|
68
|
+
TV番組の文字列をパースする例で Option の使い方を学びます。
|
|
69
|
+
|
|
70
|
+
```csharp
|
|
71
|
+
using LanguageExt;
|
|
72
|
+
using static LanguageExt.Prelude;
|
|
73
|
+
|
|
74
|
+
public record TvShow(string Title, int Start, int End);
|
|
75
|
+
|
|
76
|
+
// 入力例: "Breaking Bad (2008-2013)"
|
|
77
|
+
// 期待する出力: new TvShow("Breaking Bad", 2008, 2013)
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
#### 例外を使う方法(問題あり)
|
|
81
|
+
|
|
82
|
+
```csharp
|
|
83
|
+
public static TvShow ParseShowUnsafe(string rawShow)
|
|
84
|
+
{
|
|
85
|
+
var bracketOpen = rawShow.IndexOf('(');
|
|
86
|
+
var bracketClose = rawShow.IndexOf(')');
|
|
87
|
+
var dash = rawShow.IndexOf('-');
|
|
88
|
+
|
|
89
|
+
var name = rawShow.Substring(0, bracketOpen).Trim();
|
|
90
|
+
var yearStart = int.Parse(rawShow.Substring(bracketOpen + 1, dash - bracketOpen - 1));
|
|
91
|
+
var yearEnd = int.Parse(rawShow.Substring(dash + 1, bracketClose - dash - 1));
|
|
92
|
+
|
|
93
|
+
return new TvShow(name, yearStart, yearEnd);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// 正常ケース
|
|
97
|
+
ParseShowUnsafe("Breaking Bad (2008-2013)");
|
|
98
|
+
// new TvShow("Breaking Bad", 2008, 2013)
|
|
99
|
+
|
|
100
|
+
// 異常ケース → 例外がスローされる!
|
|
101
|
+
ParseShowUnsafe("Chernobyl (2019)"); // ArgumentOutOfRangeException
|
|
102
|
+
ParseShowUnsafe("The Wire 2002-2008"); // ArgumentOutOfRangeException
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
#### Option を使う方法
|
|
106
|
+
|
|
107
|
+
```csharp
|
|
108
|
+
public static Option<TvShow> ParseShow(string rawShow) =>
|
|
109
|
+
ExtractName(rawShow).Bind(name =>
|
|
110
|
+
{
|
|
111
|
+
var yearStart = ExtractYearStart(rawShow) || ExtractSingleYear(rawShow);
|
|
112
|
+
var yearEnd = ExtractYearEnd(rawShow) || ExtractSingleYear(rawShow);
|
|
113
|
+
|
|
114
|
+
return from start in yearStart
|
|
115
|
+
from endYear in yearEnd
|
|
116
|
+
select new TvShow(name, start, endYear);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
// 正常ケース
|
|
120
|
+
ParseShow("Breaking Bad (2008-2013)");
|
|
121
|
+
// Some(new TvShow("Breaking Bad", 2008, 2013))
|
|
122
|
+
|
|
123
|
+
// 異常ケース → None が返される(例外なし)
|
|
124
|
+
ParseShow("The Wire 2002-2008"); // None
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### 6.4 小さな関数から組み立てる
|
|
128
|
+
|
|
129
|
+
複雑なパース処理を小さな関数に分解します。
|
|
130
|
+
|
|
131
|
+
```csharp
|
|
132
|
+
/// <summary>
|
|
133
|
+
/// 文字列を安全に整数に変換
|
|
134
|
+
/// </summary>
|
|
135
|
+
public static Option<int> TryParseInt(string s) =>
|
|
136
|
+
int.TryParse(s, out var result) ? Some(result) : None;
|
|
137
|
+
|
|
138
|
+
/// <summary>
|
|
139
|
+
/// 名前を抽出
|
|
140
|
+
/// </summary>
|
|
141
|
+
public static Option<string> ExtractName(string rawShow)
|
|
142
|
+
{
|
|
143
|
+
var bracketOpen = rawShow.IndexOf('(');
|
|
144
|
+
return bracketOpen > 0
|
|
145
|
+
? Some(rawShow.Substring(0, bracketOpen).Trim())
|
|
146
|
+
: None;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/// <summary>
|
|
150
|
+
/// 開始年を抽出
|
|
151
|
+
/// </summary>
|
|
152
|
+
public static Option<int> ExtractYearStart(string rawShow)
|
|
153
|
+
{
|
|
154
|
+
var bracketOpen = rawShow.IndexOf('(');
|
|
155
|
+
var dash = rawShow.IndexOf('-');
|
|
156
|
+
if (bracketOpen != -1 && dash > bracketOpen + 1)
|
|
157
|
+
{
|
|
158
|
+
var yearStr = rawShow.Substring(bracketOpen + 1, dash - bracketOpen - 1);
|
|
159
|
+
return TryParseInt(yearStr);
|
|
160
|
+
}
|
|
161
|
+
return None;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/// <summary>
|
|
165
|
+
/// 終了年を抽出
|
|
166
|
+
/// </summary>
|
|
167
|
+
public static Option<int> ExtractYearEnd(string rawShow)
|
|
168
|
+
{
|
|
169
|
+
var dash = rawShow.IndexOf('-');
|
|
170
|
+
var bracketClose = rawShow.IndexOf(')');
|
|
171
|
+
if (dash != -1 && bracketClose > dash + 1)
|
|
172
|
+
{
|
|
173
|
+
var yearStr = rawShow.Substring(dash + 1, bracketClose - dash - 1);
|
|
174
|
+
return TryParseInt(yearStr);
|
|
175
|
+
}
|
|
176
|
+
return None;
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
```plantuml
|
|
181
|
+
@startuml
|
|
182
|
+
!theme plain
|
|
183
|
+
|
|
184
|
+
rectangle "パース処理の分解" {
|
|
185
|
+
card "\"Breaking Bad (2008-2013)\"" as input
|
|
186
|
+
|
|
187
|
+
rectangle "小さな関数" as funcs {
|
|
188
|
+
card "ExtractName" as name
|
|
189
|
+
card "ExtractYearStart" as start
|
|
190
|
+
card "ExtractYearEnd" as end
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
card "TvShow" as output
|
|
194
|
+
|
|
195
|
+
input --> funcs
|
|
196
|
+
funcs --> output : Bind/LINQ で合成
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
@enduml
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### 6.5 Option の OrElse によるフォールバック
|
|
203
|
+
|
|
204
|
+
LanguageExt では `||` 演算子または `OrElse` を使って、最初の Option が `None` の場合に代替を試すことができます。
|
|
205
|
+
|
|
206
|
+
```csharp
|
|
207
|
+
var seven = Some(7);
|
|
208
|
+
var eight = Some(8);
|
|
209
|
+
Option<int> none = None;
|
|
210
|
+
|
|
211
|
+
seven || eight; // Some(7) - 最初が Some なのでそのまま
|
|
212
|
+
none || eight; // Some(8) - 最初が None なので代替を使用
|
|
213
|
+
seven || none; // Some(7)
|
|
214
|
+
none || none; // None
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
#### 単年の番組に対応する
|
|
218
|
+
|
|
219
|
+
「Chernobyl (2019)」のような単年の番組をパースできるようにします。
|
|
220
|
+
|
|
221
|
+
```csharp
|
|
222
|
+
/// <summary>
|
|
223
|
+
/// 単年を抽出
|
|
224
|
+
/// </summary>
|
|
225
|
+
public static Option<int> ExtractSingleYear(string rawShow)
|
|
226
|
+
{
|
|
227
|
+
var dash = rawShow.IndexOf('-');
|
|
228
|
+
var bracketOpen = rawShow.IndexOf('(');
|
|
229
|
+
var bracketClose = rawShow.IndexOf(')');
|
|
230
|
+
if (dash == -1 && bracketOpen != -1 && bracketClose > bracketOpen + 1)
|
|
231
|
+
{
|
|
232
|
+
var yearStr = rawShow.Substring(bracketOpen + 1, bracketClose - bracketOpen - 1);
|
|
233
|
+
return TryParseInt(yearStr);
|
|
234
|
+
}
|
|
235
|
+
return None;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
public static Option<TvShow> ParseShow(string rawShow) =>
|
|
239
|
+
ExtractName(rawShow).Bind(name =>
|
|
240
|
+
{
|
|
241
|
+
var yearStart = ExtractYearStart(rawShow) || ExtractSingleYear(rawShow);
|
|
242
|
+
var yearEnd = ExtractYearEnd(rawShow) || ExtractSingleYear(rawShow);
|
|
243
|
+
|
|
244
|
+
return from start in yearStart
|
|
245
|
+
from endYear in yearEnd
|
|
246
|
+
select new TvShow(name, start, endYear);
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
// これで単年の番組もパースできる
|
|
250
|
+
ParseShow("Chernobyl (2019)");
|
|
251
|
+
// Some(new TvShow("Chernobyl", 2019, 2019))
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
```plantuml
|
|
255
|
+
@startuml
|
|
256
|
+
!theme plain
|
|
257
|
+
|
|
258
|
+
rectangle "Option.OrElse のフロー" {
|
|
259
|
+
card "ExtractYearStart(rawShow)" as first
|
|
260
|
+
card "ExtractSingleYear(rawShow)" as second
|
|
261
|
+
card "結果" as result
|
|
262
|
+
|
|
263
|
+
first --> result : Some の場合
|
|
264
|
+
first --> second : None の場合
|
|
265
|
+
second --> result
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
@enduml
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### 6.6 Option の主要メソッド
|
|
272
|
+
|
|
273
|
+
| メソッド | 説明 | 例 |
|
|
274
|
+
|---------|------|-----|
|
|
275
|
+
| `Map` | 値があれば変換 | `Some(5).Map(x => x * 2)` → `Some(10)` |
|
|
276
|
+
| `Bind` | 値があれば Option を返す関数を適用 | `Some(5).Bind(x => Some(x * 2))` → `Some(10)` |
|
|
277
|
+
| `Filter` | 条件を満たさなければ None | `Some(5).Filter(x => x > 10)` → `None` |
|
|
278
|
+
| `\|\|` | None なら代替を使用 | `None \|\| Some(5)` → `Some(5)` |
|
|
279
|
+
| `IfNone` | None ならデフォルト値 | `None.IfNone(0)` → `0` |
|
|
280
|
+
| `ToSeq` | Seq に変換 | `Some(5).ToSeq()` → `Seq(5)` |
|
|
281
|
+
|
|
282
|
+
```csharp
|
|
283
|
+
Option<int> year = Some(996);
|
|
284
|
+
Option<int> noYear = None;
|
|
285
|
+
|
|
286
|
+
// Map
|
|
287
|
+
year.Map(x => x * 2); // Some(1992)
|
|
288
|
+
noYear.Map(x => x * 2); // None
|
|
289
|
+
|
|
290
|
+
// Bind
|
|
291
|
+
year.Bind(y => Some(y * 2)); // Some(1992)
|
|
292
|
+
noYear.Bind(y => Some(y * 2)); // None
|
|
293
|
+
|
|
294
|
+
// Filter
|
|
295
|
+
year.Filter(x => x < 2020); // Some(996)
|
|
296
|
+
year.Filter(x => x > 2020); // None
|
|
297
|
+
|
|
298
|
+
// || (OrElse)
|
|
299
|
+
year || Some(2020); // Some(996)
|
|
300
|
+
noYear || Some(2020); // Some(2020)
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
### 6.7 エラーハンドリング戦略
|
|
304
|
+
|
|
305
|
+
複数の要素をパースする場合、2つの戦略があります。
|
|
306
|
+
|
|
307
|
+
```plantuml
|
|
308
|
+
@startuml
|
|
309
|
+
!theme plain
|
|
310
|
+
|
|
311
|
+
rectangle "エラーハンドリング戦略" {
|
|
312
|
+
rectangle "Best-effort 戦略" as best {
|
|
313
|
+
card "パースできたものだけ返す"
|
|
314
|
+
card "エラーは無視"
|
|
315
|
+
card "Seq<TvShow> を返す"
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
rectangle "All-or-nothing 戦略" as all {
|
|
319
|
+
card "全部成功するか、全部失敗"
|
|
320
|
+
card "一つでも失敗したら None"
|
|
321
|
+
card "Option<Seq<TvShow>> を返す"
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
@enduml
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
#### Best-effort 戦略
|
|
329
|
+
|
|
330
|
+
```csharp
|
|
331
|
+
public static Seq<TvShow> ParseShowsBestEffort(Seq<string> rawShows) =>
|
|
332
|
+
rawShows.Bind(raw => ParseShow(raw).ToSeq());
|
|
333
|
+
|
|
334
|
+
var rawShows = Seq(
|
|
335
|
+
"Breaking Bad (2008-2013)",
|
|
336
|
+
"The Wire 2002 2008", // 無効な形式
|
|
337
|
+
"Mad Men (2007-2015)"
|
|
338
|
+
);
|
|
339
|
+
|
|
340
|
+
ParseShowsBestEffort(rawShows);
|
|
341
|
+
// Seq(TvShow("Breaking Bad", ...), TvShow("Mad Men", ...))
|
|
342
|
+
// 無効なものは無視される
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
#### All-or-nothing 戦略
|
|
346
|
+
|
|
347
|
+
```csharp
|
|
348
|
+
public static Option<Seq<TvShow>> ParseShowsAllOrNothing(Seq<string> rawShows)
|
|
349
|
+
{
|
|
350
|
+
var parsed = rawShows.Map(ParseShow);
|
|
351
|
+
return parsed.ForAll(opt => opt.IsSome)
|
|
352
|
+
? Some(parsed.Bind(opt => opt.ToSeq()))
|
|
353
|
+
: None;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// 全部成功 → Some(...)
|
|
357
|
+
ParseShowsAllOrNothing(Seq("Breaking Bad (2008-2013)", "Mad Men (2007-2015)"));
|
|
358
|
+
// Some(Seq(TvShow("Breaking Bad", ...), TvShow("Mad Men", ...)))
|
|
359
|
+
|
|
360
|
+
// 一つでも失敗 → None
|
|
361
|
+
ParseShowsAllOrNothing(Seq("Breaking Bad (2008-2013)", "Invalid"));
|
|
362
|
+
// None
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
### 6.8 ForAll と Exists
|
|
366
|
+
|
|
367
|
+
Option で条件判定をする際に便利なメソッドです。
|
|
368
|
+
|
|
369
|
+
| メソッド | 動作 |
|
|
370
|
+
|----------|------|
|
|
371
|
+
| `ForAll(p)` | None → true, Some(x) → p(x) |
|
|
372
|
+
| `Exists(p)` | None → false, Some(x) → p(x) |
|
|
373
|
+
|
|
374
|
+
```csharp
|
|
375
|
+
Option<int> year = Some(996);
|
|
376
|
+
Option<int> noYear = None;
|
|
377
|
+
|
|
378
|
+
// ForAll - 「全て」または「存在しない」
|
|
379
|
+
year.ForAll(x => x < 2020); // true (996 < 2020)
|
|
380
|
+
noYear.ForAll(x => x < 2020); // true (値がないので「全て」が自明に真)
|
|
381
|
+
year.ForAll(x => x > 2020); // false
|
|
382
|
+
|
|
383
|
+
// Exists - 「存在して条件を満たす」
|
|
384
|
+
year.Exists(x => x < 2020); // true
|
|
385
|
+
noYear.Exists(x => x < 2020); // false (値がないので存在しない)
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
#### 実用例: ユーザーのメールフィルタリング
|
|
389
|
+
|
|
390
|
+
```csharp
|
|
391
|
+
public record User(string Name, Option<string> Email, int Age);
|
|
392
|
+
|
|
393
|
+
var users = Seq(
|
|
394
|
+
new User("Alice", Some("alice@example.com"), 25),
|
|
395
|
+
new User("Bob", None, 30),
|
|
396
|
+
new User("Charlie", Some("charlie@test.com"), 17)
|
|
397
|
+
);
|
|
398
|
+
|
|
399
|
+
// メールアドレスが設定されていないか、example.com ドメイン
|
|
400
|
+
public static Seq<User> FilterByEmailDomain(Seq<User> users, string domain) =>
|
|
401
|
+
users.Filter(user =>
|
|
402
|
+
user.Email.ForAll(email => email.EndsWith(domain)));
|
|
403
|
+
|
|
404
|
+
FilterByEmailDomain(users, "@example.com");
|
|
405
|
+
// Seq(Alice, Bob) - Bob は None なので ForAll = true
|
|
406
|
+
|
|
407
|
+
// メールアドレスが設定されていて、test.com ドメイン
|
|
408
|
+
public static Seq<User> FilterByEmailDomainExists(Seq<User> users, string domain) =>
|
|
409
|
+
users.Filter(user =>
|
|
410
|
+
user.Email.Exists(email => email.EndsWith(domain)));
|
|
411
|
+
|
|
412
|
+
FilterByEmailDomainExists(users, "@test.com");
|
|
413
|
+
// Seq(Charlie)
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
---
|
|
417
|
+
|
|
418
|
+
## 第7章: Either 型と複合的なエラー処理
|
|
419
|
+
|
|
420
|
+
### 7.1 Option の限界
|
|
421
|
+
|
|
422
|
+
`Option` は「値があるかないか」しか表現できません。**なぜ失敗したのか**を伝えられません。
|
|
423
|
+
|
|
424
|
+
```plantuml
|
|
425
|
+
@startuml
|
|
426
|
+
!theme plain
|
|
427
|
+
|
|
428
|
+
rectangle "Option vs Either" {
|
|
429
|
+
rectangle "Option<T>" as opt {
|
|
430
|
+
card "Some(value)" as some
|
|
431
|
+
card "None" as none
|
|
432
|
+
none -[hidden]-> some
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
rectangle "Either<L, R>" as result {
|
|
436
|
+
card "Right(value)" as ok
|
|
437
|
+
card "Left(error)" as error
|
|
438
|
+
error -[hidden]-> ok
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
opt --> result : 進化
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
note bottom of none
|
|
445
|
+
失敗理由が分からない
|
|
446
|
+
end note
|
|
447
|
+
|
|
448
|
+
note bottom of error
|
|
449
|
+
エラー情報を保持できる
|
|
450
|
+
end note
|
|
451
|
+
|
|
452
|
+
@enduml
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
### 7.2 Either の基本
|
|
456
|
+
|
|
457
|
+
LanguageExt の `Either<L, R>` は「`R` 型の成功値か、`L` 型のエラーか」を表す型です。
|
|
458
|
+
|
|
459
|
+
- `Right(value)`: 成功
|
|
460
|
+
- `Left(error)`: 失敗(エラー情報を保持)
|
|
461
|
+
|
|
462
|
+
**ソースファイル**: `app/csharp/src/Ch07/ResultHandling.cs`
|
|
463
|
+
|
|
464
|
+
```csharp
|
|
465
|
+
using LanguageExt;
|
|
466
|
+
using static LanguageExt.Prelude;
|
|
467
|
+
|
|
468
|
+
public static Either<string, string> ExtractName(string rawShow)
|
|
469
|
+
{
|
|
470
|
+
var bracketOpen = rawShow.IndexOf('(');
|
|
471
|
+
return bracketOpen > 0
|
|
472
|
+
? Right(rawShow.Substring(0, bracketOpen).Trim())
|
|
473
|
+
: Left($"Can't extract name from '{rawShow}'");
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
ExtractName("The Wire (2002-2008)"); // Right("The Wire")
|
|
477
|
+
ExtractName("(2022)"); // Left("Can't extract name from '(2022)'")
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
### 7.3 Either を使ったパース
|
|
481
|
+
|
|
482
|
+
```csharp
|
|
483
|
+
public static Either<string, int> ExtractYearStart(string rawShow)
|
|
484
|
+
{
|
|
485
|
+
var bracketOpen = rawShow.IndexOf('(');
|
|
486
|
+
var dash = rawShow.IndexOf('-');
|
|
487
|
+
if (bracketOpen != -1 && dash > bracketOpen + 1)
|
|
488
|
+
{
|
|
489
|
+
var yearStr = rawShow.Substring(bracketOpen + 1, dash - bracketOpen - 1);
|
|
490
|
+
return TryParseInt(yearStr);
|
|
491
|
+
}
|
|
492
|
+
return Left($"Can't extract start year from '{rawShow}'");
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
ExtractYearStart("The Wire (2002-2008)"); // Right(2002)
|
|
496
|
+
ExtractYearStart("The Wire (-2008)"); // Left("Can't extract start year from 'The Wire (-2008)'")
|
|
497
|
+
ExtractYearStart("The Wire (oops-2008)"); // Left("Can't parse 'oops' as integer")
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
### 7.4 Either の OrElse によるフォールバック
|
|
501
|
+
|
|
502
|
+
```csharp
|
|
503
|
+
public static Either<L, R> OrElse<L, R>(this Either<L, R> either, Either<L, R> alternative) =>
|
|
504
|
+
either.IsRight ? either : alternative;
|
|
505
|
+
|
|
506
|
+
var first = Right<string, int>(42);
|
|
507
|
+
var alternative = Right<string, int>(100);
|
|
508
|
+
var error = Left<string, int>("error");
|
|
509
|
+
|
|
510
|
+
first.OrElse(alternative); // Right(42) - 最初が Right なのでそのまま
|
|
511
|
+
error.OrElse(alternative); // Right(100) - 最初が Left なので代替を使用
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
### 7.5 Either による完全なパーサー
|
|
515
|
+
|
|
516
|
+
```csharp
|
|
517
|
+
public static Either<string, TvShow> ParseShow(string rawShow) =>
|
|
518
|
+
ExtractName(rawShow).Bind(name =>
|
|
519
|
+
{
|
|
520
|
+
var yearStart = ExtractYearStart(rawShow).OrElse(ExtractSingleYear(rawShow));
|
|
521
|
+
var yearEnd = ExtractYearEnd(rawShow).OrElse(ExtractSingleYear(rawShow));
|
|
522
|
+
|
|
523
|
+
return from start in yearStart
|
|
524
|
+
from endYear in yearEnd
|
|
525
|
+
select new TvShow(name, start, endYear);
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
ParseShow("The Wire (2002-2008)"); // Right(TvShow("The Wire", 2002, 2008))
|
|
529
|
+
ParseShow("Mad Men ()"); // Left("Can't extract single year from 'Mad Men ()'")
|
|
530
|
+
ParseShow("(2002-2008)"); // Left("Can't extract name from '(2002-2008)'")
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
### 7.6 バリデーション
|
|
534
|
+
|
|
535
|
+
Either はバリデーションに最適です。
|
|
536
|
+
|
|
537
|
+
```csharp
|
|
538
|
+
public static Either<string, int> ValidateAge(int age) =>
|
|
539
|
+
age < 0 ? Left("Age cannot be negative")
|
|
540
|
+
: age > 150 ? Left("Age cannot be greater than 150")
|
|
541
|
+
: Right(age);
|
|
542
|
+
|
|
543
|
+
public static Either<string, string> ValidateName(string name) =>
|
|
544
|
+
string.IsNullOrWhiteSpace(name) ? Left("Name cannot be empty")
|
|
545
|
+
: name.Length > 100 ? Left("Name cannot be longer than 100 characters")
|
|
546
|
+
: Right(name);
|
|
547
|
+
|
|
548
|
+
public static Either<string, string> ValidateEmail(string email) =>
|
|
549
|
+
string.IsNullOrWhiteSpace(email) ? Left("Email cannot be empty")
|
|
550
|
+
: !email.Contains('@') ? Left("Email must contain @")
|
|
551
|
+
: Right(email);
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
### 7.7 代数的データ型(ADT)
|
|
555
|
+
|
|
556
|
+
C# では、abstract record と record を使って直和型を表現します。
|
|
557
|
+
|
|
558
|
+
#### 直積型(Product Type)
|
|
559
|
+
|
|
560
|
+
複数のフィールドを**AND**で組み合わせる型です。
|
|
561
|
+
|
|
562
|
+
```csharp
|
|
563
|
+
/// <summary>
|
|
564
|
+
/// アーティスト(直積型)
|
|
565
|
+
/// </summary>
|
|
566
|
+
public record Artist(string Name, MusicGenre Genre, Location Origin, YearsActive YearsActive);
|
|
567
|
+
```
|
|
568
|
+
|
|
569
|
+
#### 直和型(Sum Type)
|
|
570
|
+
|
|
571
|
+
複数の選択肢を**OR**で表す型です。
|
|
572
|
+
|
|
573
|
+
```csharp
|
|
574
|
+
/// <summary>
|
|
575
|
+
/// 音楽ジャンル(直和型を enum で表現)
|
|
576
|
+
/// </summary>
|
|
577
|
+
public enum MusicGenre
|
|
578
|
+
{
|
|
579
|
+
HeavyMetal,
|
|
580
|
+
Pop,
|
|
581
|
+
HardRock,
|
|
582
|
+
Jazz,
|
|
583
|
+
Classical
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
/// <summary>
|
|
587
|
+
/// 活動期間(直和型を抽象クラスで表現)
|
|
588
|
+
/// </summary>
|
|
589
|
+
public abstract record YearsActive;
|
|
590
|
+
public record StillActive(int Since) : YearsActive;
|
|
591
|
+
public record ActiveBetween(int Start, int EndYear) : YearsActive;
|
|
592
|
+
|
|
593
|
+
/// <summary>
|
|
594
|
+
/// 地域(直和型を抽象クラスで表現)
|
|
595
|
+
/// </summary>
|
|
596
|
+
public abstract record Location;
|
|
597
|
+
public record US() : Location;
|
|
598
|
+
public record UK() : Location;
|
|
599
|
+
public record Japan() : Location;
|
|
600
|
+
public record OtherLocation(string Name) : Location;
|
|
601
|
+
```
|
|
602
|
+
|
|
603
|
+
```plantuml
|
|
604
|
+
@startuml
|
|
605
|
+
!theme plain
|
|
606
|
+
|
|
607
|
+
rectangle "代数的データ型(ADT)" {
|
|
608
|
+
rectangle "直積型(Product Type)" as product {
|
|
609
|
+
card "record 型"
|
|
610
|
+
card "A AND B AND C"
|
|
611
|
+
card "フィールドの組み合わせ"
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
rectangle "直和型(Sum Type)" as sum {
|
|
615
|
+
card "abstract record + 派生 record"
|
|
616
|
+
card "A OR B OR C"
|
|
617
|
+
card "選択肢のいずれか"
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
note bottom of product
|
|
622
|
+
Artist = Name AND Genre AND Origin AND YearsActive
|
|
623
|
+
end note
|
|
624
|
+
|
|
625
|
+
note bottom of sum
|
|
626
|
+
YearsActive = StillActive OR ActiveBetween
|
|
627
|
+
end note
|
|
628
|
+
|
|
629
|
+
@enduml
|
|
630
|
+
```
|
|
631
|
+
|
|
632
|
+
### 7.8 パターンマッチング
|
|
633
|
+
|
|
634
|
+
C# では **switch 式** でパターンマッチングを行います。
|
|
635
|
+
|
|
636
|
+
```csharp
|
|
637
|
+
public static bool WasArtistActive(Artist artist, int yearStart, int yearEnd) =>
|
|
638
|
+
artist.YearsActive switch
|
|
639
|
+
{
|
|
640
|
+
StillActive sa => sa.Since <= yearEnd,
|
|
641
|
+
ActiveBetween ab => ab.Start <= yearEnd && ab.EndYear >= yearStart,
|
|
642
|
+
_ => false
|
|
643
|
+
};
|
|
644
|
+
|
|
645
|
+
public static int ActiveLength(Artist artist, int currentYear) =>
|
|
646
|
+
artist.YearsActive switch
|
|
647
|
+
{
|
|
648
|
+
StillActive sa => currentYear - sa.Since,
|
|
649
|
+
ActiveBetween ab => ab.EndYear - ab.Start,
|
|
650
|
+
_ => 0
|
|
651
|
+
};
|
|
652
|
+
|
|
653
|
+
public static string DescribeActivity(YearsActive yearsActive) =>
|
|
654
|
+
yearsActive switch
|
|
655
|
+
{
|
|
656
|
+
StillActive sa => $"Active since {sa.Since}",
|
|
657
|
+
ActiveBetween ab => $"Active from {ab.Start} to {ab.EndYear}",
|
|
658
|
+
_ => "Unknown"
|
|
659
|
+
};
|
|
660
|
+
```
|
|
661
|
+
|
|
662
|
+
```plantuml
|
|
663
|
+
@startuml
|
|
664
|
+
!theme plain
|
|
665
|
+
|
|
666
|
+
rectangle "パターンマッチング" {
|
|
667
|
+
card "artist.YearsActive" as input
|
|
668
|
+
|
|
669
|
+
rectangle "switch expression" as match_block {
|
|
670
|
+
card "StillActive sa => ..." as case1
|
|
671
|
+
card "ActiveBetween ab => ..." as case2
|
|
672
|
+
card "_ => ... (default)" as case3
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
card "結果" as result
|
|
676
|
+
|
|
677
|
+
input --> match_block
|
|
678
|
+
match_block --> result
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
note bottom
|
|
682
|
+
全てのケースを網羅することを推奨
|
|
683
|
+
(_ はデフォルトケース)
|
|
684
|
+
end note
|
|
685
|
+
|
|
686
|
+
@enduml
|
|
687
|
+
```
|
|
688
|
+
|
|
689
|
+
### 7.9 検索条件のモデリング
|
|
690
|
+
|
|
691
|
+
検索条件も ADT でモデリングできます。
|
|
692
|
+
|
|
693
|
+
```csharp
|
|
694
|
+
public abstract record SearchCondition;
|
|
695
|
+
public record SearchByGenre(Seq<MusicGenre> Genres) : SearchCondition;
|
|
696
|
+
public record SearchByOrigin(Seq<Location> Locations) : SearchCondition;
|
|
697
|
+
public record SearchByActiveYears(int Start, int EndYear) : SearchCondition;
|
|
698
|
+
|
|
699
|
+
public static bool MatchesCondition(Artist artist, SearchCondition condition) =>
|
|
700
|
+
condition switch
|
|
701
|
+
{
|
|
702
|
+
SearchByGenre sg => sg.Genres.Exists(g => g == artist.Genre),
|
|
703
|
+
SearchByOrigin so => so.Locations.Exists(l => l == artist.Origin),
|
|
704
|
+
SearchByActiveYears say => WasArtistActive(artist, say.Start, say.EndYear),
|
|
705
|
+
_ => false
|
|
706
|
+
};
|
|
707
|
+
|
|
708
|
+
public static Seq<Artist> SearchArtists(Seq<Artist> artists, Seq<SearchCondition> conditions) =>
|
|
709
|
+
artists.Filter(artist =>
|
|
710
|
+
conditions.ForAll(condition => MatchesCondition(artist, condition)));
|
|
711
|
+
```
|
|
712
|
+
|
|
713
|
+
### 7.10 支払い方法の例
|
|
714
|
+
|
|
715
|
+
```csharp
|
|
716
|
+
public abstract record PaymentMethod;
|
|
717
|
+
public record CreditCard(string Number, string Expiry) : PaymentMethod;
|
|
718
|
+
public record BankTransfer(string AccountNumber) : PaymentMethod;
|
|
719
|
+
public record Cash() : PaymentMethod;
|
|
720
|
+
|
|
721
|
+
public static string DescribePayment(PaymentMethod method) =>
|
|
722
|
+
method switch
|
|
723
|
+
{
|
|
724
|
+
CreditCard cc => $"Credit card ending in {cc.Number}",
|
|
725
|
+
BankTransfer bt => $"Bank transfer to account {bt.AccountNumber}",
|
|
726
|
+
Cash => "Cash payment",
|
|
727
|
+
_ => "Unknown payment method"
|
|
728
|
+
};
|
|
729
|
+
|
|
730
|
+
public static bool IsValidPayment(PaymentMethod method) =>
|
|
731
|
+
method switch
|
|
732
|
+
{
|
|
733
|
+
CreditCard cc => cc.Number.Length >= 4 && cc.Expiry.Contains('/'),
|
|
734
|
+
BankTransfer bt => bt.AccountNumber.Length > 0,
|
|
735
|
+
Cash => true,
|
|
736
|
+
_ => false
|
|
737
|
+
};
|
|
738
|
+
```
|
|
739
|
+
|
|
740
|
+
---
|
|
741
|
+
|
|
742
|
+
## まとめ
|
|
743
|
+
|
|
744
|
+
### Part III で学んだこと
|
|
745
|
+
|
|
746
|
+
```plantuml
|
|
747
|
+
@startuml
|
|
748
|
+
!theme plain
|
|
749
|
+
|
|
750
|
+
rectangle "Part III: エラーハンドリング" {
|
|
751
|
+
rectangle "第6章" as ch6 {
|
|
752
|
+
card "Option<T>"
|
|
753
|
+
card "Some / None"
|
|
754
|
+
card "|| (OrElse)"
|
|
755
|
+
card "ForAll / Exists"
|
|
756
|
+
card "エラーハンドリング戦略"
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
rectangle "第7章" as ch7 {
|
|
760
|
+
card "Either<L, R>"
|
|
761
|
+
card "Right / Left"
|
|
762
|
+
card "エラーメッセージの保持"
|
|
763
|
+
card "ADT(abstract record)"
|
|
764
|
+
card "switch 式パターンマッチング"
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
ch6 --> ch7
|
|
769
|
+
|
|
770
|
+
@enduml
|
|
771
|
+
```
|
|
772
|
+
|
|
773
|
+
### Option vs Either の使い分け
|
|
774
|
+
|
|
775
|
+
| 状況 | 使用する型 |
|
|
776
|
+
|------|------------|
|
|
777
|
+
| 値があるかないかだけが重要 | `Option<T>` |
|
|
778
|
+
| 失敗理由を伝える必要がある | `Either<string, T>` |
|
|
779
|
+
| 検索結果が見つからない | `Option<T>` |
|
|
780
|
+
| バリデーションエラーを伝える | `Either<string, T>` |
|
|
781
|
+
| 複数のエラー種別がある | `Either<ErrorType, T>` |
|
|
782
|
+
|
|
783
|
+
### F# / Scala / C# の対応
|
|
784
|
+
|
|
785
|
+
| 概念 | F# | Scala | C# (LanguageExt) |
|
|
786
|
+
|------|-----|-------|------------------|
|
|
787
|
+
| 値の有無 | `option` | `Option` | `Option<T>` |
|
|
788
|
+
| 成功/失敗 | `Result` | `Either` | `Either<L, R>` |
|
|
789
|
+
| 値あり | `Some` | `Some` | `Some(value)` |
|
|
790
|
+
| 値なし | `None` | `None` | `None` |
|
|
791
|
+
| 成功 | `Ok` | `Right` | `Right(value)` |
|
|
792
|
+
| 失敗 | `Error` | `Left` | `Left(error)` |
|
|
793
|
+
| 直和型 | 判別共用体 | sealed trait | abstract record |
|
|
794
|
+
|
|
795
|
+
### キーポイント
|
|
796
|
+
|
|
797
|
+
1. **Option**: 値の有無を型で表現する
|
|
798
|
+
2. **Either**: 成功/失敗とエラー情報を型で表現する
|
|
799
|
+
3. **パターンマッチング**: switch 式で Option/Either を安全に処理する
|
|
800
|
+
4. **OrElse (||)**: フォールバックを提供する
|
|
801
|
+
5. **ADT**: 直積型と直和型でドメインを正確にモデリング
|
|
802
|
+
6. **ForAll/Exists**: Option での条件判定に便利
|
|
803
|
+
|
|
804
|
+
### 次のステップ
|
|
805
|
+
|
|
806
|
+
Part IV では、以下のトピックを学びます:
|
|
807
|
+
|
|
808
|
+
- 副作用の管理
|
|
809
|
+
- 非同期処理
|
|
810
|
+
- ストリーム処理
|
|
811
|
+
|
|
812
|
+
---
|
|
813
|
+
|
|
814
|
+
## 演習問題
|
|
815
|
+
|
|
816
|
+
### 問題 1: Option の基本
|
|
817
|
+
|
|
818
|
+
以下の関数を実装してください。
|
|
819
|
+
|
|
820
|
+
```csharp
|
|
821
|
+
public static Option<int> SafeDivide(int a, int b)
|
|
822
|
+
{
|
|
823
|
+
// TODO: 実装してください
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
// 期待される動作
|
|
827
|
+
Assert.Equal(Some(5), SafeDivide(10, 2));
|
|
828
|
+
Assert.Equal(None, SafeDivide(10, 0));
|
|
829
|
+
Assert.Equal(Some(3), SafeDivide(7, 2));
|
|
830
|
+
```
|
|
831
|
+
|
|
832
|
+
<details>
|
|
833
|
+
<summary>解答</summary>
|
|
834
|
+
|
|
835
|
+
```csharp
|
|
836
|
+
public static Option<int> SafeDivide(int a, int b) =>
|
|
837
|
+
b == 0 ? None : Some(a / b);
|
|
838
|
+
```
|
|
839
|
+
|
|
840
|
+
</details>
|
|
841
|
+
|
|
842
|
+
### 問題 2: Option の合成
|
|
843
|
+
|
|
844
|
+
以下の関数を実装してください。2つの数値文字列を受け取り、その合計を返します。
|
|
845
|
+
|
|
846
|
+
```csharp
|
|
847
|
+
public static Option<int> AddStrings(string a, string b)
|
|
848
|
+
{
|
|
849
|
+
// TODO: 実装してください
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
// 期待される動作
|
|
853
|
+
Assert.Equal(Some(30), AddStrings("10", "20"));
|
|
854
|
+
Assert.Equal(None, AddStrings("10", "abc"));
|
|
855
|
+
Assert.Equal(None, AddStrings("xyz", "20"));
|
|
856
|
+
```
|
|
857
|
+
|
|
858
|
+
<details>
|
|
859
|
+
<summary>解答</summary>
|
|
860
|
+
|
|
861
|
+
```csharp
|
|
862
|
+
public static Option<int> TryParseInt(string s) =>
|
|
863
|
+
int.TryParse(s, out var result) ? Some(result) : None;
|
|
864
|
+
|
|
865
|
+
public static Option<int> AddStrings(string a, string b) =>
|
|
866
|
+
from x in TryParseInt(a)
|
|
867
|
+
from y in TryParseInt(b)
|
|
868
|
+
select x + y;
|
|
869
|
+
```
|
|
870
|
+
|
|
871
|
+
</details>
|
|
872
|
+
|
|
873
|
+
### 問題 3: Either によるバリデーション
|
|
874
|
+
|
|
875
|
+
以下の関数を実装してください。年齢を検証し、エラーメッセージを返します。
|
|
876
|
+
|
|
877
|
+
```csharp
|
|
878
|
+
public static Either<string, int> ValidateAge(int age)
|
|
879
|
+
{
|
|
880
|
+
// TODO: 実装してください
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
// 期待される動作
|
|
884
|
+
Assert.True(ValidateAge(25).IsRight);
|
|
885
|
+
Assert.Equal(Left("Age cannot be negative"), ValidateAge(-5));
|
|
886
|
+
Assert.Equal(Left("Age cannot be greater than 150"), ValidateAge(200));
|
|
887
|
+
```
|
|
888
|
+
|
|
889
|
+
<details>
|
|
890
|
+
<summary>解答</summary>
|
|
891
|
+
|
|
892
|
+
```csharp
|
|
893
|
+
public static Either<string, int> ValidateAge(int age) =>
|
|
894
|
+
age < 0 ? Left("Age cannot be negative")
|
|
895
|
+
: age > 150 ? Left("Age cannot be greater than 150")
|
|
896
|
+
: Right(age);
|
|
897
|
+
```
|
|
898
|
+
|
|
899
|
+
</details>
|
|
900
|
+
|
|
901
|
+
### 問題 4: パターンマッチング
|
|
902
|
+
|
|
903
|
+
以下の直和型とパターンマッチングを使った関数を実装してください。
|
|
904
|
+
|
|
905
|
+
```csharp
|
|
906
|
+
public abstract record PaymentMethod;
|
|
907
|
+
public record CreditCard(string Number, string Expiry) : PaymentMethod;
|
|
908
|
+
public record BankTransfer(string AccountNumber) : PaymentMethod;
|
|
909
|
+
public record Cash() : PaymentMethod;
|
|
910
|
+
|
|
911
|
+
public static string DescribePayment(PaymentMethod method)
|
|
912
|
+
{
|
|
913
|
+
// TODO: 実装してください
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
// 期待される動作
|
|
917
|
+
Assert.Equal("Credit card ending in 1234", DescribePayment(new CreditCard("1234", "12/25")));
|
|
918
|
+
Assert.Equal("Bank transfer to account 9876", DescribePayment(new BankTransfer("9876")));
|
|
919
|
+
Assert.Equal("Cash payment", DescribePayment(new Cash()));
|
|
920
|
+
```
|
|
921
|
+
|
|
922
|
+
<details>
|
|
923
|
+
<summary>解答</summary>
|
|
924
|
+
|
|
925
|
+
```csharp
|
|
926
|
+
public static string DescribePayment(PaymentMethod method) =>
|
|
927
|
+
method switch
|
|
928
|
+
{
|
|
929
|
+
CreditCard cc => $"Credit card ending in {cc.Number}",
|
|
930
|
+
BankTransfer bt => $"Bank transfer to account {bt.AccountNumber}",
|
|
931
|
+
Cash => "Cash payment",
|
|
932
|
+
_ => "Unknown payment method"
|
|
933
|
+
};
|
|
934
|
+
```
|
|
935
|
+
|
|
936
|
+
</details>
|
|
937
|
+
|
|
938
|
+
### 問題 5: ForAll と Exists
|
|
939
|
+
|
|
940
|
+
以下の条件に合うユーザーを抽出する関数を実装してください。
|
|
941
|
+
|
|
942
|
+
```csharp
|
|
943
|
+
public record User(string Name, Option<string> Email, int Age);
|
|
944
|
+
|
|
945
|
+
var users = Seq(
|
|
946
|
+
new User("Alice", Some("alice@example.com"), 25),
|
|
947
|
+
new User("Bob", None, 30),
|
|
948
|
+
new User("Charlie", Some("charlie@test.com"), 17)
|
|
949
|
+
);
|
|
950
|
+
|
|
951
|
+
// 1. メールアドレスが設定されていないか、example.com ドメインのユーザー
|
|
952
|
+
public static Seq<User> F1(Seq<User> users)
|
|
953
|
+
{
|
|
954
|
+
// TODO: 実装してください
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
// 2. メールアドレスが設定されていて、test.com ドメインのユーザー
|
|
958
|
+
public static Seq<User> F2(Seq<User> users)
|
|
959
|
+
{
|
|
960
|
+
// TODO: 実装してください
|
|
961
|
+
}
|
|
962
|
+
```
|
|
963
|
+
|
|
964
|
+
<details>
|
|
965
|
+
<summary>解答</summary>
|
|
966
|
+
|
|
967
|
+
```csharp
|
|
968
|
+
// 1. メールアドレスが設定されていないか、example.com ドメイン
|
|
969
|
+
public static Seq<User> F1(Seq<User> users) =>
|
|
970
|
+
users.Filter(user =>
|
|
971
|
+
user.Email.ForAll(email => email.EndsWith("@example.com")));
|
|
972
|
+
// Seq(Alice, Bob)
|
|
973
|
+
|
|
974
|
+
// 2. メールアドレスが設定されていて、test.com ドメイン
|
|
975
|
+
public static Seq<User> F2(Seq<User> users) =>
|
|
976
|
+
users.Filter(user =>
|
|
977
|
+
user.Email.Exists(email => email.EndsWith("@test.com")));
|
|
978
|
+
// Seq(Charlie)
|
|
979
|
+
```
|
|
980
|
+
|
|
981
|
+
</details>
|