为新类型重用MArray实例

问题描述 投票:4回答:2

我有很多这样的新类型:

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’

如何进行这项工作?

如果不可能,获得这些实例的最痛苦的方法是什么?

haskell typeclass newtype deriving
2个回答
3
投票
来自the documentation

如果新类型是最后一个类参数,我们甚至可以派生多参数类的实例。

请注意,类参数的

order变得很重要,因为我们只能派生最后一个实例。如果上述StateMonad类改为定义为

class StateMonad m s | m -> s where ...
然后,我们将无法为上述Parser类型派生实例。我们假设多参数类通常具有一个“主要”参数,对于该参数,派生新实例最为有趣。

由于您所用的最后一个类参数不是Int / MyInt,而是IO / ST s,因此很遗憾,您对GeneralizedNewtypeDeriving感到不走运。


0
投票
[好吧,您有点受困于此,因为array软件包中的某些设计选择使它变得困难,但这是一种可以最大程度地减少样板的方法。

您可以引入类型族以将新类型映射到其基础表示形式:

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

然后介绍IOUArraySTUArray数组类型的新类型变体:

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

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

或者您可以对NTIArray新类型使用与上面相同的技术。

一些示例代码:

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

© www.soinside.com 2019 - 2024. All rights reserved.