F# 使用bind编写跨世界异步函数

F# 使用bind编写跨世界异步函数,f#,monad-transformers,f#+,F#,Monad Transformers,F#+,我有一条运行良好的铁路管道样本: 打开FSharpPlus 让福纳= 如果n福纳 >>=funB//它能工作 但是当我想将funB转换为异步函数时,我得到了一个编译错误。从逻辑上讲,这不应该有所不同。相同的输出/输入。。。怎么了 该怎么做才能让它工作 打开FSharpPlus 让福纳= 如果n福纳 >>=funB//编译错误 相同的输出/输入。。。怎么了 不,它们没有相同的输出/输入 如果您查看(>>=)的类型,它类似于'Monad'Monad,因此您的第一个示例可以重写为: let fun

我有一条运行良好的铁路管道样本:

打开FSharpPlus
让福纳=
如果n<10,则正常n
否则错误“不小于10”
让funB n=
如果n<5,则Ok(n,n*2)
否则错误“不小于5”
设funC n=//int->Result
N
|>福纳
>>=funB//它能工作
但是当我想将
funB
转换为异步函数时,我得到了一个编译错误。从逻辑上讲,这不应该有所不同。相同的输出/输入。。。怎么了

该怎么做才能让它工作

打开FSharpPlus
让福纳=
如果n<10,则正常n
否则错误“不小于10”
设funB n=async{
如果n<5,则返回Ok(n,n*2)
否则返回错误“不小于5”}
设funC n=//int->Async
N
|>福纳
>>=funB//编译错误
相同的输出/输入。。。怎么了

不,它们没有相同的输出/输入

如果您查看
(>>=)
的类型,它类似于
'Monad'Monad
,因此您的第一个示例可以重写为:

let funC n = // int -> Result<(int * int), string>
    n
    |> funA
    |> Result.bind funB
因此,我们在这里要做的是将
funA
函数“提升”到
Async
,然后将其包装在
Result
中,它是
Result
的单元转换器,因此它有一个绑定操作,在我们的例子中,
Async
也负责绑定外部单元

然后我们只需将
funB
包装到
ResultT
中,并在函数的最后使用
Result.run从
ResultT
展开

有关F#中分层单子的更多示例

还有其他方法,一些库提供了一些“神奇的工作流”,它使用特殊重载将monad与组合monad(也称为分层monad)结合起来,因此您编写的代码更少,但对类型进行推理并不容易,因为重载不遵循任何替换规则,您必须查看源代码才能理解发生了什么


<>注释:这样的编码是一个很好的练习,但在现实生活中,也要考虑使用异常来避免代码过于复杂。

查看运算符的类型<代码> > = 。第二个参数的类型是什么?它与funB的类型匹配吗?谢谢Gustavo的精彩解释。现在一切都有意义了。我认为重要的是funA的输出应该与funB的输入相同,无论funB的输出如何。我误解了原则。再次感谢您的澄清。现在我明白了困惑的来源。是的,在进行正常的函数组合时,这句话是正确的,但在绑定monad时,必须检查monad是否相同。
open FSharpPlus.Data

let funC n = // int -> Result<(int * int), string>
    n
    |> (funA >> async.Return >> ResultT)
    >>= (funB >> ResultT)
    |> ResultT.run