F# 计算表达式:何时调用 Combine?

6

我尝试实现了这个简单的Maybe Monad,如果其中一个中间步骤为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
    }

我原本期望 foobar 被翻译为 Just "foo" >>= fun foo -> Combine(Just 1, Nothing),但实际上并没有调用 Combine
1个回答

7

计算表达式的书写方式不应该是这样的。每次想要“产生结果”时,需要在表达式的左侧添加一些关键词(return、return!、yield或yield!)。在你的例子中,我会添加一个return!

let foobar =
    MaybeMonad {
        let! foo = Just "foo"
        return! Just 1
        return! Nothing
    }

但是您需要将其定义添加到构建器中:
member this.ReturnFrom (expr) = expr

如果编译器要求您添加Delay方法,则在您的情况下,我认为您正在寻找类似于以下内容的内容:

member this.Delay(x) = x()

离目标已经很接近了,现在您遇到了一个值限制错误,很可能是因为您定义的Combine没有在两个参数上使用相同的类型,您可以修复它或只是在返回类型中添加类型注释:

let foobar : Maybe<int> =
    MaybeMonad {
        let! foo = Just "foo"
        return! Just 1
        return! Nothing
    }

就是这样,现在你可以得到:

Bind called
Combine called

打印和:

val foobar : Maybe<int> = Nothing

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


6
然后跟进这篇文章,阅读这个非常好的系列文章:https://fsharpforfunandprofit.com/series/computation-expressions.html - rmunn

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接