几周以来,我一直试图弄清楚Haskell编译器如何将(。)应用于fmap。
我的意思是。
:t (.)
(.) :: (b -> c) -> (a -> b) -> a -> c
:t fmap
fmap :: Functor f => (a -> b) -> f a -> f b
:t (.) fmap
(.) fmap :: Functor f => (a -> a1 -> b) -> a -> f a1 -> f b
编译器是如何得到(。)fmap的类型的?
我实际上是在这里问这个问题,但在我解释我尝试过的时候,所有这些都在一起。所以现在我也要发布答案了。
答案 0 :(得分:5)
为了得到这个,我拿了fmap
fmap :: Functor f => (a -> b) -> f a -> f b
fmap :: Functor f => (a -> b) -> (f a -> f b)
如果
:t (.)
(.) :: (b -> c) -> (a -> b) -> a -> c
然后
(。)函数的开头的(b - > c)可以用
替换((a -> b) -> (f a -> f b))
因此我有
((a1 -> b) -> (f a1 -> f b)) -> (a -> (a1 -> b)) -> a -> (f a1 -> f b)
由于(。)已经应用于fmap,我们可以消除((a1 - > b) - >(f a1 - > f b))并且我们留下了
(a -> (a1 -> b)) -> a -> (f a1 -> f b)
然后为了更加清洁我们可以消除额外的括号。
来自IRC Beginner-haskell频道的Glguy和Hamme都提醒我( - >)是正确的联想e.g。 (a - > b - > c - > d)=(a - >(b - >(c - > d)))
所以我们消除了多余的括号。
(a -> a1 -> b) -> a -> f a1 -> f b
:t (.) fmap
(.) fmap :: Functor f => (a -> a1 -> b) -> a -> f a1 -> f b
答案 1 :(得分:2)
如果您将a
重命名为c
,将a1
重命名为a
,并添加一对额外的括号,则可以直观地理解类型签名:
> :t (.) fmap
(.) fmap :: Functor f => (c -> (a -> b)) -> c -> f a -> f b
第一个参数是一个返回另一个函数(a -> b)
的函数,该函数被送入fmap
。应用第一个参数会生成完全组合的函数,等待该一个参数c
。应用c
会生成fmap (a -> b)
,只会等待最后一个参数f a
。
((.) fmap)
((.) fmap (c -> (a -> b)) -- Apply the 1st argument
((.) fmap (c -> (a -> b)) c -- Apply the 2nd argument
fmap (a -> b)
fmap (a -> b) f a -- Apply the 3rd argument
f b -- The result
一个例子:
> ((.) fmap) (\n -> (+n)) 42 [1..5] -- Becomes: fmap (+42) [1..5]
[43,44,45,46,47]
> ((.) fmap) (\n -> (+n)) 13 [1..5]
[14,15,16,17,18]
答案 2 :(得分:0)
了解如何派生类型的一种方法是查看(fmap .)
的含义。
考虑fmap . g
:这是什么意思?扩展.
的定义,我们看到fmap . g = \x -> fmap (g x)
。由于fmap
的第一个参数需要是a -> b
类型的函数,g
必须是类似c -> a -> b
的函数; 计算给定参数的适当函数。
现在,虽然我们可以将fmap f
直接应用于列表(或其他仿函数),但我们需要首先给fmap . g
一个参数:
fmap f someFunctorialValue == someOtherFunctorialValue
((fmap . g) x) someFunctorialValue == someOtherFunctorialValue
删除一些冗余的括号,这就变成了
(fmap .) g x someFunctorialValue == someOtherFunctorialValue
现在我们可以直接了解每个表达式的类型:
-- someFunctorialValue :: Functor f => f a
-- someOtherFunctorialValue :: Functor f => f b
-- x :: c
-- g :: (c -> a -> b)
-- (fmap .) :: (c -> a -> b) -> c -> f a -> f b
-- fmap :: ( a -> b) -> f a -> f b
换句话说:fmap
采用具体功能a -> b
,而(fmap .)
采用“参数化”功能g
和“功能选择器”x