fmap.fmap
允许我们将“两层深层”变成一个仿函数:
fmap.fmap :: (a -> b) -> f (g a) -> f (g b)
这对应用函子也有可能吗?假设我想通过使用它们的应用属性来组合Just (+5)
和[1,2,3]
。我可以想出一个明显的方法来做到这一点,但对我来说这似乎并不重要。
(<*>).(<*>)
没有确凿的类型签名:
((<*>).(<*>)) :: (a1 -> a2 -> b) -> ((a1 -> a2) -> a1) -> (a1 -> a2) -> b
-- where I would expect something like:
-- ((<*>).(<*>)) :: f (g (a -> b)) -> f (g a) -> f (g b)
是否可以这种方式撰写Just (+5)
和[1,2,3]
?
编辑:
第一步是使用:
pure $ Just (+5)
和fmap pure [1,2,3]
或fmap pure (Just (+5)
和pure [1,2,3]
但我仍然不知道如何撰写这些......
编辑:
有一个通用的方法来组成一个函数f (g (a -> b)
和f (g a)
会很好,我不只是在寻找上述情况的解决方案,它只是作为一个这种功能的示例输入。基本上我想要一个功能:
(<***>) :: f (g (a -> b)) -> f (g a) -> f (g b)
答案 0 :(得分:4)
liftA2
具有与fmap
类似的组合属性。
liftA2 f :: f a -> f b -> f c
(liftA2 . liftA2) f :: g (f a) -> g (f b) -> g (f c)
所以你可以写
(liftA2 . liftA2) ($) (pure (Just (+5))) (fmap pure [1,2,3]) :: [Maybe Integer]
即,(<***>) = (liftA2 . liftA2) ($)
。 (很像(<*>) = liftA2 ($)
)
另一种看待它的方法是,应用函子的组合是一个应用函子,这是由Data.Functor.Compose
具体化的:
{-# LANGUAGE ScopedTypeVariables, PartialTypeSignatures #-}
import Data.Functor.Compose
import Data.Coerce
(<***>) :: forall f g a b. (Applicative f, Applicative g)
=> f (g (a -> b)) -> f (g a) -> f (g b)
(<***>) = coerce ((<*>) :: Compose f g (a -> b) -> _)
coerce
的要点是要显示(<***>)
适用于正确类型的(<*>)
;我们也可以手动展开
f <***> x = getCompose $ Compose f <*> Compose x
答案 1 :(得分:2)
我们有f (g (a->b))
。要从g a -> g b
获取g (a->b)
,我们只需要<*>
,但g (a->b)
包含在f
中。幸运的是f
是一个Functor,所以我们可以fmap
覆盖它。
Prelude> :t fmap (<*>)
fmap (<*>)
:: (Functor f1, Applicative f) =>
f1 (f (a -> b)) -> f1 (f a -> f b)
Prelude>
那更好,我们现在有一个包含在Functor中的函数。如果此Functor恰好是申请人,我们可以通过它申请<*>
。
Prelude> :t (<*>) . fmap (<*>)
(<*>) . fmap (<*>)
:: (Applicative f, Applicative f1) =>
f1 (f (a -> b)) -> f1 (f a) -> f1 (f b)
Prelude>
正是医生所要求的。
Prelude> let (<***>) = (<*>) . fmap (<*>)
Prelude> [Just (+2), Just (*3), Nothing] <***> [Just 7, Just 42, Nothing]
[Just 9,Just 44,Nothing,Just 21,Just 126,Nothing,Nothing,Nothing,Nothing]
Prelude>