如何解决为什么不是扩展 Functor Contravariant 的 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
(然而,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 举报,一经查实,本站将立刻删除。