Haskell 列出 monad 并返回 ()

Jan*_*ček 5 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
Run Code Online (Sandbox Code Playgroud)

在 中foo,我已手动内联Control.Monad.guard (x < 5),如评论中所述。

为什么foo编译,即使pure ()在代码中?如何[()]通过类型检查?它是 do 语法的特例吗?如果是,是否在任何地方进行了描述?

在 中foo2,我尝试在foo没有 do 语法的情况下“脱糖” 。请注意,不能有 any pure (),因为它没有通过类型检查。

如果这很重要,我正在使用 ghc-8.8.4。

ama*_*loy 6

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

foo2 :: [Int]
foo2 =
  [1..10] >>= \x ->
    (if x < 5 then pure () else []) >>= \_ ->
      pure x
Run Code Online (Sandbox Code Playgroud)

首先,括号很重要:您绑定的是整个if表达式的结果,而不是在其else分支内执行绑定。其次,您不能只引入一个新变量y,然后在pure结果中使用它。脱糖在您的源代码中保留了相同的表达式,只是稍微移动了它们。所以,pure x一定要脱糖pure x

希望你能明白为什么会这样:类型()无关紧要,因为没有人会查看它的值,而结果 ,pure x无论如何都具有正确的类型。

但实际上 GHC 并没有产生这样的代码:x >>= \_ -> y相当于x >> y,这就是 do-block 中的语句所用的,它不将其结果绑定到变量。所以你真的得到

foo2 :: [Int]
foo2 =
  [1..10] >>= \x ->
    (if x < 5 then pure () else []) >> pure x
Run Code Online (Sandbox Code Playgroud)

如果您愿意,您可以使用 Functor 中稍微高级的运算符来实现相同的结果。让我们取消内联guard,并使用(<$) :: Functor f => a -> f b -> f a而不是一元操作。x <$ y是一样的y >> pure x

foo2 :: [Int]
foo2 =
  [1..10] >>= \x ->
    x <$ guard (x < 5)
Run Code Online (Sandbox Code Playgroud)

  • 这样就清楚多了,谢谢!我仍然不明白的是 if 两个分支中的表达式必须具有相同的类型,但在这种情况下,我看到不同的类型。据我了解, () 和 [] 不是同一类型。但这也可能是一个误解。编辑:哦,等等...结果值将是 [()] 和 []。我想这现在已经很清楚了。 (2认同)