当你 can/cannot 有一个数据类型的 Functor 实例时是什么情况?

What are the situations when you can/cannot have a Functor instances for a datatype?

考虑一个类型为 * -> * 的类型,我正在尝试寻找规则并建立直觉,以确定何时可以和何时不能为该类型使用 Functor。

到目前为止,我看到的规则如下:

  1. 没有 Functor 有限制的容器类型的实例 关于包含的值。

示例:Set 不能有 Functor 实例,因为包含的值

需要 Ord
  1. 没有 Functor 逆变数据类型的实例。

示例:

newtype Contra a = Contra (a -> Int)

除此之外,还有其他情况吗?

除了您的规则:

  • 必须友善* -> *
  • 没有 Functor 容器类型的实例对包含的值有限制。
  • 没有 Functor 逆变数据类型的实例。

我要补充几点:

  • "not for contravariant types" 的自然扩展:没有 Functor 不变数据类型的实例。例如data Iso a b = Iso (a -> b) (b -> a)
  • GADT 通常不能有 Functor 个实例。例如,

    data Foo a where
        Foo :: Foo Int
    

    也许你会想以某种方式将其混入 "only covariant" 规则(我不清楚这甚至有什么差异),或者以某种方式将 "unrestricted container types" 规则(GADT 引入类型等式非常像约束)。

但是,请记住,这些规则仅适用于 Functor ,不适用于一般的函子。我希望你可以编写的任何愚蠢类型(适当的类型)都将成为与 Hask 密切相关的某个适当类别的函子。

Haskell 用户指南中有 -XDerivingFunctor 扩展。您可以找到完整的描述 here。它描述了仿函数推导可能失败的所有情况。这个描述有算法检查正确种类等的情况。但我相信这个算法约束列表是详尽无遗的。

例如,另一种情况,当 type 具有 kind * -> * 但不能是 Functor 的实例时:

A data type’s last type variable is used in an -XExistentialQuantification constraint, or is refined in a GADT

从范畴论的角度来看,Haskell的类型构造函数*->*定义了一个对象的映射在范畴Hask中(这是一个类别模终止问题,我将方便地忽略它)。函子是对象的映射,更重要的是,态射的映射。事实上,它主要是态射的映射——对象的映射在某种程度上是它的副作用。对象只是态射的端点。这种态射映射必须保持组合和同一性。

在Haskell中,态射是函数,函数的映射实现为fmap

在 Haskell 中我们从对象映射开始的事实有点倒退。这是可行的,因为语言的语法极大地限制了定义对象映射的可能性。这样的映射非常规则,并且经常配备函数的规范映射。例如,代数数据类型是使用产品和副产品构造的,它们本质上是函子的(因此 deriving Functor 自动的可能性)。此外,函数类型(分类指数)在第二个参数中是函子的(在第一个参数中是逆变的)。所以,只要我们使用双笛卡尔闭范畴的工具(乘积、余积、指数),就很容易构造出对象的函子映射。

必须为给定类别中的每个对象定义函子,因此受类型 类 约束的数据类型(例如,具有 Ord 约束的 Set)不是Functors 在 Hask 中,但它们可能是 Hask 子类别中的函子。 (可以在 Haskell 中定义子类别及其自己的函子。)