Take a look at the following snippet:
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
I'm a bit surprised that 444 is implicitly converted to 188 like in unsafe C. It looks pretty error-prone to me. What's the idiomatic way to deal with such conversions safely in Haskell?
UPDATE
It seems this is just polymorphic behavior of literals and modern compiler warns about this. The most important is that type system doesn't allow such implicit truncation. Foo (444 :: Int) generates type mismatch, so this is completely safe if value is known in run-time only.
A warning was recently added to GHC to call out cases such as this. With GHC 7.8.3 I see:
Prelude Data.Word> Foo 444
<interactive>:7:5: Warning:
Literal 444 is out of the Word8 range 0..255
Foo 188
Prelude Data.Word>
And when compiling:
$ 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
So the idiomatic solution is to use the latest version of the most popular compiler.
I don't know about idiomatic, but the problem you're experiencing basically that a literal is being truncated when it's out of bounds. Since literals are polymorphic on Num, and Integrals require Num as well, you have the functions
fromInteger :: Num a => Integer -> a
toInteger :: Integral a => a -> Integer
So you can always compare them as Integer before conversion:
-- 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
Then you can use foo as a smart constructor:
> foo 4
Just (Foo 4)
> foo 44
Just (Foo 44)
> foo 444
Nothing
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With