Если вы посмотрите на , как вычисляются вычислительные выражения , вы увидите, что
while foo() do
printfn "step"
yield bar()
переведено в нечто вроде
builder.While(fun () -> foo(),
builder.Delay(fun () ->
printfn "step"
builder.Yield(bar()))))
Этот перевод позволяеттело цикла while должно оцениваться несколько раз.Хотя ваши подписи типов являются точными для некоторых выражений вычислений (например, seq
или async
), обратите внимание, что вставка вызова в Delay
может привести к другой подписи.Например, вы можете определить построитель списка следующим образом:
type ListBuilder() =
member x.Delay f = f
member x.While(f, l) = if f() then l() @ (x.While(f, l)) else []
member x.Yield(i) = [i]
member x.Combine(l1,l2) = l1 @ l2()
member x.Zero() = []
member x.Run f = f()
let list = ListBuilder()
Теперь вы можете оценить выражение, например:
list {
let x = ref 0
while !x < 10 do
yield !x
x := !x + 1
}
, чтобы получить эквивалент [0 .. 9]
.
Здесь наш метод While
имеет подпись (unit -> bool) * (unit -> 'a list) -> 'a list
, а не (unit -> bool) * 'a list -> 'a list
.Обычно, когда операция Delay
имеет тип (unit -> M<'a>) -> D<M<'a>>
, сигнатура метода While
будет (unit -> bool) * D<M<'a>> -> M<'a>
.