如何在使用具体类型的数据类型上编写 Functor?

How do you write a Functor on a data type that uses concrete types?

我正试图从 GO 的世界进入 Haskell,所以几乎可以肯定我遗漏了一些微不足道的东西,但这让我很困惑!

以下编译但没有任何用处。

data Thing a = Type1 Int | Type2 Int Int deriving Show

instance Functor Thing where
    fmap func (Type1 a) = Type1 a
    fmap func (Type2 a b) = Type2 a b

一旦我尝试实际使用“func”,它就无法编译。例如,像这样在两个模式规则的第一个中使用“func”..

data Thing a = Type1 Int | Type2 Int Int deriving Show

instance Functor Thing where
    fmap func (Type1 a) = Type1 (func a)
    fmap func (Type2 a b) = Type2 a b

出现以下错误,但如果我使用“Type1 a | Type2 a a”声明“Thing”,则编译正常。

x2.hs:4:34:
    Couldn't match expected type ‘Int’ with actual type ‘b’
      ‘b’ is a rigid type variable bound by
          the type signature for fmap :: (a -> b) -> Thing a -> Thing b
          at x2.hs:4:5
    Relevant bindings include
      func :: a -> b (bound at x2.hs:4:10)
      fmap :: (a -> b) -> Thing a -> Thing b (bound at x2.hs:4:5)
    In the first argument of ‘Type1’, namely ‘(func a)’
    In the expression: Type1 (func a)

x2.hs:4:39:
    Couldn't match expected type ‘a’ with actual type ‘Int’
      ‘a’ is a rigid type variable bound by
          the type signature for fmap :: (a -> b) -> Thing a -> Thing b
          at x2.hs:4:5
    Relevant bindings include
      func :: a -> b (bound at x2.hs:4:10)
      fmap :: (a -> b) -> Thing a -> Thing b (bound at x2.hs:4:5)
    In the first argument of ‘func’, namely ‘a’
    In the first argument of ‘Type1’, namely ‘(func a)’

任何人都可以解释为什么以及我可以做些什么来解决它吗?是否可以在数据定义中使用具体类型,然后在 Functor 中使用它,或者我们是否仅限于使用泛型类型变量(如果我在这里使用了错误的词,请原谅)?

Is it possible to use concrete types in a data definition and then use that in a Functor or are we limited to using generic type variables (excuse me if I'm using the wrong words here)?

你不受限制。但是,只有类型为(或提及)数据声明中最后一个通用变量的字段才能在您的 Functor 实例中进行修改。 (类型检查器允许对具有其他类型的字段进行一些修改;但所有这些都会导致违反仿函数法则。)因此,例如,它混合了通用字段和具体字段:

data Thing a = Type1 Int a | Type2 Int Int

instance Functor Thing where
    fmap f (Type1 n a) = Type1 n (f a)
    fmap f (Type2 m n) = Type2 m n

当然,没有什么能阻止您编写另一个函数,它没有命名 fmap,它可以随心所欲。所以,这也可以:

data Thing = Type1 Int | Type2 Int Int

notFmap :: (Int -> Int) -> Thing -> Thing
notFmap f (Type1 n) = Type1 (f n)
notFmap f (Type2 m n) = Type2 m n