在某些类上下文中使用时,Newtypes通常用于更改某些类型的行为。例如,当用作Data.Monoid.All
时,可以使用Bool
包装来更改Monoid
的行为。
我正在编写一个适用于各种不同类型的新类型包装器。包装器应该改变一个特定类实例的行为。它可能看起来像这样:
newtype Wrapper a = Wrapper a
instance Special a => Special (Wrapper a) where
-- ...
但是,添加此包装器通常会改变包装类型的可用性。例如,如果我以前能够使用函数mconcat :: Monoid a => [a] -> a
,我现在无法将其用于包装值列表。
我当然可以使用-XGeneralizedNewtypeDeriving
和newtype Wrapper a = Wrapper a deriving (Monoid)
。然而,这只解决了Monoid
而没有其他类的问题,而我将处理一个充满不同类的开放世界,并且独立的孤立广义newtype派生并不是真正的实用选择。理想情况下,我想写deriving hiding (Special)
(导出除Special
之外的所有类),但当然这不是有效的Haskell。
有没有办法做到这一点,或者我只是搞砸了,需要添加GHC功能请求?
答案 0 :(得分:3)
看,GeneralizedNewtypeDeriving
is unsafe。在这种情况下,这是一种不安全的做法
{-# 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
然后,您可以在需要类型类实例
时随时使用它intWrapperNum :: Dict (Num (Wrapper Int))
intWrapperNum = addDictWrapper Dict
two :: Wrapper Int
two = case intWrapperNum of
Dict -> 1 + 1
这种传递显式词典的系统非常通用,并且有一个非常好的(虽然是实验性的)库来支持它,称为Data.Constraint
答案 1 :(得分:0)
我担心在GHC中没有直接的方法可以做到这一点。但我认为您可以使用Template Haskell解决问题。