我正在学习F#,但有一点令我感到困惑的是计算表达式(do-notation ??)语法和desugaring。
在haskell中,你有一个非常简单的Monad类型类和desugaring do-notation到bind和return的规则。添加关键字没有任何魔力;唯一必须匹配的是类型。
在F#中,有许多构建器,关键字和复杂性。
是否有一个很好的解释如何将一个概念映射到另一个概念?
我基本上想知道如何映射
do
x <- monadicComputation
foo x
someOtherMonadicComputation
let y = somePureComputation x
return $ bar y
到F#。
haskell中唯一的关键字是do,(&lt ;-)和let。
答案 0 :(得分:14)
您不能在F#中编写通用的monadic代码,而是必须通过命名与表达式关联的构建器来指定您正在使用的monad。您的示例代码如下所示:
let example = async {
let! a = someAsyncComputation
foo a
do! someOtherAsyncComputation
let y = somePureComputation a
return (bar y)
}
表示async
计算表达式类型。绑定monadic值时使用'bang'模式(do!,let!etc.),而常规关键字用于非monadic值。
let!
对应绑定(>>=)
,而let
对应let
表示法中的do
。 return
对应return
,而return!
用于产生现有的monadic值。 do!
类似于(>>)
执行其效果的monadic值,而do
用于非monadic效果,在Haskell中没有并行。
答案 1 :(得分:4)
如果你来自Haskell背景,那么你最近对我写的关于F#计算表达式的an academic article感兴趣。
它将计算表达式语法(非常灵活)链接到Haskell中使用的标准类型类。正如已经提到的,F#不容易让你在monad上编写泛型代码(可以完成,但它不是惯用的),但另一方面它允许你选择最合适的语法和你甚至可以为MonadPlus
或monad变换器获得良好的语法。
除了Lee提到的async
monad之外,这里是MonadPlus
的一个示例(使用序列表达式 - 列表monad - 作为示例):
let duplicate list = seq {
for n in list do
yield n
yield n ∗ 10 }
或解析器的计算表达式:
let rec zeroOrMore p = parse {
return! oneOrMore p
return [] }
答案 2 :(得分:3)
haskell符号只有一个特殊语法,即<-
映射到bind
函数,其他内容do只是普通函数应用程序,其结果是monad类型,例如: return
,putStr
等。
同样在F#中,您let!
代表bind
操作和return
关键字语法糖(不是像haskell中的普通函数调用,但此关键字映射到Return
函数你定义的)。现在,您的计算表达式可以支持许多其他关键字(如果不需要,您可以轻松省略它们),它们都记录在案here。这些额外的操作为您提供了使用F#关键字而不是返回monadic值的普通函数的语法糖。您可以看到在F#计算表达式中可以重载的所有关键字都具有monadic返回值。
所以基本上,您不必担心所有这些关键字,只需将它们视为正常的monad返回函数(具有您可以在文档中找到的特定类型签名),您可以使用其中的F#关键字调用它们计算表达式语法。