Haskell 为什么fmap不';一个元组不行吗?
在下面的示例中,我尝试在元组上使用Haskell 为什么fmap不';一个元组不行吗?,haskell,tuples,functor,Haskell,Tuples,Functor,在下面的示例中,我尝试在元组上使用fmap,但这不起作用,尽管它适用于列表和仅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 constra
fmap
,但这不起作用,尽管它适用于列表和仅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>
Prelude>fmap(+3)(仅4)
只有7个
前奏曲>fmap(+3)[1,2,3,4]
[4,5,6,7]
序曲>fmap(+3)(10,11,12,13,14)
:38:1:错误:
*非类型变量参数
在约束中:函子((,,,,)a b1 c d)
(使用flexibleContext允许此操作)
*检查推断类型时
it::对于所有b1 c d b2。
(Num d,Num c,Num b1,Num a,Num b2,函子((,,,,)a b1 c d))=>
(a、b1、c、d、b2)
序曲>
元组是(可能)不同类型的特定数量的值的分组。即使你考虑表达式<代码>(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
可以是单词
,依此类推
通常,您还可以编写一个五元组,如(“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]
值(即字符串
),下一个元素是某个数字,第三个元素是Bool
,依此类推
不能将函数+3
应用于所有这些元素。即使您认为它对字符串有意义(依我看,它不是),我希望您同意您不能将3
添加到True
由于它们的类型,元组通常不是
Functor
实例,因此不能对它们使用fmap
正如Willem Van Onsem在评论中指出的那样,二元组是一个
函子
,但它可能不会以您期望的方式运行。这个
为什么fmap不适用于[5-]元组
因为还没有人将5元组Functor
实例添加到base中。如果你看一下,你会发现函子((,)a)
,成对的实例,而不是较大元组的实例,包括函子((,,,)a b c d)
,这是你在这里需要的
接下来的问题当然是:为什么还没有人将5元组Functor
实例添加到base中?其中一个原因与必要性(或缺乏必要性)有关:在实践中,成对出现的频率远远高于较大的元组,并且随着元组变大,越来越难以证明使用它们而不是适合自己用例的非匿名类型是正确的。因此,对于较大元组的funcor
实例的需求并没有那么大
虽然您没有提到您期望的函子((,,,)a b c d)
的行为,但值得注意的是,成对的fmap
仅作用于第二个组件,而较大元组的实例类似地仅处理最后一个组件
GHCi> fmap not (False, True)
(False,False)
原因有两个:
fmap not(“foobar”,True)
无法更改这两个组件Functor
实例(除非使用,但这不在点上)(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不提供三函子
,四函子
,等等。)
在给较大的元组提供Functor
实例时,以同样的方式考虑较大的元组也是合理的;事实上,为了保持一致性,这些实例应该存在。然而,有些人确实非常不喜欢成对的例子,这导致了停顿
附言:也许值得一提的是,镜头库的一个(许多)用例使用的不是Functor
S的东西作为Functor。这为我们提供了一种方便的方法来查看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)
因为元组的类型是
(a,b,c)
(可以泛化),所以如果你想让它成为函子的一个实例,你只能映射一个元素。一个有助于界定答案范围的问题:你希望从fmap(+3)(10,11,12,13,14)
得到什么结果,这看起来像是从fmap(+3)(10,11)
中得到的吗?顺便说一下,一个2元组是函子的一个实例--“由于它们的类型,元组通常不是函子的实例”--我不会这样说。虽然不是每个人都同意这一点,但我坚定地认为配对的函子
实例是完全合理的。针对最后一个组件的较大元组的实例也同样合理;基本库不提供它们仅仅是因为,在实践中,它们比成对库需要的次数少得多(因此,有人建议为了一致性而添加缺少的实例)。@duplode确实如此,但我确实属于那种认为(,)
的函子实例不直观的阵营。我确实理解它为什么会出现,但我发现它含蓄而武断。同样,我理解将翻译应用于第二个元素的原因是由于部分应用,但我认为
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)