Ответ 1
Если вы посмотрите как вычисляются выражения вычислений, вы увидите, что
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>
.