具有多种类型的函子声明?

Functor declaration with multiple types?

我有以下数据类型:

data Users id height weight = User id height weight

instance Functor Users where
fmap f (User id height weight) = User(f id height weight)

然而这不会编译?

当我使用带有单个参数的类型时它工作正常,例如:

data Users id = User id
instance Functor Users where
fmap f (User id) = User (f id)

为什么我的第一个示例不起作用?

每个类型和类型构造函数都有一个种类。像 Int 这样简单的东西有种类 *。您的单参数类型构造函数 Users 具有种类 * -> *;它需要一种类型,return 是一种新类型。 Users 的第一个示例具有种类 * -> * -> * -> *;它需要 三种 类型和 return 一种新类型。

Functor 仅适用于 * -> * 类型的构造函数。这允许您为第二个 Users 类型构造函数定义一个 Functor 实例,但不是第一个。

想想你的第一次尝试:数据构造函数 Users 接受三个参数,但你对 fmap 的定义试图只用一个参数调用它,即 f 的 return 值。只要您愿意将所有三个字段设为同一类型,就可以将 Users 设为仿函数:

data Users a = Users a a a

instance Functor Users where
    fmap f (Users a b c) = Users (f a) (f b) (f c)

理论上,您可以定义一个 class Trifunctor(有一个 Bifunctor class 可用):

class Trifunctor f where
    trimap :: (a1 -> b1) -> (a2 -> b2) -> (a3 -> b3) -> f a1 a2 a3 -> f b1 b2 b3

data Users a b c = Users a b c

instance Trifunctor Users where
    trimap f g h (Users a b c) = Users (f a) (g b) (h c)

但这有多大用处值得商榷。 Functor 对通用容器很有用,因为它们有广泛的用途。另一方面,Users 似乎非常具体。您通常不需要定义它的灵活性; data User = User Int Int Int 看起来它会工作得很好,并且没有必要在上面映射一个函数:你多久需要一个相同的函数来以相同的方式修改身高、体重和年龄?

Functorclass 的实例只能定义为一阶种类的类型,即种类 * -> *

如果您尝试按如下方式查询 GHCI

ghci> :kind Users
Users :: * -> * -> * -> *

你会明白你的自定义类型更友好,因此编译器错误。

如果您仍希望为其提供 Functor class 的实例,尽管这可能很有用,请执行以下操作

ghci> :{
    | -- define custom type having a too high kind
    | data User a b c = User a b c deriving Show
    |
    | -- provide functor instance by reducing its type kindness 
    | instance Functor (User a b) where
    |   fmap f (User x y z) = User x y (f z)
    | :}

诀窍是部分 应用User 类型构造函数,以便生成一种类型,其友好度降低到预期类型。然后,通过模式匹配整个 User 值构造函数来定义 fmap,并将函数 f 应用于最后一个输入值。

用法示例为:

ghci> u1 = User 1 168 58.9
ghci> fmap (+5) u1
User 1 168 63.9

这就是 Either、元组 (,) 和函数 (->) 类型(它们在 Haskell prelude 中也是高阶类型)能够提供的大致方式Functor class 的实例也是。