I want to convert an enum into Int or vice verse, i.e. implement an bidirectional mapping between the tag of sum type and Int. I have tried fromEnum
but it seems not fast enough, and then I tried unsafeCoerce
but it doesn't works as expected:
import Data.Time.Clock
import Data.Int
import Unsafe.Coerce
import Control.Monad (replicateM_)
data Color = R | G | B
deriving (Enum)
main = do
printT 1 $ (unsafeCoerce R :: Int8)
printT 1000 $ (unsafeCoerce G :: Int8)
printT 1000000 $ (unsafeCoerce B :: Int8)
printT 1000000000 $ (unsafeCoerce R :: Int8)
printT 1 $ (fromEnum R)
printT 1000 $ (fromEnum G)
printT 1000000 $ (fromEnum B)
printT 1000000000 $ (fromEnum B)
---------- profile tools ------------
printT :: Show a => Int -> a -> IO ()
printT n x = print =<< timeIt n (pure x)
timeIt :: Int -> IO a -> IO a
timeIt n _ | n <= 0 = error "timeIt n | n <= 0"
timeIt n proc = do
t0 <- getCurrentTime
replicateM_ (n-1) proc
x <- proc
t1 <- getCurrentTime
putStrLn ("-- Time Used (repeat " ++ show n ++ " times): " ++ show (t1 `diffUTCTime` t0))
return x
So what is the fastest way to do this?
fromEnum
. At first sight you run benchmarks on thepure
itself, at first sightfromEnum R
, etc. are never evaluated. LikelyunsafeCoerce R
andfromEnum R
will produce (approximately) the same results, since we never really make the evaluation. - Willem Van OnsemunsafeCoerce
is 100% incorrect here and liable to crash your program at surprising times. GHC does not representInt8
and a 3-constructor sum type in anything resembling the same memory layout. - CarltoEnum
andfromEnum
are really, really fast. They are, in fact, the fastest way the GHC developers have found to "convert an enum intoInt
or vice [versa], i.e. implement an bidirectional mapping between the tag of sum type andInt
." In the event that you find a faster way, you should submit a merge request to GHC; the performance of this code has always been a pretty high priority. - dfeuer