我正在尝试使用unsafeCoerceInt8Word8,但发现了一些令人惊讶的行为(无论如何对我来说)。
Word8是8位无符号数字,范围为0-255。 Int8是一个带符号的8位数字,范围为-128..127。

由于它们都是8位数字,因此我认为将两者强制转换是安全的,并且只需返回8位值就好像它是带符号的/无符号的一样。

例如,我希望unsafeCoerce (-1 :: Int8) :: Word8的结果是Word8值为255(因为有符号int中的-1的位表示形式与无符号int中的255相同)。

但是,当我执行强制时,Word8的行为很奇怪:

> GHCi, version 7.4.1: http://www.haskell.org/ghc/  :? for help
> import Data.Int
> import Data.Word
> import Unsafe.Coerce
> class ShowType a where typeName :: a -> String
> instance ShowType Int8 where typeName _ = "Int8"
> instance ShowType Word8 where typeName _ = "Word8"

> let x = unsafeCoerce (-1 :: Int8) :: Word8
> show x
"-1"
> typeName x
"Word8"
> show (x + 0)
"255"
> :t x
x :: Word8
> :t (x + 0)
(x + 0) :: Word8

我不明白show x在这里如何返回"-1"。如果查看map show [minBound..maxBound :: Word8],则Word8可能没有"-1"的值。另外,即使类型不变,如何在数字上加0也会改变行为?奇怪的是,它似乎也只是受影响的Show类-我的ShowType类返回正确的值。

最后,代码fromIntegral (-1 :: Int8) :: Word8可以正常工作,并返回255,并且可以与show一起正常使用。编译器是否可以将此代码简化为无操作?

请注意,这个问题只是出于好奇,不足以在ghc中表示类型。我实际上没有在代码中使用unsafeCoerce。

最佳答案

就像@kosmikus所说的一样,Int8Int16都使用Int#实现,在32位体系结构上,它的宽度为32位(而Word8Word16在后台是Word#)。 GHC.Prim中的This comment对此进行了更详细的说明。

因此,让我们找出为什么这种实现选择会导致您看到的行为:

> let x = unsafeCoerce (-1 :: Int8) :: Word8
> show x
"-1"
Show is defined asWord8实例
instance Show Word8 where
    showsPrec p x = showsPrec p (fromIntegral x :: Int)

fromIntegral 只是fromInteger . toIntegertoIntegerWord8定义为
toInteger (W8# x#)            = smallInteger (word2Int# x#)
smallInteger(在integer-gmp中定义)在哪里
smallInteger :: Int# -> Integer
smallInteger i = S# i
word2Int#是类型为Word# -> Int#primop-C++中reinterpret_cast<int>的类似物。这就解释了为什么在第一个示例中看到-1的原因:该值只是重新解释为有符号整数并打印出来。

现在,为什么在0中添加x会为您提供255呢?查看NumWord8实例,我们看到以下内容:
(W8# x#) + (W8# y#)    = W8# (narrow8Word# (x# `plusWord#` y#))

因此,看来narrow8Word# primop是元凶。让我们检查:
> import GHC.Word
> import GHC.Prim
> case x of (W8# w) -> (W8# (narrow8Word# w))
255

的确是。这就解释了为什么加0并不是无操作-Word8加法实际上会将值限制在预期范围内。

10-06 07:05