【関数志向】F#の "let!" が何者かを説明する【コンピューテーション式】

# 結論、これだけ
F#言語のコンピューテーション式とは何か？答えは簡単です。例えば以下のコンピューテーション式

```fsharp:Before
builder {
  let! x = expression
  return some_expression_using_x
}
```

これは以下の糖衣構文です。

```fsharp:After
builder.Bind(expression, fun x -> builder.Return some_expression_using_x)
```

ただこれだけです。しかしこれだけでは意義が分からないと思うので、この糖衣構文がどう役立つのかを説明します。

# ロガーの例
例えば以下のように変数の内容をログに書き出したいとします。

```fsharp
let log p = printfn "expression is %A" p

let loggedWorkflow =
    let x = 42
    log x
    let y = x + 1
    log y
    //return
    y * 2
```

これを実行すると当然以下のような出力がされます。

```
expression is 42
expression is 43
```

これをもっとスマートに書きたいです。コンピューテーション式を活用すれば以下のように書くことが可能です。

```fsharp
let loggedWorkflow = logger {
  let! x = 42
  let! y = x + 1
  return y * 2
}
```

ログ出力したい部分を`logger {}`でくくり、その中で`let!`で束縛した内容がログに出力されるという仕組みです。とても読みやすいですね。こんな書き方ができるためには以下のように`logger`を定義すればよいです。

```fsharp
type LoggingBuilder() =
    let log p = printfn "expression is %A" p

    member this.Bind(x, f) =
        log x
        f x

    member this.Return(x) =
        x

let logger = new LoggingBuilder()
```

`Bind`の中でログ出力しています。この`logger`を定義したうえで前述のコンピューテーション式を実行すると最初のコードと同じようにログ出力がされます。

```
expression is 42
expression is 43
```

## 処理の流れを追う

なぜこれで所望の動作になるのか考えます。まずは簡単な場合としてログ出力が1か所のみの以下のコードを考えます。

```fsharp
let loggedWorkflow = logger {
  let! x = 42
  return x + 1
}
```

これを実行すると以下の出力になります。

```
expression is 42
```

コンピューテーション式はただの糖衣構文なので上記は以下のように展開されます。

```fsharp
let loggedWorkflow = logger.Bind(
    42, fun x -> logger.Return x + 1
)
```

上記展開されたコードと`logger`のコードから処理の流れを追ってみてください。`logger.Bind`で`42`のログが出力された後　`x + 1`の部分の計算が行われ、確かに所望の動作になることが分かります。また当然ですが、展開後のコードと展開前のコンピューテーション式どちらを実行しても同じ動作、出力になります。

もともとの2つの`let!`がある場合も全く同じように考えられます。糖衣構文を展開すると以下のようになります。

```fsharp
let loggedWorkflow = logger.Bind(
    42, fun x -> logger.Bind(
        x + 1, fun y -> logger.Return (y * 2)
    )
)
```

複数の`let!`があるので`Bind`の中で`Bind`が使われることになります。ただ糖衣構文の変換を2度適用しただけです。ちょっと複雑ですが処理の流れを追ってみると、確かに`let!`で束縛した値がログに出力されるという挙動になります。

# Optionの例
以下のように`Option`型を伴う式でどこかで`None`になったらそこで処理を止めて`None`を返すような処理を考えます。

```fsharp
let map = Map [("a", Map [("z", 1)])]

let optionWorkflow =
  let x = map.TryFind "a"
  match x with
  | None -> None
  | Some xx ->
    let y = xx.TryFind "b"
    match y with
    | None -> None
    | Some yy -> yy + 1
```

入れ子のMapから順にキー`"a"`、`"b"`とたどってその値に1を足したものを返す、ただしキーが見つからなければ`None`を返す、ということをしています。

これをコンピューテーション式を活用することで以下のように書けます。

```fsharp
let optionWorkflow = option {
  let! xx = map.TryFind "a"
  let! yy = xx.TryFind "b"
  return yy + 1
}
```

とても見やすくなりました。`option {}`でくくった中の`let!`で束縛した値が`None`なら後続の処理を実行せず即座に`None`を返すという仕組みです。こんな書き方ができるためには以下のように`option`を定義すればよいです。

```fsharp
type OptionBuilder() =

    member this.Bind(x: 'a option, f: 'a -> 'b option) =
        match x with
        | Some value -> f value
        | None -> None

    member this.Return(x) =
        Some x

let option = new OptionBuilder()
```

Bindの中で場合分けをして、`None`なら`None`を返し、そうでなければその値を使って`f`を実行します。この`option`を定義したうえで前述のコンピューテーション式を実行すると最初のコードと同じ効果が得られます。

## 処理の流れを追う
これもロガーの場合と全く同じです。この場合のコンピューテーション式は以下のように展開されます。

```fsharp
let optionWorkflow = option.Bind(
  map.TryFind "a", fun xx -> option.Bind(
    xx.TryFind "b", fun yy -> option.Return (yy + 1)
  )
)
```

処理の流れをひとつずつ追って、所望の動作になることを確かめてください。

# そもそもこの糖衣構文はなにを意味しているのか？
`let!`が糖衣構文であり、それを活用することでいろいろ便利なことができることはわかりましたが、そもそもこの糖衣構文はどこから生まれて何を意味しているのでしょう？

```fsharp
builder.Bind(expression, fun x -> builder.Return some_expression_using_x)
```

結論を言うと、これは「手続き型言語における変数（定数）定義を関数型的に表現したもの」です。変数定義とはF#でいう`let a = 1`の様な構文ですね。実はこの`let`のような変数定義は厳格な関数型の思想では許されないです。じゃあこの変数定義と同じことを関数型でどう行うのかという問いに対する答えが上の書き方になります。

と言っても意味不明だと思うので説明します。まず関数型言語ではすべてを入力と出力がある関数で考えます。手続き型言語における変数定義というものは、まず変数を定義し、その後その変数を定義以降のどこかで使います。この「定義以降のどこか」で変数を使っている部分は変数の値に依存する処理であり、言い換えれば変数の値を入力とする関数ととらえられます。

具体例を示すと`let`を使って変数`q`を作っている以下のコード

```fsharp
let divmod (x, y) =
    let q = x / y
    (q, x - q * y)
```

これは厳格な関数指向に沿った、変数を使わない以下のコードに変換できます。

```fsharp
let divmod (x, y) =
    (fun q -> (q, x - q * y)) (x / y)
    // または以下のように書いた方が分かりやすいかも
    // (x / y) |> (fun q -> (q, x - q * y))
```

変数を使っている部分は`fun q -> (q, x - q * y)`のように定義した変数を入力とする関数になっていることが分かります。

つまりこれは`let`という変数定義構文を、関数指向の考え方で以下の同値のコードに解釈しなおすことができるという事です。

```fsharp
let Bind (v, f) = f v

let divmod (x, y) =
    Bind (x / y, (fun q -> (q, x - q * y)))
```

これは`let!`の糖衣構文がやっているのと同じ変換です。

# 参考文献
以下の記事が無茶苦茶分かりやすかった

https://fsharpforfunandprofit.com/series/computation-expressions/