newtypeのMArrayインスタンスの再利用


8

私はこのようなダースをたくさん持っています:

newtype MyBool = MyBool Bool
newtype MyInt  = MyInt  Int

既存のインスタンスを再利用したい:

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

これらのインスタンスを実装し、すべての定型コードを取得することは、私が最後に望んでいることです。

私が達成しようとしていることに非常に近いものを見つけました:

{-# LANGUAGE GeneralizedNewtypeDeriving, StandaloneDeriving #-}

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

しかし、それは失敗します:

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

これを機能させるには?

それが不可能な場合、それらのインスタンスを取得するための最も苦痛の少ない方法は何ですか?


2
安全な強制変換を使用してインスタンスを生成しようとしましたが、残念ながらtype role IOUArray nominal nominal、array-of-intからarray-of-myintに強制変換できません。(なぜ私たちにはそのような役割があるのでしょうか。)
チー

2
どうやらこれは、一部はこの方法行われ、newtypes Storableがボックス化されていない表現に異なるインスタンスを使用できるようにしました(たとえば、Int整数よりも短いボックス化されていないボックスを格納するために使用newtype Age = Age Int)。
KA Buhr

回答:


3

ドキュメントから:

newtypeが最後のクラスパラメータであれば、マルチパラメータクラスのインスタンスを派生させることもできます。

最後のインスタンスしか導出できないため、クラスパラメータの順序が重要になることにも注意してください。StateMonad上記のクラスが代わりに次のように定義された場合

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

その場合、Parser上記のタイプのインスタンスを導出することはできませんでした。マルチパラメータクラスには、通常、新しいインスタンスの導出が最も興味深い「メイン」パラメータが1つあると仮定します。

あなたの場合の最後のクラスパラメーターはInt/ MyIntではなくIO/ ST sなのでGeneralizedNewtypeDeriving、残念ながらでうまくいきません。


1

さて、arrayパッケージ内のいくつかの設計の選択が困難になっているので、あなたはちょっとここで立ち往生していますが、ここではボイラープレートを最小限に抑えるのに役立つ可能性がある1つのアプローチがあります。

タイプファミリーを導入して、新しいタイプをその基礎となる表現にマッピングできます。

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

そして、IOUArrayおよびSTUArray配列型のnewtypeバリアントを導入します。

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

そして、使用THESEを適切に得るためにMArrayあなたの新しいタイプのインスタンスを:

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)

さて、あなたは使用することができるはずNTIOUArrayNTSTUArray、通常の代わりにIOUArraySTUArrayの両方を内蔵しており、あなたのnewtype要素タイプのために:

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

すべてのIArrayインスタンスは、via派生を使用して自動的に生成できます(要素タイプがIArray制約の最後の引数であるため機能します)。

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

または、NTIArraynewtypeで上記と同じ手法を使用できます。

いくつかのサンプルコード:

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

詳しく説明してもらえますOkay, you're kind of stuck here because some design choices in the array package have made it difficult.か?派生で何がうまくいかず、次のエラーが何を意味するのか理解できません。`'MArray IOUArray MyInt IO'の派生インスタンスを作成できません(狡猾なGeneralizedNewtypeDerivingを使用しても): 'MArray IOUArray MyInt IO'のスタンドアロン派生インスタンスでは、表現型を十分にeta-reduceできません`
Arkady Rost
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.