Haskell: (->) r Functor Applicative Monad 案例选讲
(->) r 的 Functor Applicative Monad 方法实现
类型构造器(->) r
是Functor
Applicative
Monad
的实例,相关定义如下:
instance Functor ((->) r) where
fmap = (.)
(<$>) = fmap
instance Applicative ((->) r) where
pure x _ = x
(<*>) f g = \x -> f x (g x)
instance Monad ((->) r) where
return x _ = x
h >>= f = \x -> f (h x) x
(因为在文档里面不好找,所以整理出来)
一些案例
在ghci中进行下列尝试时,请先:m + Prelude Control.Monad Control.Applicative
liftA系列
这些函数的定义如下:
-- | Lift a function to actions.
-- This function may be used as a value for `fmap` in a `Functor` instance.
liftA :: Applicative f => (a -> b) -> f a -> f b
liftA f a = pure f <*> a
-- | Lift a binary function to actions.
liftA2 :: Applicative f => (a -> b -> c) -> f a -> f b -> f c
liftA2 f a b = f <$> a <*> b
-- | Lift a ternary function to actions.
liftA3 :: Applicative f => (a -> b -> c -> d) -> f a -> f b -> f c -> f d
liftA3 f a b c = f <$> a <*> b <*> c
应用:
1.Applicative
里面的<*
, *>
算符就是借助liftA2
定义的。
class Functor f => Applicative f where
-- | Lift a value.
pure :: a -> f a
-- | Sequential application.
(<*>) :: f (a -> b) -> f a -> f b
-- | Sequence actions, discarding the value of the first argument.
(*>) :: f a -> f b -> f b
(*>) = liftA2 (const id)
-- | Sequence actions, discarding the value of the second argument.
(<*) :: f a -> f b -> f a
(<*) = liftA2 const
{-
in which
const x y = x (returns the 1st parameter)
const id x y = y (returns the 2nd parameter)
-}
2.liftA
系列的函数,在(->) r
类型的用法是,对同一个自变量,先用各个函数转换成应有的类型(称为lift),再feed进最开头的多元函数。
首先举一个算数的例子:
liftA2 (+) <$> f <*> g x
= (\x1 x2 -> f x1 + x2) <*> g x
= (\x3 -> (\x1 x2 -> f x1 + x2) x3 (g x3)) x
= (\x1 x2 -> f x1 + x2) x (g x)
= f x + g x
其中(+)可以换为任何一个二元运算。
应用举例:liftA2 (+) (+2) (*10) 3 = (+2) 3 + (*10) 3 = 5 + 30 = 35
再举一个更一般的例子:let f = \c x -> take x (repeat c) in liftA3 (\x y z -> x ++ y ++ z) (f 'a') (f 'b') (f 'c') 2 = "aabbcc"
也就是说,liftA3 f g1 g2 g3 x = f (g1 x) (g2 x) (g3 x)
, liftA2 f g1 g2 x = f (g1 x) (g2 x)
类似的,liftA f g x = f (g x)
.liftA
的定义形式和liftA2
, liftA3
不同,但效果一样。这里面肯定揭露了一些本质性的东西,但暂时还没想明白。可能本质上是利用了pure
函数的某个性质。(欢迎指教!)
liftM
-- | Promote a function to a monad.
liftM :: (Monad m) => (a1 -> r) -> m a1 -> m r
liftM f m1 = do { x1 <- m1; return (f x1) }
-- | Promote a function to a monad, scanning the monadic arguments from
-- left to right. For example,
--
-- > liftM2 (+) [0,1] [0,2] = [0,2,1,3]
-- > liftM2 (+) (Just 1) Nothing = Nothing
-- > liftM2 (+) (+1) (*2) x = (+1) x + (*2) x
--
liftM2 :: (Monad m) => (a1 -> a2 -> r) -> m a1 -> m a2 -> m r
liftM2 f m1 m2 = do { x1 <- m1; x2 <- m2; return (f x1 x2) }
-- | Promote a function to a monad, scanning the monadic arguments from
-- left to right (cf. 'liftM2').
liftM3 :: (Monad m) => (a1 -> a2 -> a3 -> r) -> m a1 -> m a2 -> m a3 -> m r
liftM3 f m1 m2 m3 = do { x1 <- m1; x2 <- m2; x3 <- m3; return (f x1 x2 x3) }
-- liftM4 :: (Monad m) => (a1 -> a2 -> a3 -> a4 -> r) -> m a1 -> m a2 -> m a3 -> m a4 -> m r
-- liftM5 :: (Monad m) => (a1 -> a2 -> a3 -> a4 -> a5 -> r) -> m a1 -> m a2 -> m a3 -> m a4 -> m a5 -> m r
跟liftA
基本一码事。
另外提到ap
函数:
liftM2 :: Monad m => (a1 -> a2 -> r) -> m a1 -> m a2 -> m r
ap :: Monad m => m (a -> b) -> m a -> m b
ap = liftM2 id -- a1 = a -> b, a2 = a, r = b
在实现了Monad实例的类型中,Haskell标准要求<*>
和ap
是等价的.自己推一下ap (+) (*2) 10
就知道为什么了,也可以自己证明一下。
>>=相关
心情好的时候会把文档里面和>>=
相关的函数捋一遍,然后就会发现一些原本认为很高阶的函数,在monad中都是>>=
的special case,都可以用return
和>>=
和一些简单函数重新构建,从而发现monad的强大。