为什么不是扩展FunctorContravariant的Phantom类?
我在玩弄Data.Functor.Contravariant. 该phantom方法引起了我的注意:
phantom :: (Functor f, Contravariant f) => f a -> f b
phantom x = () <$ x $< ()
或者,更具体地说,对它的注释:
如果
f两者都是Functor,Contravariant那么当您考虑每个类别的法律时,它实际上无法以任何有意义的能力使用它的论点。这种方法非常有用。当这两种情况存在,是合法的,我们有以下规律:fmap f ? phantom,contramap f ? phantom
既然fmap f ? contramap f ? phantom,为什么我们需要Contravariant和Functor实例?用另一种方式做这件事不是更方便:为一个类创建一个实例Phantom,它引入了phantom方法,然后自动为?Functor和派生实例Contravariant
class Phantom f where
phantom :: f a -> f b
instance Phantom f => Functor f where
fmap _f = phantom
instance Phantom f => Contravariant f where
contramap _f = phantom
我们将摆脱必然性的程序员改写这个phantom两次(实施fmap和contramap,这是const phantom实现实例时,作为注解说明)Contravariant和Functor。我们将允许编写一个实例而不是两个!此外,它似乎不错,地道到我对所有4案件方差类:Functor,Contravariant,Invariant(然而,有些人建议使用Profunctorinterface 而不是Invariant),和Phantom。
另外,这不是更有效的方法吗?() <$ x $< ()需要两次遍历(就像我们可以遍历一个幻影函子一样......),只要程序员可以更快地执行这个转换。据我了解,当前的phantom方法不能被覆盖。
那么,库开发者为什么不选择这种方式呢?目前的设计和我所说的设计的优缺点是什么?
回答
有许多类型是 Functor 的实例而不是 Phantom 的实例,同样是逆变的。对于此类类型,由于实例重叠,您提出的结构将是一个大问题。
instance Phantom f => Functor f
并不意味着“如果f是 Phantom 那么它也是一个 Functor”。在类型类解析期间只搜索实例头,约束稍后出现。这与开放世界假设有关。因此,您为 声明了一个 Functor 实例f,这是一个完全不受约束的类型变量,它将与所有其他可能的实例声明重叠。