寻找«实例(Enum a,Bounded a)=> IArray UArray»

时间:2012-01-20 12:19:58

标签: haskell unboxing

我正在寻找一种获得Enum a => UArray a的方式(这对我来说很有意义,因为我们可以轻松地将枚举映射到Int并返回toEnumfromEnum

到目前为止,我试图从Data.Array.Base窃取UArray Int的代码,并在这里和那里走私一些toEnumfromEnum

{-# LANGUAGE MagicHash, UnboxedTuples #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}

module UArrays where

import           Data.Array.Base
import           Data.Array.ST
import           Data.Array.Unboxed

import           GHC.Base -- (Int(I#), Int#(..))
import           GHC.Prim -- (indexIntArray#, readIntArray#, writeIntArray#)
import           GHC.ST (ST(..), runST)

import           Unsafe.Coerce

instance (Enum a, Bounded a) => IArray UArray a where
    {-# INLINE bounds #-}
    bounds (UArray l u _ _) = (l, u)
    {-# INLINE numElements #-}
    numElements (UArray _ _ n _) = n
    {-# INLINE unsafeArray #-}
    unsafeArray lu ies = runST (unsafeArrayUArray lu ies minBound)
        {-# INLINE unsafeAt #-}
    unsafeAt (UArray _ _ _ arr#) (I# i#) =
        I# $ fromEnum (indexIntArray# arr# i#)
    {-# INLINE unsafeReplace #-}
    unsafeReplace arr ies = runST (unsafeReplaceUArray arr ies)
    {-# INLINE unsafeAccum #-}
    unsafeAccum f arr ies = runST (unsafeAccumUArray f arr ies)
    {-# INLINE unsafeAccumArray #-}
    unsafeAccumArray f initialValue lu ies =
      runST (unsafeAccumArrayUArray f initialValue lu ies)

-- data STUArray s i e = STUArray !i !i !Int (GHC.Prim.MutableByteArray# s)
instance (Enum a, Bounded a) => MArray (STUArray s) a (ST s) where
    {-# INLINE getBounds #-}
    getBounds (STUArray l u _ _) = return (l, u)
    {-# INLINE getNumElements #-}
    getNumElements (STUArray _ _ n _) = return n
    {-# INLINE unsafeNewArray_ #-}
    unsafeNewArray_ (l, u) = unsafeNewArraySTUArray_ (l, u) wORD_SCALE
    {-# INLINE newArray_ #-}
    newArray_ arrBounds = newArray arrBounds minBound
    {-# INLINE unsafeRead #-}
    -- unsafeRead :: GHC.Arr.Ix i => a i e -> Int -> m e
    unsafeRead (STUArray _ _ _ marr#) (I# i#) =
      ST $ \ s1# ->
      case readIntArray# marr# i# s1# of
        (# s2#, e# #) -> (# s2#, I# (toEnum e#) #)
    {-# INLINE unsafeWrite #-}
    -- unsafeWrite :: GHC.Arr.Ix i => a i e -> Int -> e -> m ()
    unsafeWrite (STUArray _ _ _ marr#) (I# i#) (I# e#) =
      ST $ \ s1# ->
      case writeIntArray# marr# (unsafeCoerce i#) (I# $ fromEnum e#) s1# of
        s2# -> (# s2#, () #)

但当然不会编译:

[2 of 4] Compiling UArrays          ( UArrays.hs, interpreted )

UArrays.hs:27:14:
    Couldn't match expected type `Int#' with actual type `Int'
    In the return type of a call of `fromEnum'
    In the second argument of `($)', namely
      `fromEnum (indexIntArray# arr# i#)'
    In the expression: I# $ fromEnum (indexIntArray# arr# i#)

UArrays.hs:52:45:
    Couldn't match expected type `Int' with actual type `Int#'
    In the first argument of `toEnum', namely `e#'
    In the first argument of `I#', namely `(toEnum e#)'
    In the expression: I# (toEnum e#)

UArrays.hs:57:57:
    Couldn't match expected type `Int#' with actual type `Int'
    In the return type of a call of `fromEnum'
    In the second argument of `($)', namely `fromEnum e#'
    In the third argument of `writeIntArray#', namely
      `(I# $ fromEnum e#)'
Failed, modules loaded: Utils.

unboxInt :: Int -> Int#中也没有神奇的GHC.*I#上的模式匹配不会产生Int而是产生Int#,但不知何故UArray Int存在且适用于Int# s。

我还发现了一篇关于making an UArray for newtypes的帖子,但似乎并不适用,因为它依赖于unsafeCoerce。我试过了,但它制作了一个有趣的listArray (0, 54) $ cycle [Red, Yellow, Green],其中所有构造函数都是Blue

我走错了路吗?

更新

现在可以使用,这是源代码:

2 个答案:

答案 0 :(得分:3)

您需要意识到Int是一个盒装整数,它是通过构造函数Int#从未装箱的整数I#构造的。所以:

-- These functions aren't practical; they just demonstrate the types.
unboxInt :: Int -> Int#
unboxInt (I# unboxed) = unboxed

boxInt :: Int# -> Int
boxInt = I#

由于fromEnum已经返回了一个盒装整数,因此您无需再次应用I#来重新包装它,因此在此代码段中,例如:

{-I# $-} fromEnum (indexIntArray# arr# i#)

...你可以简单地省略I#构造函数。同样,在使用toEnum时,您应该应用I#构造函数从未装箱的整数中获取一个盒装整数。

正如@leftaroundabout所提到的,除了fromEnumtoEnum可能具有的复杂性(特别是对于元组等)之外,这种装箱和拆箱可能会导致与使用简单的盒装{{{{{{ 1}} S上。

答案 1 :(得分:2)

警告:函数fromEnum . toEnum并不总是一个双射,因此这对所有枚举类型都不起作用。特别是,DoubleEnum个实例,但toEnum只会截断Double个值。

原因是因为Enum是您想要编写[0, 0.1 .. 1]等表达式时必须实现的类型类。但总的来说,你所做的事情对于某些类型而言根本不起作用。