目前通过this article阅读(这非常棒)并且有一个非常简单的问题:
如果我将(+3)
和(+2)
这两个函数与<$>
结合起来,它似乎给了我一个新函数,它将5传递给它。如果我对函数组合运算符(即(+3) . (+2)
)执行相同的操作,是否会执行相同的操作?如果这是真的,这两个运算符之间是否存在关系,这样它们在这个简单的情况下会做同样的事情?
这甚至是一个聪明的问题吗?
答案 0 :(得分:8)
函数fmap
和<$>
都具有相同的类型:
> :t fmap
fmap :: Functor f => (a -> b) -> f a -> f b
> :t (<$>)
(<$>) :: Functor f => (a -> b) -> f a -> f b
虽然函数.
是
> :t (.)
(.) :: (b -> c) -> (a -> b) -> a -> c
那么我们如何才能在函数上使用fmap
并最终得到.
?我假设您了解Functor是什么,所以现在您必须了解&#34;功能&#34;是Functors。怎么样?
> :i (->)
data (->) a b -- Defined in `GHC.Prim'
instance Monad ((->) r) -- Defined in `GHC.Base'
instance Functor ((->) r) -- Defined in `GHC.Base'
instance Applicative ((->) a) -- Defined in `Control.Applicative'
与Just
,[]
和Left
不同,函数没有可以使用的构造函数。 Functor实例应用于语法本身。我们可以从:ghci中的信息中看到,语法箭头->
实际上有一个仿函数实例。
当我们查看+3的类型时会发生什么?
> :t (+3)
(+3) :: Num a => a -> a
因此函数(+3)是一个接受a并返回a的Functor。当我们在Functor上使用fmap
并且还给我们一个Functor时,我们得到嵌套的Functors:
> :t fmap Just (Just 3)
fmap Just (Just 3) :: Num a => Maybe (Maybe a)
> :t fmap (replicate 5) [1,2,3]
fmap (replicate 5) [1,2,3] :: Num a => [[a]]
同样,当我们将fmap
应用于两个函数时,我们在函数内部得到一个函数。唯一的区别是它们融合在一起:
> :t (fmap (+3) (+2))
(fmap (+3) (+2)) :: Num a => a -> a
为什么这种结果不属于(->) (->) a a
类型?我们必须记住,fmap
的第一个参数是函数(a -> b)
而不一定是一个函数。因此,当我们fmap g (Just 5)
时,我们可以进行任何转换。但是,每当我们对某个函数执行fmap
时,我们就会知道它将始终在函数内部生成一个函数。
因此fmap (+3) (+2)
评估为:\x -> (\x' -> x' + 3) (x + 2)
。这是写(+3) . (+2)
的真正迂回方式。
> :t (fmap (+3) (+2))
(fmap (+3) (+2)) :: Num a => a -> a
> :t ((.) (+3) (+2))
((.) (+3) (+2)) :: Num a => a -> a
通常,为了解决concat问题(Maybe (Maybe a))
或[[a]]
,我们实际上需要依赖它Monad a
,以便我们可以使用绑定>>=
。但是函数(->)
是一个特例,因为我们知道每次在函数上使用fmap
时,它总是会给函数一个函数。除了->
之外,对于任何其他Functor都不能这样说。因此,我们可以确保始终在函数上连接fmap
。
因此任何f <$> g == f . g
编辑:快速注释,如果你执行此操作fmap (+) (+0)
,你最终会在函数内部使用一个函数。在这种情况下,实际上需要使用monadic绑定(>>=
)来连接函数:
> :t fmap (+) (+0)
fmap (+) (+0) :: Num a => a -> a -> a
> :t (+0) >>= (+)
(+0) >>= (+) :: Num b => b -> b
> let bindfunc = (+0) >>= (+)
> bindfunc 5
10
这与我们[1,2] >>= replicate 5
时的行为完全不同:
> [1,2] >>= replicate 5
[1,1,1,1,1,2,2,2,2,2]
答案 1 :(得分:7)
要查找有关函数的Functor
实例的信息,请匹配类型以查找相关实例:
fmap :: (a -> b) -> f a -> f b
然后是a ~ Int
,b ~ Int
和f ~ (->) Int
。
您可以查看GHC here附带的所有Functor
个实例。 (->)
只是一个具有两个类型参数的中缀类型运算符。我们通常会将其应用为Int -> Int
,但这相当于(->) Int Int
。
(部分应用)类型Functor
(对于任何类型(->) r
)都有r::*
个实例。
查看((->) r)
instance for Functor
,我们看到fmap = (.)
,因此(+3) . (+2)
和fmap (+3) (+2)
之间没有实际差异(与(+3) <$> (+2)
相同。