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



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

这种行为导致我今天遇到了一个相当奇怪的错误:

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

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

(这种行为也适用于Int,例如18446744073709551617::Int = 1)。

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

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

例如:我们应该如何知道256超出了Word8的范围?好吧,我想一个答案可能是编译器可以注意到Word8IntegralOrdBounded的所有三个的实例。所以它可以生成类似的检查

(256 :: Integer) > fromIntegral (maxBound :: Word8)

并在编译时评估此检查。问题是,突然之间,我们在编译时运行了潜在的用户编写的代码(例如maxBoundfromIntegral(>)可能都来自程序员可以编写的实例声明)。这可能有点危险——因为我们不可能知道是否会得到答案!因此,至少您希望默认情况下关闭此检查,并且可能至少与TemplateHaskell一样难以打开

另一方面,也可以只构建我们"信任"的少数实例,例如Word8Int。我会觉得这有点令人失望,尽管也许这样的补丁不会被拒绝。

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

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

因此,Haskell最初只是将某些内容转换为泛型Num,然后指定一个类型Word8。如果你尝试

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

这就是所谓的溢出,在许多语言中都适用,特别是C.Haskell并不阻止您这样做,因为在某些合法的情况下,您可能希望发生溢出。相反,这取决于你,程序员,来确保你的输入数据是有效的。此外,正如jozefg所指出的,在编译时不可能知道每个转换是否有效。


如果您已经有了EqBoundedEnum:,那么您可以实现一个Cyclic类来提供您想要的行为

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,而我所知道的唯一没有BoundedIntegralInteger。这只是为您想要使用的每个添加instance Cyclic <Int Type>的问题。

最新更新