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
}
12
不会引入任何问题,但 twelve2
会带来
FS0001: This expression was expected to have type 'Identity<'a>' but here has type ''b * 'c'
上线让! t = 三
。
我认为12
和twelve2
应该是等价的......我错了吗?
最佳答案
正如 Szer 的评论中所述,您需要对计算构建器方法使用元组参数。但是,使用柯里化(Currying)版本进行管道传输通常很方便,如您的示例所示。因此,我通常做的是以柯里化(Currying)形式创建一个包含计算构建器所需的所有函数的模块,然后在构建器本身中使用它们。这样我就可以根据场景使用计算表达式语法或管道语法。
就您而言,看起来像这样:
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)))
关于f# - 计算表达式中的类型推断错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51530646/