我有以下数据类型:
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)
为什么我的第一个例子没有工作?
答案 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 }}类。