为什么没有一个Phantom类来扩展Functor Contravariant



我在玩Data.Functor.Contravariantphantom方法吸引了我的眼球:

phantom :: (Functor f, Contravariant f) => f a -> f b
phantom x = () <$ x $< ()

或者,更具体地说,它的注释:

如果f既是Functor又是Contravariant,那么当你考虑到这些类中每一个的定律时,它实际上无法以任何有意义的方式使用它的参数。这种方法非常有用。在这两种情况都存在且合法的情况下,我们有以下法律:fmap f ≡ phantomcontramap f ≡ phantom

既然是fmap f ≡ contramap f ≡ phantom,为什么我们需要ContravariantFunctor实例?用另一种方式做这件事不是更方便吗:为一个类Phantom创建一个实例,它引入了phantom方法

,然后自动派生FunctorContravariant的实例?
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

在实现ContravariantFunctor的实例时,我们将免除程序员重写两次phantom的必要性(以实现fmapcontramap,它们是const phantom,如注释中所述(。我们将允许编写一个实例,而不是两个!此外,对我来说,为所有4种方差情况都设置类似乎很好,也很习惯:FunctorContravariantInvariant(然而,有些人建议使用Profunctor接口而不是Invariant(和Phantom

此外,这不是一种更有效的方法吗?() <$ x $< ()需要两次遍历(就像我们可以遍历一个幻影函子一样…(,只要程序员可以更快地执行此转换。据我所知,当前的phantom方法不能被覆盖。

那么,库开发人员为什么不选择这种方式呢?目前的设计和我所说的设计有什么优点和缺点?

有许多类型是Functor的实例,但不是Phantom的实例,同样也是Contravariant的实例。对于此类类型,由于实例重叠,您提出的结构将是一个大问题。

instance Phantom f => Functor f

并不意味着";如果CCD_ 29是幻影,那么它也是Functor";。在类型类解析过程中,只搜索实例头,稍后会出现约束。这与开放世界的假设有关。因此,您正在为f声明一个Functor实例,这是一个完全不受约束的类型变量,它将与其他所有可能的实例声明重叠。

为了避免amalloy提到的重叠实例,您可以定义一个可以与DerivingVia:一起使用的新类型

{-# LANGUAGE DerivingVia #-}
import Data.Functor.Contravariant hiding (phantom)
class (Functor f, Contravariant f) => Phantom f where
phantom :: f a -> f b
newtype WrappedPhantom f a = WrappedPhantom (f a)
instance Phantom f => Phantom (WrappedPhantom f) where
phantom (WrappedPhantom x) = WrappedPhantom (phantom x)
instance Phantom f => Functor (WrappedPhantom f) where
fmap _ = phantom
instance Phantom f => Contravariant (WrappedPhantom f) where
contramap _ = phantom
-- example of usage:
data SomePhantom a = SomePhantom
deriving (Functor, Contravariant) via WrappedPhantom SomePhantom
instance Phantom SomePhantom where
phantom SomePhantom = SomePhantom

它不像自动拥有实例那样方便,但这仍然意味着您不必手动实现Functor和Contravariant实例。

你真正能做的最好的事情是这样的:

class (Functor f, Contravariant f) => Phantom f where
phantom :: f a -> f b
phantom x = () <$ x $< ()

问题是,人们可能不会对花时间实例化类感兴趣。

最新更新