将 compose 应用到 fmap
Applying compose to fmap
几个星期以来,我一直在努力弄清楚 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 的类型的?
我本来想在这里问这个问题的,但是当我解释我试过的东西时,一切都在一起了。所以现在我也要 post 回答。
为了得到这个,我使用了 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 都提醒我 (->) 是右联想
例如(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
将a
重命名为c
,将a1
重命名为a
,再多加一对括号,可以直观理解类型签名:
> :t (.) fmap
(.) fmap :: Functor f => (c -> (a -> b)) -> c -> f a -> f b
第一个参数是一个函数,returns 另一个函数 (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]
了解类型如何派生的一种方法是查看 (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 .)
接受"parameterized"函数g
和"function selector" x
.
几个星期以来,我一直在努力弄清楚 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 的类型的?
我本来想在这里问这个问题的,但是当我解释我试过的东西时,一切都在一起了。所以现在我也要 post 回答。
为了得到这个,我使用了 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 都提醒我 (->) 是右联想
例如(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
将a
重命名为c
,将a1
重命名为a
,再多加一对括号,可以直观理解类型签名:
> :t (.) fmap
(.) fmap :: Functor f => (c -> (a -> b)) -> c -> f a -> f b
第一个参数是一个函数,returns 另一个函数 (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]
了解类型如何派生的一种方法是查看 (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 .)
接受"parameterized"函数g
和"function selector" x
.