当数字常数超出范围时,为什么GHC不抱怨

时间:2013-12-17 21:18:55

标签: haskell

GHC默默地忽略数值常量中的超出范围位。

这种行为导致我今天与一个相当奇怪的错误搏斗:

[0..256]::[Word8] -- evaluates to [0]!

我知道导致这个错误的原因(在rot256世界中256 == 0)....我感兴趣的是为什么GHC / Haskell的设计不会在编译时抱怨它。< / p>

(这种行为对于Int也是如此,例如,18446744073709551617::Int = 1)。

我已经习惯了Haskell捕捉琐碎的编译时间问题,当我不得不追踪它时,我感到很惊讶。

2 个答案:

答案 0 :(得分:10)

我怀疑诚实的回答是“因为没有人实现它”。但我认为这个答案还有另一层,那就是存在一些微妙的设计问题。

例如:我们应该如何知道256超出Word8的范围?好吧,我想一个答案可能是编译器可能会注意到Word8是{em>所有三个 IntegralOrd和{{1}的实例}。所以它可以生成像

这样的支票
Bounded

并在编译时评估此检查。问题是我们突然运行了可能用户编写的代码(例如(256 :: Integer) > fromIntegral (maxBound :: Word8) maxBoundfromIntegral,大概都是来自实例声明可以由编程人员在编译时编写。这可能有点危险 - 因为我们无法判断我们是否会得到答案!所以至少你会希望这个检查在默认情况下是关闭的,并且可能至少与模板Haskell一样难以启用。

另一方面,也可能只构建一些我们“信任”的实例 - 例如正如你所说,(>)Word8。我会发现有点令人失望,但也许这样的补丁不会被拒绝。

答案 1 :(得分:2)

这取决于单个Num实例的实现。如果你执行

> :type 1
1 :: Num a => a

因此,Haskell最初只会将某些内容转换为通用Num,然后指定类型Word8。如果你试试

> (maxBound :: Word8) + 1
0
> maxBound :: Word8
255

这就是所谓的溢出,并且在许多语言中都适用,特别是C. Haskell不会阻止您这样做,因为有合法的情况,您可能想要溢出。相反,程序员必须确保输入数据有效。此外,正如jozefg指出的那样,在编译时不可能知道每次转换是否有效。


如果您已经拥有CyclicEqBounded,则可以实施一个Enum课程,为您提供所需的行为:

class (Eq a, Bounded a, Enum a) => Cyclic a where
    next :: a -> a
    next a = if a == maxBound then minBound else succ a
    prev :: a -> a
    prev a = if a == minBound then maxBound else pred a

instance Cyclic Word8

> next 255 :: Word8
0
> prev 0 :: Word8
255

幸运的是,对于所有Integral类型,您已经拥有EnumEq,并且我所知道的唯一Integral没有BoundedInteger。只需为您要使用的每个添加instance Cyclic <Int Type>即可。