8 votes

Réutilisation des instances MArray pour un nouveau type de tableau

J'ai une douzaine de nouveaux types comme celui-ci :

newtype MyBool = MyBool Bool
newtype MyInt  = MyInt  Int

Je veux réutiliser les instances existantes :

instance MArray IOUArray Int IO         where ...
instance MArray (STUArray s) Int (ST s) where ...

Mettre en œuvre ces instances et avoir tout le code passe-partout est la dernière chose que je souhaite.

J'ai trouvé quelque chose qui ressemble beaucoup à ce que j'essaie de réaliser :

{-# LANGUAGE GeneralizedNewtypeDeriving, StandaloneDeriving #-}

deriving instance MArray IOUArray MyInt IO      
deriving instance MArray (STUArray s) MyInt (ST s)  

Cependant, il échoue avec :

Can't make a derived instance of ‘MArray IOUArray MyInt IO’
    (even with cunning GeneralizedNewtypeDeriving):
    cannot eta-reduce the representation type enough
In the stand-alone deriving instance for ‘MArray IOUArray MyInt IO’

Comment faire pour que cela fonctionne ?

Si ce n'est pas possible, quel est le moyen le moins pénible d'obtenir ces instances ?

3voto

Joseph Sible Points 7272

De la documentation :

Nous pouvons même dériver des instances de classes multi-paramètres, à condition que le nouveau type soit le dernier paramètre de la classe.

Notez également que le commander des paramètres de classe devient important, puisque nous ne pouvons dériver des instances que pour le dernier. Si le StateMonad ci-dessus ont été plutôt définis comme

class StateMonad m s | m -> s where ...

alors nous n'aurions pas été en mesure de dériver une instance pour la Parser type ci-dessus. Nous supposons que les classes multi-paramètres ont généralement un paramètre "principal" pour lequel la dérivation de nouvelles instances est la plus intéressante.

Puisque le dernier paramètre de classe dans votre cas n'est pas Int / MyInt mais plutôt IO / ST s vous n'avez pas de chance avec GeneralizedNewtypeDeriving malheureusement.

1voto

K. A. Buhr Points 14622

Ok, tu es un peu coincé ici parce que certains choix de design dans le array Les paquets ont rendu la tâche difficile, mais voici une approche qui peut aider à minimiser les textes passe-partout.

Vous pouvez introduire une famille de types pour faire correspondre vos nouveaux types à leur représentation sous-jacente :

type family UType e where
  UType MyBool = Bool
  UType MyInt = Int
  UType a = a    -- default for built-in types

puis introduire des variantes de type nouveau de la IOUArray y STUArray types de tableaux :

newtype NTSTUArray s i e = NTSTUArray (STUArray s i (UType e))
newtype NTIOUArray i e = NTIOUArray (IOUArray i (UType e))

et utiliser CEUX-CI pour obtenir des MArray pour vos nouveaux types :

instance (MArray (STUArray s) (UType e) (ST s), Coercible e (UType e))
       => MArray (NTSTUArray s) e (ST s) where
  getBounds (NTSTUArray arr) = getBounds arr
  getNumElements (NTSTUArray arr) = getNumElements arr
  newArray (a,b) e = NTSTUArray <$> newArray (a,b) (coerce e)
  newArray_ (a,b) = NTSTUArray <$> newArray_ (a,b)
  unsafeNewArray_ (a,b) = NTSTUArray <$> unsafeNewArray_ (a,b)
  unsafeRead (NTSTUArray arr) i = coerce <$> unsafeRead arr i
  unsafeWrite (NTSTUArray arr) i e = unsafeWrite arr i (coerce e)

instance (MArray IOUArray (UType e) IO, Coercible e (UType e))
       => MArray NTIOUArray e IO where
  getBounds (NTIOUArray arr) = getBounds arr
  getNumElements (NTIOUArray arr) = getNumElements arr
  newArray (a,b) e = NTIOUArray <$> newArray (a,b) (coerce e)
  newArray_ (a,b) = NTIOUArray <$> newArray_ (a,b)
  unsafeNewArray_ (a,b) = NTIOUArray <$> unsafeNewArray_ (a,b)
  unsafeRead (NTIOUArray arr) i = coerce <$> unsafeRead arr i
  unsafeWrite (NTIOUArray arr) i e = unsafeWrite arr i (coerce e)

Maintenant, vous devriez être en mesure d'utiliser NTIOUArray y NTSTUArray à la place de l'habituel IOUArray y STUArray pour les types d'éléments intégrés et ceux de votre nouveau type :

main = do
  x <- newArray (1,10) (MyInt 0) :: IO (NTIOUArray Int MyInt)
  y <- newArray (1,10) 0         :: IO (NTIOUArray Int Int)
  readArray x 5 >>= writeArray y 8 . coerce

Tout IArray peuvent être générées automatiquement en utilisant via (qui fonctionne parce que le type d'élément est le dernier argument de la fonction IArray contrainte) :

deriving via MyBool instance IArray UArray MyBool
deriving via MyInt instance IArray UArray MyInt

ou vous pouvez utiliser la même technique que ci-dessus avec un fichier NTIArray nouveau type.

Quelques exemples de code :

{-# LANGUAGE DerivingVia, FlexibleContexts, FlexibleInstances, GeneralizedNewtypeDeriving,
    MultiParamTypeClasses, StandaloneDeriving, TypeFamilies, UndecidableInstances #-}

import Data.Coerce (coerce, Coercible)
import Data.Array.Base
import Data.Array.IO
import Control.Monad.ST (ST)

newtype MyBool = MyBool Bool deriving (Show)
newtype MyInt = MyInt Int deriving (Show)

-- newtype arrays
type family UType e where
  UType MyBool = Bool
  UType MyInt = Int
  UType a = a
newtype NTSTUArray s i e = NTSTUArray (STUArray s i (UType e))
newtype NTIOUArray i e = NTIOUArray (IOUArray i (UType e))

deriving via MyBool instance IArray UArray MyBool
deriving via MyInt instance IArray UArray MyInt

instance (MArray (STUArray s) (UType e) (ST s), Coercible e (UType e))
       => MArray (NTSTUArray s) e (ST s) where
  getBounds (NTSTUArray arr) = getBounds arr
  getNumElements (NTSTUArray arr) = getNumElements arr
  newArray (a,b) e = NTSTUArray <$> newArray (a,b) (coerce e)
  newArray_ (a,b) = NTSTUArray <$> newArray_ (a,b)
  unsafeNewArray_ (a,b) = NTSTUArray <$> unsafeNewArray_ (a,b)
  unsafeRead (NTSTUArray arr) i = coerce <$> unsafeRead arr i
  unsafeWrite (NTSTUArray arr) i e = unsafeWrite arr i (coerce e)

instance (MArray IOUArray (UType e) IO, Coercible e (UType e))
       => MArray NTIOUArray e IO where
  getBounds (NTIOUArray arr) = getBounds arr
  getNumElements (NTIOUArray arr) = getNumElements arr
  newArray (a,b) e = NTIOUArray <$> newArray (a,b) (coerce e)
  newArray_ (a,b) = NTIOUArray <$> newArray_ (a,b)
  unsafeNewArray_ (a,b) = NTIOUArray <$> unsafeNewArray_ (a,b)
  unsafeRead (NTIOUArray arr) i = coerce <$> unsafeRead arr i
  unsafeWrite (NTIOUArray arr) i e = unsafeWrite arr i (coerce e)

main = do
  x <- newArray (1,10) (MyInt 0) :: IO (NTIOUArray Int MyInt)
  y <- newArray (1,10) 0         :: IO (NTIOUArray Int Int)
  readArray x 5 >>= writeArray y 8 . coerce
  x' <- freeze x :: IO (UArray Int MyInt)
  y' <- freeze y :: IO (UArray Int Int)
  print $ (x' ! 5, y' ! 8)

foo :: ST s (NTSTUArray s Int MyInt)
foo = newArray (1,10) (MyInt 0)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X