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的強大。