コンピュテーション式
from F#
computation expression
Monad?do式?に似た記法
http://bleis-tift.hatenablog.com/entry/how-to-make-computation-expression
https://qiita.com/7shi/items/026c7daa5b0b24d02c0f
gpt-5.icon
do式に近い“構文拡張”で、モナド(やそれに類する構造)に対して逐次風の書き方を提供する仕組み。Haskell の Monad 型クラスに相当するのが、F# では「builder (f#))オブジェクトが持つメソッド群」です(静的ディスパッチ寄り)。
1. 直感:Haskell の do と同型の糖衣
Haskell:
code:hs
do
x <- mx
y <- my x
pure (x + y)
F#:
code:fsharp
builder {
let! x = mx
let! y = my x
return x + y
}
let! は Haskell の <-(bind)に相当
return は pure / return に相当
builder { ... } が do ブロック相当(ただし “どの抽象” かは builder 次第)
2. コア対応:Bind / Return が最重要
CE の「最小セット」は概ねこれ:
Bind : M<'a> * ('a -> M<'b>) -> M<'b>(Haskell の (>>=))
Return : 'a -> M<'a>(Haskell の return / pure)
F# の CE は、ブロック内の構文を builder のメソッド呼び出しに desugar します。
例:
code:fsharp
builder {
let! x = mx
return f x
}
は概ね(概念的に):
code:fsharp
builder.Bind(mx, fun x -> builder.Return(f x))
Haskell の
code:hs
mx >>= \x -> pure (f x)
と同じ形。
3. let と let! の違い(Haskell の let と <-)
let x = ... は通常の束縛(コンテキストに入らない)
let! x = mx は “効果(文脈)を開く” 束縛(bind)
Haskell と同様に、let は純粋計算、let! はモナディック計算。
4. モナド“だけ”ではない:CE は「インターフェースが開いている」
Haskell の do は Monad に固定されますが、F# の CE は builder が用意する追加メソッドで機能が増えます。
代表的な追加メソッド(builder が持っていると使える):
Zero:空計算(mzero 的・あるいは () 的)
Combine:逐次結合((*>) / >> 的)
Delay:遅延(ブロックを thunk 化する)
ReturnFrom:return!(mx をそのまま返す)
Using / TryFinally / TryWith:リソース管理や例外構文(bracket や catch 相当を糖衣で提供)
While / For:ループを CE 内で書ける(ただし意味は builder が定める)
Yield / YieldFrom:シーケンス生成っぽい DSL に使う
重要なのは、CE は「モナド一般」より広くて、モナド + 例外/リソース/ループ/生成…を“その builder の意味で”定義できる DSL 機構だという点です。Haskell だと MonadPlus/Alternative/MonadCatch/MonadMask 等の型クラスを組み合わせてやる領域を、F# では builder のメソッドセットでまとめて表現できます。
5. 具体例:option をモナドとして扱う CE
Haskell なら Maybe で:
code:hs
do
x <- mx
y <- my x
pure (x + y)
F# でも Option に対して builder を作れば同じ:
code:fsharp
type OptionBuilder() =
member _.Bind(m, f) = Option.bind f m
member _.Return(x) = Some x
member _.ReturnFrom(m) = m
member _.Zero() = None
let option = OptionBuilder()
option {
let! x = Some 1
let! y = Some 2
return x + y
}
// -> Some 3
None が出たら以降をスキップ、という Maybe と同型の挙動になります。
6. CE がよく使われる領域(Haskell 観点の対応)
async { ... }:非同期(Haskell の IO や Async モナド、do 記法に近い)
task { ... }(F# の task CE):.NET Task 用の do 記法
seq { yield ... }:リスト内包表記や ListT というより “生成 DSL” に近い(Yield/For/Combine が効く)
result { ... }(ライブラリでよくある):Either e 的な early-exit
7. Haskell との差分でハマりやすい点
1. 型クラスではなくメソッド探索
Haskell: Monad m => ...
F#: builder に Bind があるかどうか(静的に解決)
2. 同じ構文でも意味は builder 次第
for や while が書けても、それが “本当にループ” かは builder の For/While 実装次第
3. do より DSL 寄り
Haskell だと do はモナドの合成に特化しているが、F# の CE は “その領域専用の小言語” を作る感じに近い