有多种类型的Functor声明?

时间:2016-09-29 19:55:39

标签: haskell functor

我有以下数据类型:

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)

为什么我的第一个例子没有工作?

2 个答案:

答案 0 :(得分:6)

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

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

考虑您的第一次尝试:数据构造函数Users接受三个参数,但您对fmap的定义尝试仅使用一个f,f的返回值来调用它。只要您愿意将所有三个字段设为相同类型,就可以使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)

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

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似乎可以正常工作,并且不需要为此映射函数:您需要以相同方式修改高度,重量和年龄的相同功能吗?

答案 1 :(得分:0)

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

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

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

您将了解您的自定义类型具有更高的种类,因此会产生编译器错误。

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

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类型构造函数,以便产生一种将善良程度降低到期望值的类型。然后,通过对整个fmap值构造函数进行模式匹配来定义User,并将函数f应用于最后一个输入值。

用法示例为:

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

这大致就是Either,元组(,)和函数(->)类型(它们也是Haskell序言中的高阶类型)如何提供{{1 }}类。