@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,920 @@
|
|
|
1
|
+
# Part VI: 実践的なアプリケーション構築とテスト
|
|
2
|
+
|
|
3
|
+
本章では、これまで学んだ関数型プログラミングの概念を統合し、実践的なアプリケーションを構築します。また、関数型プログラミングにおけるテスト戦略についても学びます。
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 第11章: 実践的なアプリケーション構築
|
|
8
|
+
|
|
9
|
+
### 11.1 TravelGuide アプリケーション
|
|
10
|
+
|
|
11
|
+
旅行ガイドアプリケーションを例に、実践的な FP アプリケーションの構築方法を学びます。
|
|
12
|
+
|
|
13
|
+
```plantuml
|
|
14
|
+
@startuml
|
|
15
|
+
!theme plain
|
|
16
|
+
|
|
17
|
+
package "TravelGuide Application" {
|
|
18
|
+
rectangle "Model" {
|
|
19
|
+
class Location {
|
|
20
|
+
Id: LocationId
|
|
21
|
+
Name: string
|
|
22
|
+
Population: int
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
class Attraction {
|
|
26
|
+
Name: string
|
|
27
|
+
Description: string option
|
|
28
|
+
Location: Location
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
class TravelGuide {
|
|
32
|
+
Attraction: Attraction
|
|
33
|
+
Subjects: string list
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
rectangle "Data Access" {
|
|
38
|
+
interface IDataAccess {
|
|
39
|
+
+FindAttractions(): Async<Attraction list>
|
|
40
|
+
+FindArtistsFromLocation(): Async<MusicArtist list>
|
|
41
|
+
+FindMoviesAboutLocation(): Async<Movie list>
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
class TestDataAccess
|
|
45
|
+
class CachedDataAccess
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
IDataAccess <|.. TestDataAccess
|
|
49
|
+
IDataAccess <|.. CachedDataAccess
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
@enduml
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### 11.2 ドメインモデルの定義
|
|
56
|
+
|
|
57
|
+
**ソースファイル**: `app/fsharp/src/Ch11/TravelGuide.fs`
|
|
58
|
+
|
|
59
|
+
```fsharp
|
|
60
|
+
/// ロケーション ID(値オブジェクト)
|
|
61
|
+
type LocationId = LocationId of string
|
|
62
|
+
|
|
63
|
+
module LocationId =
|
|
64
|
+
let value (LocationId id) = id
|
|
65
|
+
let create id = LocationId id
|
|
66
|
+
|
|
67
|
+
/// ロケーション
|
|
68
|
+
type Location = {
|
|
69
|
+
Id: LocationId
|
|
70
|
+
Name: string
|
|
71
|
+
Population: int
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/// アトラクション(観光地)
|
|
75
|
+
type Attraction = {
|
|
76
|
+
Name: string
|
|
77
|
+
Description: string option
|
|
78
|
+
Location: Location
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/// ミュージックアーティスト
|
|
82
|
+
type MusicArtist = {
|
|
83
|
+
Name: string
|
|
84
|
+
Genre: string option
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/// 映画
|
|
88
|
+
type Movie = {
|
|
89
|
+
Name: string
|
|
90
|
+
BoxOffice: int option
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/// 旅行ガイド
|
|
94
|
+
type TravelGuide = {
|
|
95
|
+
Attraction: Attraction
|
|
96
|
+
Subjects: string list
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### 11.3 データアクセス層の抽象化
|
|
101
|
+
|
|
102
|
+
外部データソースへのアクセスをインターフェースで抽象化します。
|
|
103
|
+
|
|
104
|
+
```fsharp
|
|
105
|
+
/// データアクセスインターフェース
|
|
106
|
+
type IDataAccess =
|
|
107
|
+
abstract member FindAttractions:
|
|
108
|
+
name: string * ordering: AttractionOrdering * limit: int
|
|
109
|
+
-> Async<Attraction list>
|
|
110
|
+
abstract member FindArtistsFromLocation:
|
|
111
|
+
locationId: LocationId * limit: int
|
|
112
|
+
-> Async<MusicArtist list>
|
|
113
|
+
abstract member FindMoviesAboutLocation:
|
|
114
|
+
locationId: LocationId * limit: int
|
|
115
|
+
-> Async<Movie list>
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
```plantuml
|
|
119
|
+
@startuml
|
|
120
|
+
!theme plain
|
|
121
|
+
|
|
122
|
+
interface IDataAccess {
|
|
123
|
+
+FindAttractions(): Async<Attraction list>
|
|
124
|
+
+FindArtistsFromLocation(): Async<MusicArtist list>
|
|
125
|
+
+FindMoviesAboutLocation(): Async<Movie list>
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
note right of IDataAccess
|
|
129
|
+
純粋な関数型インターフェース
|
|
130
|
+
すべての操作は Async でラップ
|
|
131
|
+
end note
|
|
132
|
+
|
|
133
|
+
class TestDataAccess {
|
|
134
|
+
-testAttractions: Attraction list
|
|
135
|
+
+FindAttractions(): Async<Attraction list>
|
|
136
|
+
+FindArtistsFromLocation(): Async<MusicArtist list>
|
|
137
|
+
+FindMoviesAboutLocation(): Async<Movie list>
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
class CachedDataAccess {
|
|
141
|
+
-cache: Ref<Map<string, 'a>>
|
|
142
|
+
+FindAttractions(): Async<Attraction list>
|
|
143
|
+
+FindArtistsFromLocation(): Async<MusicArtist list>
|
|
144
|
+
+FindMoviesAboutLocation(): Async<Movie list>
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
IDataAccess <|.. TestDataAccess
|
|
148
|
+
IDataAccess <|.. CachedDataAccess
|
|
149
|
+
|
|
150
|
+
@enduml
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### 11.4 Resource によるリソース管理
|
|
154
|
+
|
|
155
|
+
F# では `IDisposable` パターンと `use` キーワードでリソースを管理できますが、より関数型的な `Resource` 型を実装することもできます。
|
|
156
|
+
|
|
157
|
+
```fsharp
|
|
158
|
+
/// リソースを安全に管理するための型
|
|
159
|
+
type Resource<'a> = {
|
|
160
|
+
Acquire: unit -> Async<'a>
|
|
161
|
+
Release: 'a -> Async<unit>
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
module Resource =
|
|
165
|
+
/// リソースを作成
|
|
166
|
+
let make (acquire: unit -> Async<'a>) (release: 'a -> Async<unit>) : Resource<'a> =
|
|
167
|
+
{ Acquire = acquire; Release = release }
|
|
168
|
+
|
|
169
|
+
/// リソースを使用して操作を実行
|
|
170
|
+
let useAsync (f: 'a -> Async<'b>) (resource: Resource<'a>) : Async<'b> =
|
|
171
|
+
async {
|
|
172
|
+
let! r = resource.Acquire()
|
|
173
|
+
try
|
|
174
|
+
return! f r
|
|
175
|
+
finally
|
|
176
|
+
resource.Release r |> Async.RunSynchronously
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
```plantuml
|
|
181
|
+
@startuml
|
|
182
|
+
!theme plain
|
|
183
|
+
|
|
184
|
+
participant "Application" as app
|
|
185
|
+
participant "Resource" as res
|
|
186
|
+
participant "External Resource" as ext
|
|
187
|
+
|
|
188
|
+
app -> res: Resource.make(acquire)(release)
|
|
189
|
+
res -> ext: acquire
|
|
190
|
+
ext --> res: resource
|
|
191
|
+
|
|
192
|
+
app -> res: useAsync (operation)
|
|
193
|
+
res -> ext: execute operation
|
|
194
|
+
ext --> res: result
|
|
195
|
+
res --> app: result
|
|
196
|
+
|
|
197
|
+
app -> res: release (automatic)
|
|
198
|
+
res -> ext: cleanup
|
|
199
|
+
|
|
200
|
+
note over res
|
|
201
|
+
Resource は例外が発生しても
|
|
202
|
+
必ず release を実行する
|
|
203
|
+
end note
|
|
204
|
+
|
|
205
|
+
@enduml
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### 11.5 キャッシュの実装
|
|
209
|
+
|
|
210
|
+
`Ref` を使用したスレッドセーフなキャッシュの実装:
|
|
211
|
+
|
|
212
|
+
```fsharp
|
|
213
|
+
/// キャッシュ付きデータアクセス
|
|
214
|
+
let cachedDataAccess (dataAccess: IDataAccess) : Async<IDataAccess> =
|
|
215
|
+
async {
|
|
216
|
+
let attractionCache = Ref.Of(Map.empty<string, Attraction list>)
|
|
217
|
+
let artistCache = Ref.Of(Map.empty<string, MusicArtist list>)
|
|
218
|
+
let movieCache = Ref.Of(Map.empty<string, Movie list>)
|
|
219
|
+
|
|
220
|
+
return
|
|
221
|
+
{ new IDataAccess with
|
|
222
|
+
member _.FindAttractions(name, ordering, limit) =
|
|
223
|
+
async {
|
|
224
|
+
let key = sprintf "%s-%A-%d" name ordering limit
|
|
225
|
+
let cached = attractionCache.Get() |> Map.tryFind key
|
|
226
|
+
match cached with
|
|
227
|
+
| Some attractions -> return attractions
|
|
228
|
+
| None ->
|
|
229
|
+
let! attractions = dataAccess.FindAttractions(name, ordering, limit)
|
|
230
|
+
attractionCache.Update(Map.add key attractions)
|
|
231
|
+
return attractions
|
|
232
|
+
}
|
|
233
|
+
// 他のメソッドも同様に実装...
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
```plantuml
|
|
239
|
+
@startuml
|
|
240
|
+
!theme plain
|
|
241
|
+
skinparam activity {
|
|
242
|
+
BackgroundColor White
|
|
243
|
+
BorderColor Black
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
start
|
|
247
|
+
|
|
248
|
+
:キャッシュキー生成;
|
|
249
|
+
|
|
250
|
+
:cache.Get() でキャッシュ確認;
|
|
251
|
+
|
|
252
|
+
if (キャッシュにデータあり?) then (yes)
|
|
253
|
+
:キャッシュからデータ返却;
|
|
254
|
+
else (no)
|
|
255
|
+
:外部 API を呼び出し;
|
|
256
|
+
:結果をキャッシュに保存;
|
|
257
|
+
:結果を返却;
|
|
258
|
+
endif
|
|
259
|
+
|
|
260
|
+
stop
|
|
261
|
+
|
|
262
|
+
note right
|
|
263
|
+
Ref を使用した
|
|
264
|
+
スレッドセーフなキャッシュ
|
|
265
|
+
end note
|
|
266
|
+
|
|
267
|
+
@enduml
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### 11.6 アプリケーションの組み立て
|
|
271
|
+
|
|
272
|
+
すべてのコンポーネントを組み合わせてアプリケーションを構築します。
|
|
273
|
+
|
|
274
|
+
```fsharp
|
|
275
|
+
/// 旅行ガイドを生成
|
|
276
|
+
let travelGuide (data: IDataAccess) (attractionName: string) : Async<TravelGuide option> =
|
|
277
|
+
async {
|
|
278
|
+
let! attractions = data.FindAttractions(attractionName, ByLocationPopulation, 1)
|
|
279
|
+
match attractions with
|
|
280
|
+
| [] -> return None
|
|
281
|
+
| attraction :: _ ->
|
|
282
|
+
let! artists = data.FindArtistsFromLocation(attraction.Location.Id, 2)
|
|
283
|
+
let! movies = data.FindMoviesAboutLocation(attraction.Location.Id, 2)
|
|
284
|
+
let subjects =
|
|
285
|
+
(artists |> List.map (fun a -> a.Name))
|
|
286
|
+
@ (movies |> List.map (fun m -> m.Name))
|
|
287
|
+
return Some {
|
|
288
|
+
Attraction = attraction
|
|
289
|
+
Subjects = subjects
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
```plantuml
|
|
295
|
+
@startuml
|
|
296
|
+
!theme plain
|
|
297
|
+
|
|
298
|
+
rectangle "travelGuide 関数" {
|
|
299
|
+
card "1. アトラクション検索" as step1
|
|
300
|
+
card "2. アーティスト検索" as step2
|
|
301
|
+
card "3. 映画検索" as step3
|
|
302
|
+
card "4. TravelGuide 組み立て" as step4
|
|
303
|
+
|
|
304
|
+
step1 --> step2 : attraction
|
|
305
|
+
step1 --> step3 : location.Id
|
|
306
|
+
step2 --> step4 : artists
|
|
307
|
+
step3 --> step4 : movies
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
note bottom
|
|
311
|
+
async 計算式で
|
|
312
|
+
複数の Async を合成
|
|
313
|
+
end note
|
|
314
|
+
|
|
315
|
+
@enduml
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
---
|
|
319
|
+
|
|
320
|
+
## 第12章: テスト戦略
|
|
321
|
+
|
|
322
|
+
### 12.1 関数型プログラミングのテスト
|
|
323
|
+
|
|
324
|
+
関数型プログラミングでは、純粋関数のおかげでテストが非常に簡単になります。
|
|
325
|
+
|
|
326
|
+
```plantuml
|
|
327
|
+
@startuml
|
|
328
|
+
!theme plain
|
|
329
|
+
|
|
330
|
+
rectangle "テストの種類" {
|
|
331
|
+
rectangle "単体テスト" as unit {
|
|
332
|
+
card "純粋関数のテスト"
|
|
333
|
+
card "高速・独立"
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
rectangle "プロパティベーステスト" as property {
|
|
337
|
+
card "ランダム入力"
|
|
338
|
+
card "不変条件の検証"
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
rectangle "統合テスト" as integration {
|
|
342
|
+
card "コンポーネント連携"
|
|
343
|
+
card "スタブ/モック使用"
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
unit --> property : 補完
|
|
348
|
+
property --> integration : 補完
|
|
349
|
+
|
|
350
|
+
@enduml
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
### 12.2 SearchReport の導入
|
|
354
|
+
|
|
355
|
+
**ソースファイル**: `app/fsharp/src/Ch12/TravelGuideWithReport.fs`
|
|
356
|
+
|
|
357
|
+
テスト可能性を高めるため、`SearchReport` を導入します。
|
|
358
|
+
|
|
359
|
+
```fsharp
|
|
360
|
+
/// 検索の統計情報とエラー情報
|
|
361
|
+
type SearchReport = {
|
|
362
|
+
AttractionsSearched: int
|
|
363
|
+
ArtistsFound: int
|
|
364
|
+
MoviesFound: int
|
|
365
|
+
Errors: string list
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
module SearchReport =
|
|
369
|
+
let empty = {
|
|
370
|
+
AttractionsSearched = 0
|
|
371
|
+
ArtistsFound = 0
|
|
372
|
+
MoviesFound = 0
|
|
373
|
+
Errors = []
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
let addError error report =
|
|
377
|
+
{ report with Errors = error :: report.Errors }
|
|
378
|
+
|
|
379
|
+
/// SearchReport 付きの旅行ガイド
|
|
380
|
+
type TravelGuideWithReport = {
|
|
381
|
+
Attraction: Attraction
|
|
382
|
+
Subjects: string list
|
|
383
|
+
SearchReport: SearchReport
|
|
384
|
+
}
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
```plantuml
|
|
388
|
+
@startuml
|
|
389
|
+
!theme plain
|
|
390
|
+
|
|
391
|
+
class TravelGuideWithReport {
|
|
392
|
+
Attraction: Attraction
|
|
393
|
+
Subjects: string list
|
|
394
|
+
SearchReport: SearchReport
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
class SearchReport {
|
|
398
|
+
AttractionsSearched: int
|
|
399
|
+
ArtistsFound: int
|
|
400
|
+
MoviesFound: int
|
|
401
|
+
Errors: string list
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
TravelGuideWithReport --> SearchReport
|
|
405
|
+
|
|
406
|
+
note right of SearchReport
|
|
407
|
+
検索の統計情報と
|
|
408
|
+
エラー情報を保持
|
|
409
|
+
end note
|
|
410
|
+
|
|
411
|
+
@enduml
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
### 12.3 エラーハンドリングの改善
|
|
415
|
+
|
|
416
|
+
```fsharp
|
|
417
|
+
/// Either を使用したデータアクセスインターフェース
|
|
418
|
+
type IDataAccessWithErrors =
|
|
419
|
+
abstract member FindAttractions:
|
|
420
|
+
name: string * ordering: AttractionOrdering * limit: int
|
|
421
|
+
-> Async<Attraction list>
|
|
422
|
+
abstract member FindArtistsFromLocation:
|
|
423
|
+
locationId: LocationId * limit: int
|
|
424
|
+
-> Async<Result<MusicArtist list, string>>
|
|
425
|
+
abstract member FindMoviesAboutLocation:
|
|
426
|
+
locationId: LocationId * limit: int
|
|
427
|
+
-> Async<Result<Movie list, string>>
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
Result を使用してエラーを明示的に扱います:
|
|
431
|
+
|
|
432
|
+
```fsharp
|
|
433
|
+
/// エラーハンドリング付き旅行ガイド生成
|
|
434
|
+
let travelGuideWithReport (data: IDataAccessWithErrors) (attractionName: string)
|
|
435
|
+
: Async<TravelGuideWithReport option> =
|
|
436
|
+
async {
|
|
437
|
+
let! attractions = data.FindAttractions(attractionName, ByLocationPopulation, 1)
|
|
438
|
+
match attractions with
|
|
439
|
+
| [] -> return None
|
|
440
|
+
| attraction :: _ ->
|
|
441
|
+
let! artistsResult = data.FindArtistsFromLocation(attraction.Location.Id, 2)
|
|
442
|
+
let! moviesResult = data.FindMoviesAboutLocation(attraction.Location.Id, 2)
|
|
443
|
+
|
|
444
|
+
let artistError = match artistsResult with Error e -> Some e | Ok _ -> None
|
|
445
|
+
let movieError = match moviesResult with Error e -> Some e | Ok _ -> None
|
|
446
|
+
let errors = [artistError; movieError] |> List.choose id
|
|
447
|
+
|
|
448
|
+
let artists = artistsResult |> Result.defaultValue []
|
|
449
|
+
let movies = moviesResult |> Result.defaultValue []
|
|
450
|
+
|
|
451
|
+
let subjects =
|
|
452
|
+
(artists |> List.map (fun a -> a.Name))
|
|
453
|
+
@ (movies |> List.map (fun m -> m.Name))
|
|
454
|
+
|
|
455
|
+
let report = {
|
|
456
|
+
AttractionsSearched = 1
|
|
457
|
+
ArtistsFound = List.length artists
|
|
458
|
+
MoviesFound = List.length movies
|
|
459
|
+
Errors = errors
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
return Some {
|
|
463
|
+
Attraction = attraction
|
|
464
|
+
Subjects = subjects
|
|
465
|
+
SearchReport = report
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
### 12.4 スタブを使用したテスト
|
|
471
|
+
|
|
472
|
+
**ソースファイル**: `app/fsharp/tests/Ch12/TravelGuideWithReportTests.fs`
|
|
473
|
+
|
|
474
|
+
```fsharp
|
|
475
|
+
/// 成功するテスト用データアクセス
|
|
476
|
+
let successfulDataAccess
|
|
477
|
+
(testAttractions: Attraction list)
|
|
478
|
+
(testArtists: MusicArtist list)
|
|
479
|
+
(testMovies: Movie list)
|
|
480
|
+
: IDataAccessWithErrors =
|
|
481
|
+
{ new IDataAccessWithErrors with
|
|
482
|
+
member _.FindAttractions(name, _, limit) =
|
|
483
|
+
async {
|
|
484
|
+
return
|
|
485
|
+
testAttractions
|
|
486
|
+
|> List.filter (fun a -> a.Name.Contains(name))
|
|
487
|
+
|> List.truncate limit
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
member _.FindArtistsFromLocation(_, limit) =
|
|
491
|
+
async { return Ok (testArtists |> List.truncate limit) }
|
|
492
|
+
|
|
493
|
+
member _.FindMoviesAboutLocation(_, limit) =
|
|
494
|
+
async { return Ok (testMovies |> List.truncate limit) }
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
/// 部分的に失敗するテスト用データアクセス
|
|
498
|
+
let partiallyFailingDataAccess
|
|
499
|
+
(testAttractions: Attraction list)
|
|
500
|
+
(artistError: string)
|
|
501
|
+
(movieError: string)
|
|
502
|
+
: IDataAccessWithErrors =
|
|
503
|
+
{ new IDataAccessWithErrors with
|
|
504
|
+
member _.FindAttractions(name, _, limit) =
|
|
505
|
+
async {
|
|
506
|
+
return
|
|
507
|
+
testAttractions
|
|
508
|
+
|> List.filter (fun a -> a.Name.Contains(name))
|
|
509
|
+
|> List.truncate limit
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
member _.FindArtistsFromLocation(_, _) =
|
|
513
|
+
async { return Error artistError }
|
|
514
|
+
|
|
515
|
+
member _.FindMoviesAboutLocation(_, _) =
|
|
516
|
+
async { return Error movieError }
|
|
517
|
+
}
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
```plantuml
|
|
521
|
+
@startuml
|
|
522
|
+
!theme plain
|
|
523
|
+
|
|
524
|
+
rectangle "テスト構成" {
|
|
525
|
+
rectangle "本番" as prod {
|
|
526
|
+
interface "IDataAccess" as da1
|
|
527
|
+
class "RealDataAccess" as rda
|
|
528
|
+
da1 <|.. rda
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
rectangle "テスト" as test {
|
|
532
|
+
interface "IDataAccess" as da2
|
|
533
|
+
class "TestDataAccess" as tda
|
|
534
|
+
da2 <|.. tda
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
note bottom of prod
|
|
539
|
+
実際の外部 API に接続
|
|
540
|
+
end note
|
|
541
|
+
|
|
542
|
+
note bottom of test
|
|
543
|
+
テストデータを返す
|
|
544
|
+
async { return ... } で即座に結果を返す
|
|
545
|
+
end note
|
|
546
|
+
|
|
547
|
+
@enduml
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
### 12.5 プロパティベーステスト用ジェネレーター
|
|
551
|
+
|
|
552
|
+
ランダムなテストデータを生成するジェネレーターを実装:
|
|
553
|
+
|
|
554
|
+
```fsharp
|
|
555
|
+
module Generators =
|
|
556
|
+
open System
|
|
557
|
+
|
|
558
|
+
let private random = Random()
|
|
559
|
+
|
|
560
|
+
/// ランダムな LocationId を生成
|
|
561
|
+
let locationId () =
|
|
562
|
+
LocationId (sprintf "Q%d" (random.Next(1, 10000)))
|
|
563
|
+
|
|
564
|
+
/// ランダムな Location を生成
|
|
565
|
+
let location () = {
|
|
566
|
+
Id = locationId ()
|
|
567
|
+
Name = sprintf "City%d" (random.Next(1, 1000))
|
|
568
|
+
Population = random.Next(1000, 10000000)
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
/// ランダムな Attraction を生成
|
|
572
|
+
let attraction () = {
|
|
573
|
+
Name = sprintf "Attraction%d" (random.Next(1, 1000))
|
|
574
|
+
Description = if random.Next(2) = 0 then Some "Description" else None
|
|
575
|
+
Location = location ()
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
/// ランダムな Attraction リストを生成
|
|
579
|
+
let attractions (count: int) =
|
|
580
|
+
[ for _ in 1..count -> attraction () ]
|
|
581
|
+
```
|
|
582
|
+
|
|
583
|
+
### 12.6 不変条件のテスト
|
|
584
|
+
|
|
585
|
+
```fsharp
|
|
586
|
+
module Invariants =
|
|
587
|
+
/// 結果のサイズは limit 以下
|
|
588
|
+
let resultSizeIsLimited (results: 'a list) (limit: int) : bool =
|
|
589
|
+
List.length results <= limit
|
|
590
|
+
|
|
591
|
+
/// SearchReport のエラー数は検索数以下
|
|
592
|
+
let errorsAreBounded (report: SearchReport) : bool =
|
|
593
|
+
List.length report.Errors <= 2 // artists + movies の最大
|
|
594
|
+
|
|
595
|
+
/// ユーティリティ関数
|
|
596
|
+
let filterPopularLocations (locations: Location list) (minPopulation: int) : Location list =
|
|
597
|
+
locations |> List.filter (fun loc -> loc.Population >= minPopulation)
|
|
598
|
+
```
|
|
599
|
+
|
|
600
|
+
### 12.7 プロパティベーステスト(簡易版)
|
|
601
|
+
|
|
602
|
+
```fsharp
|
|
603
|
+
[<Fact>]
|
|
604
|
+
let ``filterPopularLocations の結果サイズは入力以下`` () =
|
|
605
|
+
for _ in 1..10 do
|
|
606
|
+
let locations = Generators.attractions 10 |> List.map (fun a -> a.Location)
|
|
607
|
+
let minPop = System.Random().Next(1, 10000000)
|
|
608
|
+
let result = filterPopularLocations locations minPop
|
|
609
|
+
Assert.True(List.length result <= List.length locations)
|
|
610
|
+
|
|
611
|
+
[<Fact>]
|
|
612
|
+
let ``filterPopularLocations の結果はすべて条件を満たす`` () =
|
|
613
|
+
for _ in 1..10 do
|
|
614
|
+
let locations = Generators.attractions 10 |> List.map (fun a -> a.Location)
|
|
615
|
+
let minPop = System.Random().Next(1, 100000)
|
|
616
|
+
let result = filterPopularLocations locations minPop
|
|
617
|
+
Assert.True(result |> List.forall (fun loc -> loc.Population >= minPop))
|
|
618
|
+
```
|
|
619
|
+
|
|
620
|
+
### 12.8 統合テスト
|
|
621
|
+
|
|
622
|
+
```fsharp
|
|
623
|
+
[<Fact>]
|
|
624
|
+
let ``完全なフローのテスト - 成功ケース`` () =
|
|
625
|
+
let testLocation = {
|
|
626
|
+
Id = LocationId "Q123"
|
|
627
|
+
Name = "Tokyo"
|
|
628
|
+
Population = 14000000
|
|
629
|
+
}
|
|
630
|
+
let testAttractions = [
|
|
631
|
+
{ Name = "Tokyo Tower"; Description = Some "Famous landmark"; Location = testLocation }
|
|
632
|
+
]
|
|
633
|
+
let testArtists = [
|
|
634
|
+
{ Name = "Hikaru Utada"; Genre = Some "Pop" }
|
|
635
|
+
{ Name = "Ayumi Hamasaki"; Genre = Some "Pop" }
|
|
636
|
+
]
|
|
637
|
+
let testMovies = [
|
|
638
|
+
{ Name = "Lost in Translation"; BoxOffice = Some 44000000 }
|
|
639
|
+
]
|
|
640
|
+
|
|
641
|
+
let dataAccess = successfulDataAccess testAttractions testArtists testMovies
|
|
642
|
+
let result = travelGuideWithReport dataAccess "Tokyo" |> Async.RunSynchronously
|
|
643
|
+
|
|
644
|
+
Assert.True(result.IsSome)
|
|
645
|
+
let guide = result.Value
|
|
646
|
+
Assert.Equal("Tokyo Tower", guide.Attraction.Name)
|
|
647
|
+
Assert.Equal(3, List.length guide.Subjects)
|
|
648
|
+
Assert.Empty(guide.SearchReport.Errors)
|
|
649
|
+
|
|
650
|
+
[<Fact>]
|
|
651
|
+
let ``完全なフローのテスト - 部分的失敗ケース`` () =
|
|
652
|
+
let testLocation = { Id = LocationId "Q123"; Name = "Tokyo"; Population = 14000000 }
|
|
653
|
+
let testAttractions = [
|
|
654
|
+
{ Name = "Tokyo Tower"; Description = Some "Famous landmark"; Location = testLocation }
|
|
655
|
+
]
|
|
656
|
+
|
|
657
|
+
let dataAccess = partiallyFailingDataAccess testAttractions "Network error" "Timeout"
|
|
658
|
+
let result = travelGuideWithReport dataAccess "Tokyo" |> Async.RunSynchronously
|
|
659
|
+
|
|
660
|
+
Assert.True(result.IsSome)
|
|
661
|
+
let guide = result.Value
|
|
662
|
+
Assert.Equal(2, List.length guide.SearchReport.Errors)
|
|
663
|
+
```
|
|
664
|
+
|
|
665
|
+
### 12.9 テストピラミッド
|
|
666
|
+
|
|
667
|
+
```plantuml
|
|
668
|
+
@startuml
|
|
669
|
+
!theme plain
|
|
670
|
+
|
|
671
|
+
rectangle "テストピラミッド" {
|
|
672
|
+
rectangle "E2E テスト\n(少数)" as e2e
|
|
673
|
+
rectangle "統合テスト\n(中程度)" as integration
|
|
674
|
+
rectangle "単体テスト + プロパティテスト\n(多数)" as unit
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
e2e -[hidden]down- integration
|
|
678
|
+
integration -[hidden]down- unit
|
|
679
|
+
|
|
680
|
+
note right of unit
|
|
681
|
+
FP では純粋関数が多いため
|
|
682
|
+
単体テストが非常に効果的
|
|
683
|
+
end note
|
|
684
|
+
|
|
685
|
+
@enduml
|
|
686
|
+
```
|
|
687
|
+
|
|
688
|
+
---
|
|
689
|
+
|
|
690
|
+
## まとめ
|
|
691
|
+
|
|
692
|
+
### Part VI で学んだこと
|
|
693
|
+
|
|
694
|
+
```plantuml
|
|
695
|
+
@startuml
|
|
696
|
+
!theme plain
|
|
697
|
+
|
|
698
|
+
rectangle "Part VI: 実践的なアプリケーション" {
|
|
699
|
+
rectangle "第11章" as ch11 {
|
|
700
|
+
card "ドメインモデル設計"
|
|
701
|
+
card "IDataAccess 抽象化"
|
|
702
|
+
card "Resource 管理"
|
|
703
|
+
card "キャッシュ実装"
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
rectangle "第12章" as ch12 {
|
|
707
|
+
card "SearchReport"
|
|
708
|
+
card "スタブ/モック"
|
|
709
|
+
card "プロパティベーステスト"
|
|
710
|
+
card "統合テスト"
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
ch11 --> ch12
|
|
715
|
+
|
|
716
|
+
@enduml
|
|
717
|
+
```
|
|
718
|
+
|
|
719
|
+
### キーポイント
|
|
720
|
+
|
|
721
|
+
1. **抽象化の重要性**: IDataAccess インターフェースで外部依存を抽象化
|
|
722
|
+
2. **Resource でリソース管理**: 安全なリソースの取得と解放
|
|
723
|
+
3. **Ref でキャッシュ**: スレッドセーフな状態管理
|
|
724
|
+
4. **Result でエラー処理**: 明示的なエラーハンドリング
|
|
725
|
+
5. **SearchReport**: テスト可能性と可観測性の向上
|
|
726
|
+
6. **スタブ**: 外部依存を差し替えてテスト
|
|
727
|
+
7. **プロパティベーステスト**: ランダム入力で不変条件を検証
|
|
728
|
+
|
|
729
|
+
### 学習の総括
|
|
730
|
+
|
|
731
|
+
```plantuml
|
|
732
|
+
@startuml
|
|
733
|
+
!theme plain
|
|
734
|
+
left to right direction
|
|
735
|
+
|
|
736
|
+
rectangle "F# FP の学習パス" {
|
|
737
|
+
card "Part I\n基礎" as p1
|
|
738
|
+
card "Part II\n関数型スタイル" as p2
|
|
739
|
+
card "Part III\nエラーハンドリング" as p3
|
|
740
|
+
card "Part IV\n非同期/ストリーム" as p4
|
|
741
|
+
card "Part V\n並行処理" as p5
|
|
742
|
+
card "Part VI\n実践" as p6
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
p1 --> p2
|
|
746
|
+
p2 --> p3
|
|
747
|
+
p3 --> p4
|
|
748
|
+
p4 --> p5
|
|
749
|
+
p5 --> p6
|
|
750
|
+
|
|
751
|
+
@enduml
|
|
752
|
+
```
|
|
753
|
+
|
|
754
|
+
---
|
|
755
|
+
|
|
756
|
+
## 演習問題
|
|
757
|
+
|
|
758
|
+
### 問題 1: DataAccess の拡張
|
|
759
|
+
|
|
760
|
+
以下の要件で `IDataAccess` を拡張してください:
|
|
761
|
+
- 新しいメソッド `FindHotelsNearLocation` を追加
|
|
762
|
+
- 戻り値は `Async<Result<Hotel list, string>>`
|
|
763
|
+
|
|
764
|
+
<details>
|
|
765
|
+
<summary>解答</summary>
|
|
766
|
+
|
|
767
|
+
```fsharp
|
|
768
|
+
type Hotel = {
|
|
769
|
+
Name: string
|
|
770
|
+
Rating: float
|
|
771
|
+
Location: Location
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
type IDataAccessExtended =
|
|
775
|
+
inherit IDataAccess
|
|
776
|
+
abstract member FindHotelsNearLocation:
|
|
777
|
+
locationId: LocationId * limit: int
|
|
778
|
+
-> Async<Result<Hotel list, string>>
|
|
779
|
+
|
|
780
|
+
// テスト用スタブ
|
|
781
|
+
let testDataAccessExtended
|
|
782
|
+
(testAttractions: Attraction list)
|
|
783
|
+
(testArtists: MusicArtist list)
|
|
784
|
+
(testMovies: Movie list)
|
|
785
|
+
(testHotels: Hotel list)
|
|
786
|
+
: IDataAccessExtended =
|
|
787
|
+
{ new IDataAccessExtended with
|
|
788
|
+
member _.FindAttractions(name, _, limit) =
|
|
789
|
+
async { return testAttractions |> List.filter (fun a -> a.Name.Contains(name)) |> List.truncate limit }
|
|
790
|
+
member _.FindArtistsFromLocation(_, limit) =
|
|
791
|
+
async { return testArtists |> List.truncate limit }
|
|
792
|
+
member _.FindMoviesAboutLocation(_, limit) =
|
|
793
|
+
async { return testMovies |> List.truncate limit }
|
|
794
|
+
member _.FindHotelsNearLocation(_, limit) =
|
|
795
|
+
async { return Ok (testHotels |> List.truncate limit) }
|
|
796
|
+
}
|
|
797
|
+
```
|
|
798
|
+
|
|
799
|
+
</details>
|
|
800
|
+
|
|
801
|
+
### 問題 2: プロパティベーステスト
|
|
802
|
+
|
|
803
|
+
以下の関数に対するプロパティベーステストを書いてください:
|
|
804
|
+
|
|
805
|
+
```fsharp
|
|
806
|
+
let sortAttractionsByPopulation (attractions: Attraction list) : Attraction list =
|
|
807
|
+
attractions |> List.sortByDescending (fun a -> a.Location.Population)
|
|
808
|
+
```
|
|
809
|
+
|
|
810
|
+
<details>
|
|
811
|
+
<summary>解答</summary>
|
|
812
|
+
|
|
813
|
+
```fsharp
|
|
814
|
+
[<Fact>]
|
|
815
|
+
let ``sortAttractionsByPopulation は降順になる`` () =
|
|
816
|
+
for _ in 1..10 do
|
|
817
|
+
let attractions = Generators.attractions 5
|
|
818
|
+
let result = sortAttractionsByPopulation attractions
|
|
819
|
+
let populations = result |> List.map (fun a -> a.Location.Population)
|
|
820
|
+
let sorted = populations |> List.sortDescending
|
|
821
|
+
Assert.Equal<int list>(sorted, populations)
|
|
822
|
+
|
|
823
|
+
[<Fact>]
|
|
824
|
+
let ``sortAttractionsByPopulation は要素数を変えない`` () =
|
|
825
|
+
for _ in 1..10 do
|
|
826
|
+
let attractions = Generators.attractions 5
|
|
827
|
+
let result = sortAttractionsByPopulation attractions
|
|
828
|
+
Assert.Equal(List.length attractions, List.length result)
|
|
829
|
+
|
|
830
|
+
[<Fact>]
|
|
831
|
+
let ``sortAttractionsByPopulation は全要素を保持する`` () =
|
|
832
|
+
for _ in 1..10 do
|
|
833
|
+
let attractions = Generators.attractions 3
|
|
834
|
+
let result = sortAttractionsByPopulation attractions
|
|
835
|
+
let inputNames = attractions |> List.map (fun a -> a.Name) |> Set.ofList
|
|
836
|
+
let outputNames = result |> List.map (fun a -> a.Name) |> Set.ofList
|
|
837
|
+
Assert.Equal<Set<string>>(inputNames, outputNames)
|
|
838
|
+
```
|
|
839
|
+
|
|
840
|
+
</details>
|
|
841
|
+
|
|
842
|
+
### 問題 3: Resource の実装
|
|
843
|
+
|
|
844
|
+
ファイルを安全に読み取る `Resource` を実装してください。
|
|
845
|
+
|
|
846
|
+
<details>
|
|
847
|
+
<summary>解答</summary>
|
|
848
|
+
|
|
849
|
+
```fsharp
|
|
850
|
+
open System.IO
|
|
851
|
+
|
|
852
|
+
let fileResource (path: string) : Resource<StreamReader> =
|
|
853
|
+
Resource.make
|
|
854
|
+
(fun () -> async { return new StreamReader(path) })
|
|
855
|
+
(fun reader -> async { reader.Dispose() })
|
|
856
|
+
|
|
857
|
+
let readAllLines (path: string) : Async<string list> =
|
|
858
|
+
fileResource path
|
|
859
|
+
|> Resource.useAsync (fun reader ->
|
|
860
|
+
async {
|
|
861
|
+
let lines = ResizeArray<string>()
|
|
862
|
+
let mutable line = reader.ReadLine()
|
|
863
|
+
while line <> null do
|
|
864
|
+
lines.Add(line)
|
|
865
|
+
line <- reader.ReadLine()
|
|
866
|
+
return lines |> Seq.toList
|
|
867
|
+
})
|
|
868
|
+
|
|
869
|
+
// 使用例
|
|
870
|
+
let program : Async<unit> =
|
|
871
|
+
async {
|
|
872
|
+
let! lines = readAllLines "data.txt"
|
|
873
|
+
printfn "Read %d lines" (List.length lines)
|
|
874
|
+
}
|
|
875
|
+
```
|
|
876
|
+
|
|
877
|
+
</details>
|
|
878
|
+
|
|
879
|
+
---
|
|
880
|
+
|
|
881
|
+
## シリーズ全体の総括
|
|
882
|
+
|
|
883
|
+
本シリーズでは、「Grokking Functional Programming」の内容に沿って、関数型プログラミングの基礎から実践的なアプリケーション構築までを F# で学びました。
|
|
884
|
+
|
|
885
|
+
### 学んだ主な概念
|
|
886
|
+
|
|
887
|
+
| Part | 章 | 主な概念 |
|
|
888
|
+
|------|-----|----------|
|
|
889
|
+
| I | 1-2 | 純粋関数、参照透過性 |
|
|
890
|
+
| II | 3-5 | イミュータブルデータ、高階関数、flatMap |
|
|
891
|
+
| III | 6-7 | Option、Result、判別共用体 |
|
|
892
|
+
| IV | 8-9 | Async、Seq(ストリーム) |
|
|
893
|
+
| V | 10 | 並行処理、Ref、MailboxProcessor |
|
|
894
|
+
| VI | 11-12 | 実践アプリケーション、テスト |
|
|
895
|
+
|
|
896
|
+
### 関数型プログラミングの利点
|
|
897
|
+
|
|
898
|
+
1. **予測可能性**: 純粋関数は同じ入力に対して常に同じ出力
|
|
899
|
+
2. **テスト容易性**: 副作用がないためテストが簡単
|
|
900
|
+
3. **合成可能性**: 小さな関数を組み合わせて複雑な処理を構築
|
|
901
|
+
4. **並行安全性**: イミュータブルデータは競合状態を防ぐ
|
|
902
|
+
5. **型安全性**: Option、Result で null や例外を型で表現
|
|
903
|
+
|
|
904
|
+
### F# の特徴
|
|
905
|
+
|
|
906
|
+
F# は .NET プラットフォーム上で動作する関数型優先言語で、以下の特徴があります:
|
|
907
|
+
|
|
908
|
+
- **型推論**: 明示的な型注釈なしでも型安全
|
|
909
|
+
- **パイプライン演算子**: `|>` でデータの流れを直感的に表現
|
|
910
|
+
- **パターンマッチング**: 強力なパターンマッチングで分岐処理
|
|
911
|
+
- **計算式**: `async { }` などで非同期処理を宣言的に記述
|
|
912
|
+
- **相互運用性**: C# や .NET ライブラリとシームレスに連携
|
|
913
|
+
|
|
914
|
+
### 次のステップ
|
|
915
|
+
|
|
916
|
+
- F# の高度な機能(型プロバイダー、アクティブパターン)を学ぶ
|
|
917
|
+
- Elmish/Bolero などの F# Web フレームワークを探索
|
|
918
|
+
- Fable で F# を JavaScript にコンパイル
|
|
919
|
+
- 実際のプロジェクトで FP を適用する
|
|
920
|
+
|