在下面,我尝试在元组上使用fmap
,但这不起作用,虽然它适用于列表和Just 4
:
Prelude> fmap (+3) (Just 4)
Just 7
Prelude> fmap (+3) [1,2,3,4]
[4,5,6,7]
Prelude> fmap (+3) (10,11,12,13,14)
<interactive>:38:1: error:
* Non type-variable argument
in the constraint: Functor ((,,,,) a b1 c d)
(Use FlexibleContexts to permit this)
* When checking the inferred type
it :: forall a b1 c d b2.
(Num d, Num c, Num b1, Num a, Num b2, Functor ((,,,,) a b1 c d)) =>
(a, b1, c, d, b2)
Prelude>
答案 0 :(得分:7)
为什么fmap对[5-]元组不起作用?
因为还没有人将5元组Functor
实例添加到 base 。如果你看一下the list of Functor
instances provided by base,你会发现Functor ((,) a)
,对的实例,但不是大型元组的实例,包括Functor ((,,,,) a b c d)
,这就是你需要的。
后续问题当然是:为什么没有人将5元组Functor
实例添加到 base 了?一个原因与必要性(或缺乏必然性)有关:在实践中,成对出现的次数远多于大型元组,并且随着元组变大,使用它们而不是非适合的非匿名类型变得越来越难以证明一个用例。既然如此,对较大元组的Functor
实例的需求并不是那么大。
虽然你没有提到你期望Functor ((,,,,) a b c d)
的行为,但值得注意的是,对fmap
只对第二个组件起作用,而较大元组的实例类似地仅处理最后一个组成部分。
GHCi> fmap not (False, True)
(False,False)
有两个原因:
组件的类型可以不同,因此没有办法,例如fmap not ("foobar", True)
可能会更改这两个组件。
在编写实例时无法翻转类型构造函数,因此例如对于作用于第一个组件的对,不能有Functor
个实例(除非您使用a newtype wrapper,但除此之外)。
虽然这种行为可能看起来令人惊讶,但如果您将类型为(a, b)
的货币对作为带有注释的b
值(标签,标签,额外的东西),则完全合理 - 但是您喜欢称之为附加的a
类型。在这种情况下,您宁愿将其视为可以独立修改的两对值,您可以使用Bifunctor
类:
GHCi> import Data.Bifunctor
GHCi> first reverse ("foobar", True)
("raboof",True)
GHCi> second not ("foobar", True)
("foobar",False)
GHCi> bimap reverse not ("foobar", True)
("raboof",False)
( base 不提供Trifunctor
,Tetrafunctor
等等,因为缺乏必要,正如开头所讨论的那样。)
在提供Functor
个实例时,以相同的方式考虑更大的元组是合理的。事实上,为了保持一致,这些实例可能存在。但是,有些人非常不喜欢对的实例,导致proposals to add the instances for other tuples停滞。
P.S。:或许值得一提的是,镜头库的(许多)用例之一是使用不是Functor
s作为仿函数的东西。这为我们提供了一种方便的方法来查看Functor
和(如果那是一个东西)Pentafunctor
实例将使用5元组:
GHCi> import Control.Lens
GHCi> over _5 (+3) (10,11,12,13,14)
(10,11,12,13,17)
GHCi> over _4 (+3) (10,11,12,13,14)
(10,11,12,16,14)
GHCi> over _3 (+3) (10,11,12,13,14)
(10,11,15,13,14)
GHCi> over _2 (+3) (10,11,12,13,14)
(10,14,12,13,14)
GHCi> over _1 (+3) (10,11,12,13,14)
(13,11,12,13,14)
甚至可以映射所有组件......
GHCi> over both (+3) (13,14)
(16,17)
GHCi> over each (+3) (10,11,12,13,14)
(13,14,15,16,17)
......但不出所料,他们要求所有组件具有相同的类型:
GHCi> over each (+3) (True,11,12,13,14)
<interactive>:9:12: error:
* No instance for (Num Bool) arising from a use of `+'
* In the second argument of `over', namely `(+ 3)'
In the expression: over each (+ 3) (True, 11, 12, 13, 14)
In an equation for `it':
it = over each (+ 3) (True, 11, 12, 13, 14)
GHCi> :set -XPartialTypeSignatures
GHCi> :set -fno-warn-partial-type-signatures
GHCi> :t \f -> over each f :: (_,_,_,_,_) -> _
\f -> over each f :: (_,_,_,_,_) -> _
:: (w -> b5) -> (w, w, w, w, w) -> (b5, b5, b5, b5, b5)
答案 1 :(得分:4)
元组是特定数量的(可能)不同类型的值的分组。即使您考虑表达式(10,11,12,13,14)
,每个元素也可能有不同的类型:
Prelude> :t (10,11,12,13,14)
(10,11,12,13,14)
:: (Num t, Num t1, Num t2, Num t3, Num t4) => (t4, t3, t2, t1, t)
例如,表达式10
可能是Int
,而11
可能是Word
,依此类推。
一般来说,你也可以写一个像("foo", 42, True, "bar", 11)
这样的五元组,它有这种类型:
Prelude> :t ("foo", 42, True, "bar", 11)
("foo", 42, True, "bar", 11)
:: (Num t, Num t1) => ([Char], t1, Bool, [Char], t)
第一个元素是[Char]
值(即String
),下一个元素是某个数字,第三个元素是Bool
,依此类推。
您无法将函数+3
应用于所有这些元素。即使你认为它对字符串有意义(它不是,IMO),我希望你同意你不能将3
添加到True
。
由于它们的类型,元组通常不是Functor
个实例,因此,您不能将fmap
与它们一起使用。
正如Willem Van Onsem在评论中指出的那样,一个两元组 一个Functor
,但它可能不会按照你期望的方式行事。 Haskell pair is a bit funky。