En realidad, hay dos formas diferentes de implementar los constructores de continuación en F #. Una de ellas es para representar cálculos retardada utilizando el tipo monádico (si es compatible con alguna forma de representar cálculos retardados, como Async<'T>
o el tipo unit -> option<'T>
como se muestra por kkm.
Sin embargo, también se puede utilizar la flexibilidad de F expresiones # computación y el uso . un tipo diferente como valor de retorno de Delay
Luego hay que modificar el funcionamiento Combine
en consecuencia y también poner en práctica Run
miembro, pero todo funciona bastante bien:
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()
el truco es que F # compilador utiliza Delay
cuando tener un cálculo que necesita ser retrasado - t hat is: 1) para envolver todo el cálculo, 2) cuando compila cálculos secuencialmente, p.usando if
dentro del cálculo y 3) para retrasar cuerpos de while
o for
.
En la definición anterior, el miembro Delay
devuelve unit -> M<'a>
en lugar de M<'a>
, pero eso es perfectamente bien porque Combine
y While
tomar unit -> M<'a>
como su segundo argumento. Por otra parte, mediante la adición de Run
que evalúa la función, el resultado de maybe { .. }
bloque (una función retardada) se evalúa, porque todo el bloque se pasa a 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 }
Esta es una manera de definir constructor cálculo para no tipos retrasados que probablemente sean más eficientes que el tipo de envoltura dentro de una función (como en la solución de kkm) y no requiere definir una versión especial retardada del tipo.
Tenga en cuenta que este problema no ocurre, p. Haskell, porque es un lenguaje perezoso, por lo que no necesita retrasar los cálculos explícitamente. Creo que la traducción F # es bastante elegante ya que permite tratar con ambos tipos que están retrasados (usando Delay
que devuelve M<'a>
) y tipos que representan solo un resultado inmediato (usando Delay
que devuelve una función & Run
).
¿Has probado 'let delay f = fun() -> f()'? – Daniel
¿Has echado un vistazo a la implementación de la mónada 'Maybe' en FSharpx https://github.com/fsharp/fsharpx/blob/master/src/FSharpx.Core/Monad.fs? – pad
Consulte http://stackoverflow.com/questions/4577050/what-is-the-role-of-while-loops-in-computation-expressions-in-f para obtener un enfoque. – kvb