12
4

6 回答 6

13

我认为这是因为您需要指定要丢弃的值的类型。在 Haskell-98 中,()是显而易见的选择。只要您知道类型是(),您也可以制作该值()(假设评估进行到那一步),以防万一有人试图对其进行模式匹配或其他事情。我认为大多数程序员不喜欢在代码中引入额外的⊥,因为这只是一个额外的陷阱。我当然避免它。

代替(),可以创建一个无人居住的类型(当然除了 ⊥ )。

{-# LANGUAGE EmptyDataDecls #-}

data Void

mapM_ :: (Monad m) => (a -> m b) -> [a] -> m Void

现在甚至不可能进行模式匹配,因为没有Void构造函数。我怀疑不经常这样做的原因是因为它不兼容 Haskell-98,因为它需要 EmptyDataDecls 扩展。

编辑:你不能在 Void 上进行模式匹配,但seq会毁了你的一天。感谢@sacundim 指出这一点。

于 2012-06-18T05:31:41.670 回答
10

好吧,底部类型的字面意思是无限的计算,而单元类型就是它的本来面目——一种包含单个值的类型。显然,monadic 计算通常意味着要完成,所以让它们 return 根本没有意义undefined。而且,当然,这只是一种安全措施——就像约翰 L 说的,如果有人模式匹配一​​元结果怎么办?所以一元计算返回“最低”可能的(在 Haskell 98 中)类型 - 单位。

于 2012-06-18T06:30:53.237 回答
9

所以,也许我们可以有以下签名:

mapM_     :: (Monad m) => (a -> m b) -> [a] -> m z
foldM_    :: (Monad m) => (a -> b -> m a) -> a -> [b] -> m z
writeFile :: FilePath -> String -> IO z

我们将重新实现有问题的函数,以便任何尝试绑定zinm zIO z将变量绑定到undefined或任何其他底部。

我们有什么收获?现在人们可以编写程序来强制执行undefined这些计算的结果。这怎么是好事?这意味着人们现在可以编写无缘无故无法终止的程序,而这在以前是不可能编写的。

于 2012-06-18T17:14:00.027 回答
8
于 2012-06-19T00:32:49.173 回答
1

I would like to point out one severe downside to writing one particular form of returning ⊥: if you write types like this, you get bad programs:

mapM_ :: (Monad m) => (a -> m b) -> [a] -> m z

This is way too polymorphic. As an example, consider forever :: Monad m => m a -> m b. I encountered this gotcha a long time ago and I'm still bitter:

main :: IO ()
main = forever putStrLn "This is going to get printed a lot!"

The error is obvious and simple: missing parentheses.

  • It typechecks. This is exactly the sort of error that the type system is supposed to catch easily.
  • It silently infinite loops at runtime (without printing anything). It is a pain to debug.

Why? Well, because r -> is a monad. So m b matches virtually anything. For example:

forever :: m a -> m b
forever putStrLn :: String -> b
forever putStrLn "hello!" :: b -- eep!
forever putStrLn "hello" readFile id flip (Nothing,[17,0]) :: t -- no type error.

This sort of thing inclines me to the view that forever should be typed m a -> m Void.

于 2012-06-30T14:09:21.870 回答
1
于 2012-06-18T18:25:41.207 回答