为什么(,)映射第二个值的Functor实例?

时间:2018-06-01 12:47:33

标签: haskell tuples functor

在Haskell中,(,)的Functor实例显然是

instance Functor (,) where
    fmap f (a,b) = (a,f b)

这导致了不直观的事实:

> fmap (const 5) [1, 2]
[5,5]
> fmap (const 5) (1, 2)
(1,5)

现在,在我看来,使用这个定义会更好:

instance Functor (,) where
    fmap f (a,b) = (f a,f b)

它会像这样工作:

> fmap (const 5) (1, 2)
(5,5)

为什么不喜欢这个?

2 个答案:

答案 0 :(得分:4)

实例实际上不像你说的instance Functor (,)。那不是很好的方式:

Prelude> :k Functor
Functor :: (* -> *) -> Constraint
Prelude> :k (,)
(,) :: * -> * -> *

即,(,)采用两个类型参数(两个元组字段的类型)来构造元组类型,但Functor类实际上是仅用于的类型构造函数一个参数,如

Prelude> :k []
[] :: * -> *
Prelude> :k IO
IO :: * -> *

那为什么还有一个仿函数实例呢?坦率地说,我认为这是 not 定义的实例之一,正是因为元组不对称而令人困惑。但是,可以实际定义,并且只有一种方法可以实现,因此标准库的这种选择肯定不是不合理的。诀窍是:你可以将(,)构造函数部分应用于任何固定的左字段类型,然后为你提供单参数类型构造函数。例如

Prelude> :k (,) Int
(,) Int :: * -> *

所以你可以举个例子

instance Functor ((,) Int)

显然,它并不取决于左侧场具有哪种具体类型,所以你也可以做到这一点

instance Functor ((,) a)

在价值级Haskell中,本节将编写

instance Functor (a,)

与术语级别不同,部分应用程序为最左边的参数工作((,b)部分实际上是\a -> (a,b)的糖,但是没有类型-hvel lambda in Haskell),所以instance Functor ((,) a)是唯一可能的实例。

要获得您要求的行为,即应用于两个/所有字段的功能,您需要两个字段实际上具有相同的类型。即,您需要一个只有一个参数的类型构造函数,并且只对该 value 构造函数的字段使用该类型两次。具有该行为的标准类型是V2

答案 1 :(得分:2)

Product仿函数可以使用此行为:

Data.Functor.Product Data.Functor.Identity> fmap (+1) (Pair 5 6) :: Product Identity Identity Integer
Pair (Identity 6) (Identity 7)

正如您所看到的,Product(,)之间的主要区别在于Pair的两个元素都具有相同的叶类型(在本例中为Integer) 。 (相关地:请注意,在上面的类型中,Integer仅提及一次,而在(5,6)类型中,Integer被提及两次。)自{ {1}}可能包含完全不相关的类型,不能保证您可以以良好的方式将单个函数应用于这两个部分。