Haskell程序员的计算表达式



我想学习F#,但有一件事让我感到困惑的是计算表达式(do-notation??)语法和脱糖。

在 haskell 中,你有一个非常简单的 Monad 类型类和规则,用于将 do-notation 脱糖为绑定和返回。添加关键字没有魔力;唯一必须匹配的是类型。

在 F# 中,有一堆生成器、关键字和复杂性。

对于如何将一个概念映射到另一个概念,是否有很好的解释?

基本上想知道我是如何映射的

do
  x <- monadicComputation
  foo x
  someOtherMonadicComputation
  let y = somePureComputation x
  return $ bar y

到 F#。

Haskell中唯一的关键字是do,(<-)和let。

不能在 F# 中编写泛型 monadic 代码,而是必须通过命名与表达式关联的生成器来指定正在使用的 monad。您的示例代码如下所示:

let example = async {
    let! a = someAsyncComputation
    foo a
    do! someOtherAsyncComputation
    let y = somePureComputation a
    return (bar y)
}

对于async计算表达式类型。"bang"模式(do!,let!等)用于绑定一元值,而常规关键字用于非一元值。

let!对应于绑定(>>=),而let对应于do表示法中的letreturn对应于return,而return!用于产生现有的一元值。 do!类似于(>>),它为其效果执行一元值,而do用于非一元效应,这在 Haskell 中没有相似之处。

如果你来自Haskell背景,那么你可能会对我最近写的一篇关于F#计算表达式的学术文章感兴趣。

它将计算表达式语法(非常灵活)链接到 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 [] }

haskell do 符号只有一个特殊的语法,即映射到bind函数的<-,里面的其他一切都只是普通的函数应用程序,其结果是 monad 类型,例如:returnputStr 等。

同样,在 F# 中,您必须let!来表示bind操作并return关键字语法糖(不像 haskell 那样的普通函数调用,但此关键字映射到您定义的Return函数)。现在,您的计算表达式可以支持许多其他关键字(如果不需要,您可以轻松省略它们),它们都记录在这里。这些附加操作为您提供了使用 F# 关键字而不是返回 monadic 值的普通函数的语法糖。可以看到,可以在 F# 计算表达式中重载的所有关键字都具有 monadic 返回值。

因此,基本上,您无需担心所有这些关键字,只需将它们视为普通的 monad 返回函数(具有可在文档中找到的特定类型签名),您可以在计算表达式语法中使用 F# 关键字调用这些函数。

最新更新