Listコンピュテーション式にyield breakもどきを作れないかあがいてみた
コンピュテーション式におけるreturnとyieldにも書かれている通り、カスタムオペレーターで yield break を実装することはできません。では、型の力をつかってどうにかできないか、とあがいてみました。
Builder の定義
前述の記事で掲載されている ListBuilder の定義を少し変更します。なお、下記コードでは変更のない部分を省略しています。
module Control = type YieldControl<'a> = Break | Return of 'a open System open Control // オープンする順序で Break の優先順位を決める open Basis.Core.ComputationExpr type ListBuilder internal () = ... member this.YieldFrom(x) = match x with | YieldControl.Break -> (this.Zero() |> fst, Break) | YieldControl.Return x -> this.Return(x) ...
すると、
open Control list { for x in [1; 2; 3; 4; 0; 5; 6] do if x = 0 then yield! Return -1 yield x return 10 } // equal [1; 2; 3; 4; -1] list { for x in [1; 2; 3; 4; 0; 5; 6] do if x = 0 then yield! Break yield x return 10 } // equal [1; 2; 3; 4]
と、ちょっと残念な形で yield break (ついでに yield return)もどきを記述できます。
Yield メソッドを使えない理由
既存の Yield は 'a -> 'a list * FlowControl
です。仮に YieldControl<_> -> 'a list * FlowControl
を定義したとしても、呼び出し側でどちらを呼べばいいかの型注釈をつける必要がでてきます。
YieldFrom は list を受け取るメソッドしか存在しないため、どのメソッドを呼び出せばよいか判別できます。なので型注釈も必要ありません。
結論
定義が複雑になるくらいなら、素直に return! []
を使ったほうがよさそうですね。