9

在某些类上下文中使用时,新类型通常用于更改某些类型的行为。例如,可以使用Data.Monoid.All包装器来更改Bool用作Monoid.

我目前正在编写一个适用于各种不同类型的新型包装器。包装器应该改变一个特定类实例的行为。它可能看起来像这样:

newtype Wrapper a = Wrapper a

instance Special a => Special (Wrapper a) where
  -- ...

但是,添加此包装器通常会改变包装类型的可用性。例如,如果我以前能够使用 function mconcat :: Monoid a => [a] -> a,我现在不能将它用于包装值的列表。

我当然可以使用-XGeneralizedNewtypeDerivingand newtype Wrapper a = Wrapper a deriving (Monoid)。但是,这仅解决了Monoid其他类的问题,而我将处理一个充满不同类的开放世界,并且独立的孤儿广义新类型派生并不是一个实际的选择。理想情况下,我想写deriving hiding (Special)(派生除 之外的所有类Special),但这当然不是有效的 Haskell。

有什么方法可以做到这一点,还是我只是搞砸了,需要添加 GHC 功能请求?

4

2 回答 2

3

看,GeneralizedNewtypeDeriving不安全。在这种情况下,这是一种不安全的做法

{-# LANGUAGE GADTs, ConstraintKinds #-}
import Data.Monoid
import Unsafe.Coerce

data Dict c where
  Dict :: c => Dict c

newtype Wrapper a = Wrapper a

addDictWrapper :: Dict (f a) -> Dict (f (Wrapper a))
addDictWrapper = unsafeCoerce

然后,您可以在需要 typeclass 实例的任何时候使用它

intWrapperNum :: Dict (Num (Wrapper Int))
intWrapperNum = addDictWrapper Dict

two :: Wrapper Int
two = case intWrapperNum of
           Dict -> 1 + 1

这种传递显式字典的系统非常通用,并且有一个非常好的(尽管是实验性的)库来支持它,称为Data.Constraint

于 2012-10-01T03:32:13.137 回答
0

恐怕在 GHC 中没有直接的方法可以做到这一点。但我认为您可以使用Template Haskell解决您的问题。

于 2012-10-01T06:46:07.733 回答