Ищу «instance (Enum a, Bounded a) => IArray UArray a» - PullRequest
3 голосов
/ 20 января 2012

Я ищу способ получить Enum a => UArray a (что имеет смысл для меня, поскольку мы можем тривиально отобразить перечисления в Int и обратно toEnum и fromEnum)

Пока чтоЯ пытался украсть код UArray Int из Data.Array.Base и переправить несколько toEnum с и fromEnum с здесь и там:

{-# 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.

Я также нашел пост о создании UArray для новых типов , но, похоже, он не применяется, потому что полагаетсяна unsafeCoerce.Я попробовал это, но получилось забавно listArray (0, 54) $ cycle [Red, Yellow, Green], в котором все конструкторы были Blue.

Я не на том пути?

Обновление:

Теперь работает, вот исходный код:

Ответы [ 2 ]

3 голосов
/ 20 января 2012

Вы должны понимать, что 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, этот бокс и распаковка в дополнение к сложности, которую могут иметь fromEnum и toEnum (особенно для кортежей и т. Д.), Может привести к снижению производительности по сравнению с использованием простых Array s в штучной упаковке.

2 голосов
/ 20 января 2012

Предупреждение: Функция fromEnum . toEnum не всегда является биекцией, поэтому она не будет работать для всех типов перечислений.В частности, Double является экземпляром Enum, но toEnum просто усекает Double значения.

Причина этого в том, что Enum - это класс типов, который необходимо реализовать, если вы хотитеписать выражения, как [0, 0.1 .. 1].Но, вообще говоря, то, что вы делаете, просто не будет работать для некоторых типов.

...