TL;DR

  • ステップを合成するとき、入力と出力の型が合わない問題によく出くわす。
  • 依存関係による余計な入力は、部分適用で取り除ける。依存関係の渡し方はDomain Modeling Made Functional 第9章 依存関係の注入を参照。
  • 部分適用で解決できないときは、関数アダプタを作るか、命令型の書き方にする。

内容

合成したいワークフロー

Domain Modeling Made Functional 第9章 最初のステップの実装とDomain Modeling Made Functional 第9章 残りのステップの実装で実装したステップを、1 つのワークフローに合成する。目指す形は次のとおりである。

let placeOrder: PlaceOrderWorkflow =
  fun unvalidatedOrder ->
    unvalidatedOrder
    |> validateOrder
    |> priceOrder
    |> acknowledgeOrder
    |> createEvents

しかし、このままでは合成できない。validateOrder には依存関係の入力が 2 つ余計にあるので、前後のステップと入力・出力が一致しない。priceOrder も同じである。「形」の異なる関数を合成することは、関数型プログラミングの主要な課題の 1 つである(Domain Modeling Made Functional 第8章 関数合成)。解決策にはモナドを使う方法と、部分適用を使う方法がある。

部分適用で依存関係を取り除く

関数を値として扱えるので(Domain Modeling Made Functional 第8章 ものとしての関数)、依存関係のパラメータだけを先に適用して、入力が 1 つだけの関数を作れる。F# ではシャドーイングができるので、同じ名前で定義し直してよい。Haskell でよく見るように、末尾に ' を付けた名前にしてもよい。

let validateOrder =
  validateOrder checkProductCodeExists checkAddressExists
 
// または
let validateOrder' =
  validateOrder checkProductCodeExists checkAddressExists

ワークフロー全体は次のようになる。

let placeOrder: PlaceOrderWorkflow =
  let validateOrder =
    validateOrder checkProductCodeExists checkAddressExists
  let priceOrder =
    priceOrder getProductPrice
  let acknowledgeOrder =
    acknowledgeOrder createOrderAcknowledgmentLetter sendOrderAcknowledgment
 
  fun unvalidatedOrder ->
    unvalidatedOrder
    |> validateOrder
    |> priceOrder
    |> acknowledgeOrder // ここでエラーになる
    |> createEvents

部分適用では解決できない問題

まだ問題が残る。acknowledgeOrder の出力はイベントであり、createEvents の入力と一致しない。この場合は、関数アダプタを書くか、命令型の書き方にすればよい。命令型の書き方では、各ステップの出力を明示的に変数に束縛する。

let placeOrder: PlaceOrderWorkflow =
  fun unvalidatedOrder ->
    let validatedOrder =
      unvalidatedOrder
      |> validateOrder checkProductCodeExists checkAddressExists
 
    let pricedOrder =
      validatedOrder
      |> priceOrder getProductPrice
 
    let acknowledgmentOption =
      pricedOrder
      |> acknowledgeOrder createOrderAcknowledgmentLetter sendOrderAcknowledgment
 
    let events =
      createEvents pricedOrder acknowledgmentOption
 
    events

キーワード