Есть на самом деле два различных способа реализации продолжения строителей в F #. Одна из них представляет запаздывающее вычисление с использованием монадического типа (если он поддерживает некоторый способ представления запаздывающих вычислений, как Async<'T>
или unit -> option<'T>
типа, как показан на КОМ.
Однако, вы можете также использовать гибкость F выражений # вычислений и использование . другой тип в качестве возвращаемого значения Delay
Затем вам нужно изменить Combine
операции соответственно, а также осуществлять Run
член, но все это работает довольно хорошо:
type OptionBuilder() =
member x.Bind(v, f) = Option.bind f v
member x.Return(v) = Some v
member x.Zero() = Some()
member x.Combine(v, f:unit -> _) = Option.bind f v
member x.Delay(f : unit -> 'T) = f
member x.Run(f) = f()
member x.While(cond, f) =
if cond() then x.Bind(f(), fun _ -> x.While(cond, f))
else x.Zero()
let maybe = OptionBuilder()
хитрость заключается в том, что F # компилятор использует Delay
когда вы иметь расчет, который необходимо отложить - t hat: 1) для обертывания всего вычисления, 2) когда вы последовательно составляете вычисления, например.используя if
внутри вычислений и 3) задержать тела while
или for
.
В приведенном выше определении, то Delay
элемент возвращает unit -> M<'a>
вместо M<'a>
, но это прекрасно, потому что Combine
и While
принять unit -> M<'a>
в качестве второго аргумента. Кроме того, путем добавления Run
, которая оценивает функцию, результат maybe { .. }
блока (отсроченная функция) оценивается, поскольку весь блок передается Run
:
// As usual, the type of 'res' is 'Option<int>'
let res = maybe {
// The whole body is passed to `Delay` and then to `Run`
let! a = Some 3
let b = ref 0
while !b < 10 do
let! n = Some() // This body will be delayed & passed to While
incr b
if a = 3 then printfn "got 3"
else printfn "got something else"
// Code following `if` is delayed and passed to Combine
return a }
Это способ определить вычисления построитель для не- отложенные типы, которые наиболее вероятно более эффективны, чем тип упаковки внутри функции (как в решении kkm), и не требует определения специальной версии с задержкой.
Обратите внимание, что эта проблема не происходит, например. Haskell, потому что это ленивый язык, поэтому ему не нужно явно откладывать вычисления. Я считаю, что перевод F # довольно изящный, поскольку он позволяет обрабатывать оба типа, которые задерживаются (используя Delay
, который возвращает M<'a>
) и типы, которые представляют собой только немедленный результат (с использованием Delay
, который возвращает функцию & Run
).
Вы пробовали 'let delay f = fun() -> f()'? – Daniel
Вы взглянули на реализацию монады «Maybe» в FSharpx https://github.com/fsharp/fsharpx/blob/master/src/FSharpx.Core/Monad.fs? – pad
См. Http://stackoverflow.com/questions/4577050/what-is-the-role-of-while-loops-in-comput-expressions-in-f для одного подхода. – kvb