微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

为什么不是扩展 Functor Contravariant 的 Phantom 类?

如何解决为什么不是扩展 Functor Contravariant 的 Phantom 类?

我在玩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

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

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

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

解决方法

有很多类型是 Functor 的实例而不是 Phantom 的实例,同样是逆变的。对于这样的类型,你提出的结构会因为重叠实例而成为一个大问题。

instance Phantom f => Functor f

并不意味着“如果 f 是一个 Phantom 那么它也是一个 Functor”。在类型类解析期间只搜索实例头,约束稍后出现。这与 open world assumption 相关。因此,您正在为 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 $< ()

问题在于人们可能对花时间实例化类不感兴趣。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。