安全转换为Word8

3

请看以下代码片段:

Prelude> import Data.Word
Prelude> data Foo = Foo Word8 deriving Show
Prelude> Foo 4
Foo 4
Prelude> Foo 44
Foo 44
Prelude> Foo 444
Foo 188

我有些惊讶444会像不安全的C语言一样被隐式转换为188。我认为这看起来相当容易出错。在Haskell中,处理这种转换的惯用方法是什么?

更新

看起来这只是字面值的多态行为,现代编译器对此进行了警告。最重要的是,类型系统不允许这种隐式截断。如果运行时值已知,则Foo (444 :: Int)会生成类型不匹配的错误,因此如果值只在运行时知道,这是完全安全的。


为什么会不安全? - Arnon
@Arnon更新了问题。我认为它不仅适用于字面值,而且适用于任意值(例如从stdin读取的值)。 - Stas
2个回答

6
最近在GHC中添加了一个警告来提示这种情况。 我使用GHC 7.8.3看到如下内容:
Prelude Data.Word> Foo 444

<interactive>:7:5: Warning:
    Literal 444 is out of the Word8 range 0..255
Foo 188
Prelude Data.Word> 

编译时:

$ ghc so.hs
[1 of 1] Compiling Main             ( so.hs, so.o )

so.hs:5:19: Warning: Literal 444 is out of the Word8 range 0..255

因此,惯用的解决方案是使用最流行的编译器最新版本。

谢谢。我担心类型系统允许像C语言一样的整数隐式截断(Int到Word8)。看起来这只是文字和“Foo(444 :: Int)”生成类型不匹配的多态行为。所以,实际上这是相当安全的。 - Stas

2

我不知道是否符合惯用语,但你所遇到的问题基本上是当一个字面量超出范围时被截断。由于字面量在 Num 上是多态的,并且 Integral 也需要 Num,所以你有以下函数:

fromInteger :: Num a => Integer -> a
toInteger :: Integral a => a -> Integer

因此,在转换之前,您可以始终将它们作为Integer进行比较:

-- Don't export the constructor
data Foo = Foo Word8 deriving (Eq, Show)

foo :: Integral a => a -> Maybe Foo
foo x = if xI > mBW8I then Nothing else Just (Foo $ fromInteger xI)
    where
        xI = toInteger x
        mBW8 :: Word8
        mBW8 = maxBound
        mbW8I = toInteger mBW8

那么您可以使用foo作为一个智能构造函数:

> foo 4
Just (Foo 4)
> foo 44
Just (Foo 44)
> foo 444
Nothing

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接