【问题标题】:What are the situations when you can/cannot have a Functor instances for a datatype?什么情况下您可以/不能拥有数据类型的 Functor 实例?
【发布时间】:2017-04-25 08:51:04
【问题描述】:

考虑到 kind * -> * 的类型,我正在尝试寻找规则并建立直觉,以了解何时可以使用该类型的 Functor。

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

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

示例:您不能拥有 SetFunctor 实例,因为包含的值需要 Ord

  1. 没有用于逆变数据类型的 Functor 实例。

例子:

newtype Contra a = Contra (a -> Int)

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

【问题讨论】:

  • 不是重复的,而是相关的:stackoverflow.com/q/7220436/477476
  • 为什么不建设性地制定它:当你可以有一个 Functor 实例的情况是什么?考虑到这一点,IMO 总是更有用。
  • 我想你可以计算类型错误的类型,例如newtype Fix f = Fix { unFix :: f (Fix f) } 有一种Fix :: (* -> *) -> *,而Functor 需要一种类型为* -> * 的构造函数,即Functor :: (* -> *) -> Constraint。 (不过,Fix 是一般范畴意义上的函子,从 Haskell 内函子范畴到 Haskell 类型范畴。)
  • @leftaroundabout 我正在寻找一些规则以及它们背后的直觉。在某些时候,我认为您可以为任何容器提供函子,这显然是错误的。所以我觉得除了少数情况外,大多数容器都可以使用 Functor 实例。
  • @cactus 我已经看到了链接的问题,但我正在尝试识别其他规则并建立直觉

标签: haskell typeclass functor category-theory


【解决方案1】:

除了你的规则:

  • 必须是那种* -> *
  • 对于包含的值有限制的容器类型没有Functor 实例。
  • 没有用于逆变数据类型的 Functor 实例。

我会补充一些:

  • “不适用于逆变类型”的自然扩展:没有用于不变数据类型的Functor 实例。例如data Iso a b = Iso (a -> b) (b -> a)
  • GADT 通常不能有Functor 实例。例如,

    data Foo a where
        Foo :: Foo Int
    

    也许您想以某种方式将其归为“唯一协变”规则(我不清楚这甚至有什么差异),或者以某种方式“不受限制的容器类型”规则(GADT 引入了非常约束的类型等式- 喜欢)。

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

【讨论】:

  • 你能更详细地解释一下“这些规则仅适用于 Functor,而不适用于一般的 functor”是什么意思吗?谢谢!
  • @vidi 对此进行扩展可能是另一个完整的答案,编写它的级别很大程度上取决于您对仿函数的熟悉程度。但简而言之:您可以想象class Functor' f where fmap' :: Ord b => (a -> b) -> f a -> f bFunctor 具有基本相同的类法律,并且即使Functor 没有,它也会承认Set 的实例。对于大多数非Functors,可以为大多数非Functors 制作一个具有基本相同类法的适当修改的类。
  • @vidi 或者,换一种说法:Haskell 作为一种语言的限制已经很容易满足仅仅作为一个函子所施加的更宽松的规则。参数多态性已经阻止你编写仿函数定律旨在排除的大多数愚蠢的东西。
【解决方案2】:

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

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

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

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

【讨论】:

    【解决方案3】:

    Haskell 用户指南中有-XDerivingFunctor 扩展名。你可以找到完整的描述here。它描述了函子推导可能失败的所有情况。此描述包含算法检查适当种类等的情况。但我相信此算法约束列表是详尽的。

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

    数据类型的最后一个类型变量在 -XExistentialQuantification 约束中使用,或在 GADT 中进行细化

    【讨论】:

    • 根据文档,DerivingFunctor 不会为newtype Wrong a = Wrong (Either a Int) 派生仿函数实例;但这并不意味着Wrong不能成为Functor
    • @DanielWagner 是的,这是真的。 DerivingFunctor 机制更具限制性。我只是指可以查找所有案例的官方文档。但不应盲目阅读本文档,以免错过您在评论中提到的案例。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-25
    • 2020-09-17
    • 2015-05-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多