做你要求的事
如果你看看我关于 hackage 的约束包,有
数据.约束.Forall
这可用于创建量化约束,并将 inst 与包中的其他约束组合器一起使用,并通过创建辅助约束将参数放在正确的位置,您可以直接对您所要求的内容进行编码。
我的博客上有反射机制的描述。
http://comonad.com/reader/2011/what-constraints-entail-part-1/ http://comonad.com/reader/2011/what-constraints-entail-part-1/
http://comonad.com/reader/2011/what-constraints-entail-part-2/ http://comonad.com/reader/2011/what-constraints-entail-part-2/
然而,这需要前沿的 GHC。
在许多情况下,您通常可以通过制作特定约束的等级 2 版本来模仿这一点。
class Monoid1 m where
mappend1 :: m a -> m a -> m a
mempty1 :: m a
但在您的情况下,您不仅需要 2 级约束,还需要约束含义。
使用该包中的机器,我们could make
class SuitableLowering t m where
lowerSuitability :: Suitable (t m) a :- Suitable m a
然后你可以使用
instance (PrintSuitable m, SuitableLowering t m) => PrintSuitable (t m) where
and use expr \\ lowerSuitability
手动将其纳入范围Suitable m a
在你知道的上下文中的实例Suitable (t m) a
.
但这是表达实例的一种非常危险的方式,因为它阻止您制作某种 (* -> *) -> * -> * PrintSuitable 的实例以任何其他方式如果您不小心,可能会干扰您的基本情况的定义!
做你需要做的事
The right做到这一点的方法是放弃定义涵盖所有情况的单个实例,而是定义一个printSuitableDefault
可用于任何合适的变压器。
假设丹尼尔的回复中提到的 RMonadTrans 的存在
class RMonadTrans t where
rlift :: Suitable m a => m a -> t m a
我们可以定义:
printSuitableDefault :: (RMonadTrans t, Suitable m a) => a -> t ()
printSuitableDefault = ...
instance PrintSuitable m => PrintSuitable (Foo m) where
printSuitable = printSuitableDefault
instance PrintSuitable m => PrintSuitable (Bar m) where
printSuitable = printsuitableDefault
您不可能有太多的 rmonad 转换器,这确保了如果您想以不同的方式进行打印,您可以拥有这种灵活性。
在前沿编译器下更好地完成您需要的事情
在 7.3.x(当前的 GHC HEAD)或更高版本中,您甚至可以使用新的默认声明来减轻这种痛苦。
class RMonad m => PrintSuitable m where
printSuitable :: a -> m ()
default printSuitable :: (RMonadTrans t, RMonad n, Suitable n a, m ~ t n) =>
a -> t n ()
printSuitable = <the default lifted definition>
那么每个变压器的实例可以看起来像:
instance PrintSuitable m => PrintSuitable (Foo m)
instance PrintSuitable m => PrintSuitable (Bar m)
你可以为一些受限的单子定义你的漂亮打印合适的基本情况,而不必担心重叠。