调用Combine时的F#计算表达式

sqd*_*sqd 6 monads f#

我试图实现这个直接的Maybemonad.因此,基本上整个表达式的评估结果Nothing是中间步骤之一Nothing.

type Maybe<'a> =
    | Just of 'a
    | Nothing
type MaybeBuilder () =
    member this.Combine ((first, second) : Maybe<'a> * Maybe<'b>) : Maybe<'b> =
        printfn "Combine called"
        match first with
        | Nothing -> Nothing
        | _ ->
            match second with
            | Nothing -> Nothing
            | _ as a -> a
     member this.Zero () = Just ()
     member this.Bind((m, f) : Maybe<'a> * ('a -> Maybe<'b>)) =
        printfn "Bind called"
        match m with
        | Nothing -> Nothing
        | Just a -> f a
let MaybeMonad = MaybeBuilder()
let foobar =
    MaybeMonad {
        let! foo = Just "foo"
        Just 1
        Nothing
    }
Run Code Online (Sandbox Code Playgroud)

我期待foobar被翻译成Just "foo" >>= fun foo -> Combine(Just 1, Nothing),但Combine不被称为.

Gus*_*Gus 7

这不是预期编写计算表达式的方式.每次你想要'产生结果'时你需要在表达式的左侧添加一些关键字(return,return!,yield或yield!),在你的例子中我会添加一个return!:

let foobar =
    MaybeMonad {
        let! foo = Just "foo"
        return! Just 1
        return! Nothing
    }
Run Code Online (Sandbox Code Playgroud)

但是,您需要将其定义添加到构建器:

member this.ReturnFrom (expr) = expr
Run Code Online (Sandbox Code Playgroud)

然后编译器会要求你添加一个Delay方法,在你的情况下我认为你正在寻找类似的东西:

member this.Delay(x) = x()
Run Code Online (Sandbox Code Playgroud)

几乎在那里,现在你有一个值限制,很可能因为Combine你定义的两个参数上没有使用相同的类型,你可以修复它或只在返回类型中添加一个类型注释:

let foobar : Maybe<int> =
    MaybeMonad {
        let! foo = Just "foo"
        return! Just 1
        return! Nothing
    }
Run Code Online (Sandbox Code Playgroud)

就是这样,现在你得到:

Bind called
Combine called
Run Code Online (Sandbox Code Playgroud)

印刷和:

val foobar : Maybe<int> = Nothing
Run Code Online (Sandbox Code Playgroud)

如果你想了解CE的所有细节,请查看这篇好文章:https://www.microsoft.com/en-us/research/publication/the-f-computation-expression-zoo/

  • 然后用这篇非常好的系列文章跟进那篇文章:https://fsharpforfunandprofit.com/series/computation-expressions.html (4认同)