为什么在Haskell中丢弃值是()而不是⊥?

L̲̳*_*̲̳̳ 12 haskell

如果有一个可以丢弃的值,Haskell中的结果会被()用来代替?吗?

示例(目前无法想到除IO操作之外的任何其他内容):

mapM_ :: (Monad m) => (a -> m b) -> [a] -> m ()
foldM_ :: (Monad m) => (a -> b -> m a) -> a -> [b] -> m ()
writeFile :: FilePath -> String -> IO ()
Run Code Online (Sandbox Code Playgroud)

在严格的评估下,这是完全合理的,但在Haskell中,它只会使域更大.

也许有"未使用的参数"函数d -> a是严格的d(哪里d是一个无约束的类型参数,并没有显示免费a)?例如:seq,const' x y = yseq x.

Joh*_*n L 13

我认为这是因为您需要指定要丢弃的值的类型.在Haskell-98中,()显而易见的选择.只要你知道类型是什么(),你也可以创造价值()(假设评估进展得那么远),以防万一有人试图对它进行模式匹配.我认为大多数程序员不喜欢在代码中引入额外的⊥,因为它只是一个额外的陷阱.我当然避免它.

而不是(),可以创建一个无人居住的类型(当然除了⊥).

{-# LANGUAGE EmptyDataDecls #-}

data Void

mapM_ :: (Monad m) => (a -> m b) -> [a] -> m Void
Run Code Online (Sandbox Code Playgroud)

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

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


Vla*_*eev 10

嗯,底部类型字面意思是一个无终止的计算,单位类型就是它 - 一个居住着单一值的类型.很明显,monadic计算通常意味着完成,因此让它们返回是没有意义的undefined.而且,当然,这只是一个安全措施 - 就像John L所说,如果有人模式匹配monadic结果怎么办?因此,monadic计算返回'最低'(在Haskell 98中)类型 - 单位.


Lui*_*las 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
Run Code Online (Sandbox Code Playgroud)

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

我们获得了什么?现在人们可以编写强制undefined这些计算结果的程序.这是一件好事吗?所有这一切都意味着人们现在可以编写无法正常终止的程序,这是以前无法编写的.

  • 这些失败的程序现在不是不可能写的.你总能介绍⊥.但我投了赞成票是因为你让我想起了我忽略的事情. (2认同)

Ben*_*Ben 8

你在类型和价值观之间感到困惑.

writeFile :: FilePath -> String -> IO (),()是单位类型.你得到的价值xx <- writeFile foo bar在做块(通常)的 (),这是该类型的唯一非居民底部().

?OTOH是一种价值.由于?是每种类型的成员,因此它也可用作类型的值().如果你在x不使用它的情况下丢弃上面的内容(我们通常甚至不将它提取到一个变量中),它很可能就是?你永远不会知道的.从那个意义上说,你已经拥有了你想要的东西 如果您正在编写一个函数,其结果您希望始终被忽略,那么您可以使用?.但由于?是每种类型的值,因此没有类型?,因此没有类型IO ?.

但实际上,它们代表了不同的概念.类型()是包含零信息的值的类型(这就是为什么只有一个值;如果有两个或更多值,则()值将包含至少与值相同的信息Bool).IO ()是生成没有信息的值的IO操作的类型,但可能是由于生成非信息性值而发生的影响.

?从某种意义上说,它是一种非价值.1 `div` 0给出?因为没有值可以用作满足整数除法的表达式的结果.抛出异常会产生?因为包含异常抛出的函数不会为您提供其类型的值.非终止,?因为表达式永远不会以值终止.?是一种处理所有这些非价值的方式,就好像它们是某种用途的价值一样.据我所知,它主要是有用的,因为Haskell的懒惰意味着?包含?(即[?])的数据结构是可区分的.

该值()与我们使用的情况不同?.writeFile foo bar没有"不可能的价值" return $ 1 `div` 0,它只是没有信息的价值(除了monadic结构中包含的信息).我从做到的事情中可以做出非常明智的事情; 他们只是不是很有趣,所以没有人做过.这与使用该值做任何事情必须给我另一个定义不明确的价值明显不同.()x <- writeFile foo barx <- return $ 1 `div` 0