GHC默默地忽略数值常量中的超出范围位。
这种行为导致我今天与一个相当奇怪的错误搏斗:
[0..256]::[Word8] -- evaluates to [0]!
我知道导致这个错误的原因(在rot256世界中256 == 0)....我感兴趣的是为什么GHC / Haskell的设计不会在编译时抱怨它。< / p>
(这种行为对于Int也是如此,例如,18446744073709551617::Int = 1
)。
我已经习惯了Haskell捕捉琐碎的编译时间问题,当我不得不追踪它时,我感到很惊讶。
答案 0 :(得分:10)
我怀疑诚实的回答是“因为没有人实现它”。但我认为这个答案还有另一层,那就是存在一些微妙的设计问题。
例如:我们应该如何知道256
超出Word8
的范围?好吧,我想一个答案可能是编译器可能会注意到Word8
是{em>所有三个 Integral
,Ord
和{{1}的实例}。所以它可以生成像
Bounded
并在编译时评估此检查。问题是我们突然运行了可能用户编写的代码(例如(256 :: Integer) > fromIntegral (maxBound :: Word8)
,maxBound
和fromIntegral
,大概都是来自实例声明可以由编程人员在编译时编写。这可能有点危险 - 因为我们无法判断我们是否会得到答案!所以至少你会希望这个检查在默认情况下是关闭的,并且可能至少与模板Haskell一样难以启用。
另一方面,也可能只构建一些我们“信任”的实例 - 例如正如你所说,(>)
和Word8
。我会发现有点令人失望,但也许这样的补丁不会被拒绝。
答案 1 :(得分:2)
这取决于单个Num
实例的实现。如果你执行
> :type 1
1 :: Num a => a
因此,Haskell最初只会将某些内容转换为通用Num
,然后指定类型Word8
。如果你试试
> (maxBound :: Word8) + 1
0
> maxBound :: Word8
255
这就是所谓的溢出,并且在许多语言中都适用,特别是C. Haskell不会阻止您这样做,因为有合法的情况,您可能想要溢出。相反,程序员必须确保输入数据有效。此外,正如jozefg指出的那样,在编译时不可能知道每次转换是否有效。
如果您已经拥有Cyclic
,Eq
和Bounded
,则可以实施一个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
类型,您已经拥有Enum
和Eq
,并且我所知道的唯一Integral
没有Bounded
是Integer
。只需为您要使用的每个添加instance Cyclic <Int Type>
即可。