将构图应用于fmap

时间:2016-10-15 05:30:08

标签: haskell types composition

几周以来,我一直试图弄清楚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的类型的?

我实际上是在这里问这个问题,但在我解释我尝试过的时候,所有这些都在一起。所以现在我也要发布答案了。

3 个答案:

答案 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