关于为 ADT 推断 fmap

On inferring fmap for ADTs

假设两个新类型是这样定义的

type MyProductType a = (FType1 a, FType2 a)

type MyCoproductType a = Either (FType1 a) (FType2 a)

...并且 FType1Ftype2 都是 Functor.

的实例

如果现在将 MyProductTypeMyCoproductType 声明为 Functor 的实例,编译器是否 需要 为它们各自的显式定义fmap的,或者它能从前面的定义中推断出这些定义吗?

此外,这个问题的答案是依赖于实现的,还是遵循 Haskell 规范?


作为背景,这个问题的动机是试图理解 something I'm reading 中的一句话。笔者首先定义

type Writer a = (a, String)

...后来写(我的重点)

...the Writer type constructor is functorial in a. We don't even have to implement fmap for it, because it's just a simple product type.

强调的文字是我试图理解的评论。我 认为 这意味着 Haskell 可以根据函子类型为 any ADT 推断 fmap,并且,在特别是,它可以推断 fmap 为 "simple product type",如 Writer,但现在我认为这种解释是不正确的(至少如果我正确阅读 Ørjan Johansen 的回答)。

至于作者那句话是什么意思,我现在真的是一头雾水。也许他的意思是,不值得费心重新定义 Writer,使其函子性可以明确,因为它就是这样一个 "simple ... type"。 (在这里抓住救命稻草。)

首先,您通常不能为 type 同义词定义新实例,尤其是您的情况需要的部分应用实例。我想你是想定义一个 newtypedata 来代替:

newtype MyProductType a = MP (FType1 a, FType2 a)
newtype MyCoproductType a = MC (Either (FType1 a) (FType2 a))

标准 Haskell 根本没有提到自动派生 Functor,这只有在 GHC 的 DeriveFunctor 扩展中才有可能。 (或者有时 GeneralizedNewtypeDeriving,但这不适用于您的示例,因为您没有使用 a 作为构造函数中的最后一个参数。)

那么让我们试试看:

{-# LANGUAGE DeriveFunctor #-}
data FType1 a = FType1 a deriving Functor
data FType2 a = FType2 a deriving Functor
newtype MyProductType a = MP (FType1 a, FType2 a) deriving Functor
newtype MyCoproductType a = MC (Either (FType1 a) (FType2 a)) deriving Functor

我们收到错误消息:

Test.hs:6:76:
    Can't make a derived instance of ‘Functor MyCoproductType’:
      Constructor ‘MC’ must use the type variable only as the last argument of a data type
    In the newtype declaration for ‘MyCoproductType’

原来GHC可以推导出三个,但不能推导出最后一个。我相信第三个之所以有效,是因为元组是特殊情况。但是 Either 不起作用,因为 GHC 不保留任何关于 Either 如何处理其 first 参数的特殊知识。它名义上是该参数中的 数学函子 ,但不是 Haskell Functor.

请注意,GHC 更聪明地使用变量作为已知类型的最后一个参数 Functors。以下工作正常:

newtype MyWrappedType a = MW (Either (FType1 Int) (FType2 (Maybe a))) deriving Functor

所以总结一下:这取决于,GHC 对此有一个扩展,但它并不总是足够聪明来做你想做的事。