Haskell: (-」) r Functor Applicative Monad 案例選講

Haskell: (->) r Functor Applicative Monad 案例選講

(->) r 的 Functor Applicative Monad 方法實現

類型構造器(->) rFunctor 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的強大。

爛尾了 心情好的時候再補

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章