Brent Yorgey出色的UPenn Haskell course礼物:
fmap2 :: Functor f => (a -> b -> c) -> (f a -> f b -> f c)
fmap2 h fa fb = undefined
h
,fa
和fb
的类型细分为:
h :: a -> b -> c
fa :: f a
fb :: f b
我不清楚为什么h
引用整个函数(a -> b -> c)
。
为什么h
无法引用a
而fa
引用(b -> c)
?
(a -> b -> c)
中的括号是否有所作为?
修改
对于只是在不知道所提及的特定课程的情况下阅读此内容的任何人:
fmap2
无法使用该签名进行定义。实际上需要
liftA2 :: Applicative a => (a->b->c) -> (f a->f b->f c)
答案 0 :(得分:12)
是的,括号与您说的完全不同。由于(->)
是right-associative,而不是mathematically associative,因此功能箭头左侧的带括号的表达式无法按照您的建议方式进行拆分:
(a -> b) -> (f a -> f b) /= a -> b -> f a -> f b
->
运算符在这方面就像取幂运算符^
一样,它是符号右关联的not mathematically associative:
(2 ^ 2) ^ (2 ^ 2) /= 2 ^ 2 ^ 2 ^ 2
4 ^ 4 /= 2 ^ (2 ^ (2 ^ 2))
256 /= 2 ^ (2 ^ 4)
256 /= 2 ^ 16
256 /= 65536
(取幂的类比不是我自己的发明;函数类型是“指数类型”,意思是(a, b)
是“产品类型”而Either a b
是“和类型”。 “但请注意,a -> b
类似于b ^ a
,而不是a ^ b
。See this blog post for an example-heavy explanation;还有this answer gives a mathematical overview of type algebra。)
fmap2
的明显奇怪之处在于该类型看起来需要一个参数,但定义看起来需要三个。对比这个版本,对我来说至少看起来更像是类型签名:
fmap2 :: Functor f => (a -> b -> c) -> (f a -> f b -> f c)
fmap2 h = \fa fb -> undefined
现在我们有一个很好的“一个参数”,fmap2 h = ...
,右边有一个“双参数”lambda。诀窍在于,在Haskell中,这两个表达式是等价的[*]:Haskell Report表示“函数”形式,与LHS上的参数一样,在语义上等同于lambda的简单模式绑定。 / p>
您还可以重写类型以消除箭头右侧侧的括号,再次因为->
是右关联的:
(a -> b -> c) -> (f a -> f b -> f c)
== (a -> b -> c) -> f a -> f b -> f c
就像
一样 (2 ^ 2 ^ 2) ^ (2 ^ 2 ^ 2)
== (2 ^ 2 ^ 2) ^ 2 ^ 2 ^ 2
[*]:它们在语义上是等价的,但是当使用GHC编译时,它们的性能特征可能并且有时会有所不同。 GHC的优化程序以不同的方式对待f x = ...
和f = \x -> ...
。