2
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:这个表达式应该有类型'Identity<'a>',但这里有类型''b *'c'

就行了let! t = three

我认为twelve并且twelve2应该是等价的……我弄错了吗?

4

1 回答 1

3

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

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

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)))
于 2018-07-26T12:14:32.147 回答