Haskell list monad 和 return ()

标签 haskell

考虑以下代码:

foo :: [Int]
foo = do
  x <- [1..10]
  if x < 5 then pure () else [] -- Control.Monad.guard (x < 5)
  pure x

foo2 :: [Int]
foo2 = 
  [1..10] >>= \x ->
    if x < 5 then pure x else [] >>= \y ->
      pure y
foo , 我已经手动内联 Control.Monad.guard (x < 5) ,如评论中所述。
为什么foo编译,即使有 pure ()在代码中? [()] 如何通过类型检查?它是 do 语法的特例吗?如果是,是否在任何地方进行了描述?
foo2 ,我试图“脱糖”foo没有 do 语法。注意不能有pure () ,因为它没有通过类型检查。
如果这很重要,我正在使用 ghc-8.8.4。

最佳答案

您的手动脱糖有几个错误。仅使用 >>= 的一次尝试将会:

foo2 :: [Int]
foo2 =
  [1..10] >>= \x ->
    (if x < 5 then pure () else []) >>= \_ ->
      pure x
首先,括号很重要:您将绑定(bind)整个 if 的结果。表达式,不在其 else 内执行绑定(bind)分支。其次,不能只引入一个新变量y然后在你的 pure 中使用它结果。脱糖会在源代码中保留相同的表达式,只是稍微移动它们。所以,pure x必须脱糖到 pure x .
希望你能明白为什么会这样:() 的类型没关系,因为没有人看过它的值,结果是pure x , 无论如何都有正确的类型。
但实际上 GHC 并不会产生完全这样的代码:x >>= \_ -> y相当于x >> y , 这就是 do-block 中不将其结果绑定(bind)到变量的语句所使用的。所以你真的得到
foo2 :: [Int]
foo2 =
  [1..10] >>= \x ->
    (if x < 5 then pure () else []) >> pure x
如果您愿意,您可以使用 Functor 中稍微花哨的运算符来获得相同的结果。让我们取消内联 guard , 并使用 (<$) :: Functor f => a -> f b -> f a而不是一元操作。 x <$ yy >> pure x 相同:
foo2 :: [Int]
foo2 =
  [1..10] >>= \x ->
    x <$ guard (x < 5)

关于Haskell list monad 和 return (),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64348649/

相关文章:

javascript - 我应该学习 haskell 来理解 javascript 的函数式编程部分吗?

xml - Haskell解析低内存的大xml文件

parsing - 如何组合多个解析器?

haskell - 在 Haskell 中读取大型二进制文件的最快方法是什么?

haskell - Lens - traverseInit 发生了什么?

haskell - 为什么 Haskell/unpack 会弄乱我的字节?

haskell - 确保两个 (G) ADT 在 (GHC) Haskell 中具有相同的底层表示

haskell - 在 GHCi 中映射换行符

haskell - yesod 中带有自定义验证的文件上传字段

haskell - 我可以根据 a 的类型类专门化 Foldable 实例方法吗?