具有多种类型的函子声明?
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
看起来它会工作得很好,并且没有必要在上面映射一个函数:你多久需要一个相同的函数来以相同的方式修改身高、体重和年龄?
Functor
class 的实例只能定义为一阶种类的类型,即种类 * -> *
如果您尝试按如下方式查询 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 的实例也是。
我有以下数据类型:
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
看起来它会工作得很好,并且没有必要在上面映射一个函数:你多久需要一个相同的函数来以相同的方式修改身高、体重和年龄?
Functor
class 的实例只能定义为一阶种类的类型,即种类 * -> *
如果您尝试按如下方式查询 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 的实例也是。