haskell - show returning wrong value when used with unsafeCoerced value -
i experimenting unsafecoerce
int8
, word8
, , found surprising behaviour (for me anyway).
word8
8 bit unsigned number ranges 0-255. int8
signed 8 bit number ranges -128..127.
since both 8 bit numbers, assumed coercing 1 safe, , return 8 bit values if signed/unsigned.
for example, unsafecoerce (-1 :: int8) :: word8
expect result in word8
value of 255 (since bit representation of -1 in signed int same 255 in unsigned int).
however, when perform coerce, word8
behaviour strange:
> ghci, version 7.4.1: http://www.haskell.org/ghc/ :? > import data.int > import data.word > import unsafe.coerce > class showtype typename :: -> string > instance showtype int8 typename _ = "int8" > instance showtype word8 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
i don't understand how show x
returning "-1"
here. if @ map show [minbound..maxbound :: word8]
, no possible value word8
results in "-1"
. also, how adding 0 number change behaviour, if type isn't changed? strangely, appears show
class affected - showtype
class returns correct value.
finally, code fromintegral (-1 :: int8) :: word8
works expected, , returns 255, , works correctly show
. is/can code reduced no-op compiler?
note question out of curiosity how types represented in ghc @ low level. i'm not using unsafecoerce in code.
like @kosmikus said, both int8
, int16
implemented using int#
, 32 bit-wide on 32-bit architectures (and word8
, word16
word#
under hood). this comment in ghc.prim explains in more detail.
so let's find out why implementation choice results in behaviour see:
> let x = unsafecoerce (-1 :: int8) :: word8 > show x "-1"
the show
instance word8
is defined as
instance show word8 showsprec p x = showsprec p (fromintegral x :: int)
and fromintegral
frominteger . tointeger
. definition of tointeger
word8
is
tointeger (w8# x#) = smallinteger (word2int# x#)
where smallinteger
(defined in integer-gmp) is
smallinteger :: int# -> integer smallinteger = s#
and word2int#
primop type word# -> int#
- analog of reinterpret_cast<int>
in c++. explains why see -1
in first example: value reinterpreted signed integer , printed out.
now, why adding 0
x
give 255
? looking @ num
instance word8
see this:
(w8# x#) + (w8# y#) = w8# (narrow8word# (x# `plusword#` y#))
so looks narrow8word#
primop culprit. let's check:
> import ghc.word > import ghc.prim > case x of (w8# w) -> (w8# (narrow8word# w)) 255
indeed is. explains why adding 0 not no-op - word8
addition clamps down value intended range.
Comments
Post a Comment