计算表达式中的类型推断错误

Type inference error in computation expression

type Identity<'T> = Identity of 'T

type IdentityBuilder() =
    member __.Bind (Identity x) (k : 'a -> Identity<'b>) = k x
    member __.Return x = Identity x
let identity = new IdentityBuilder()

let three = Identity 3
let four = Identity 4
let twelve =
    identity.Bind three <| fun t ->
    identity.Bind four <| fun f ->
    identity.Return (t * f)
let twelve2 = identity {
    let! t = three
    let! f = four
    return t * f
}

twelve不引入任何问题,但是twelve2给出

FS0001: This expression was expected to have type 'Identity<'a>' but here has type ''b * 'c'

线上let! t = three.

我认为 twelvetwelve2 应该是等价的...我错了吗?

正如 Szer 在评论中指出的那样,您需要为 Computation Builder 方法使用元组参数。但是,如您的示例所示,使用柯里化版本进行流水线操作通常很方便。因此,我通常做的是创建一个包含柯里化形式的计算生成器所需的所有函数的模块,然后在生成器本身中使用它们。这样我就可以根据场景使用计算表达式语法或流水线语法。

在你的情况下,它看起来像这样:

type Identity<'T> = Identity of 'T

[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module Identity =
    let bind (f: 'T -> Identity<'U>) (Identity x) = f x

    let create x = Identity x

type IdentityBuilder() =
    member __.Bind (x, f) = Identity.bind f x
    member __.Return x = Identity.create x

let identity = new IdentityBuilder()

let three = Identity 3
let four = Identity 4

let twelve =
    three |> Identity.bind  (fun t ->
        four |> Identity.bind (fun f ->
            Identity.create (t * f)))

let twelve2 = identity {
    let! t = three
    let! f = four
    return t * f
}

另一种常见的做法是为bind函数定义一个运算符>>=,这样可以进一步简化语法:

let (>>=) f x = Identity.bind x f

let twelve3 = three >>= (fun t -> four >>= (fun f -> Identity.create (t * f)))