Reader Monad

本文使用Haskell語言,并需要讀者有Monad的基本概念

什么是Reader Monad

在介紹Reader之前,我們先看看Reader的函數(shù)簽名:


newtype Reader e a = Reader {

runReader :: e -> a

}

類似于State, Reader內(nèi)部包含的也是一個函數(shù);但不同之處為State在除了輸出結(jié)果外還會生成一個新的state,而Reader只是單純的輸出一個結(jié)果。

如果不能像State那樣實(shí)現(xiàn)狀態(tài)的傳遞,那么Reader有什么用呢?我們可以先看看下面這個例子。


readString :: Reader String [String]

readString = do

e <- ask  -- 獲取環(huán)境變量

e' <- local ((++) "hi! ") ask  -- 獲取并修改環(huán)境變量

e'' <- ask -- 獲取環(huán)境變量

return (e : e' : e'' : [])

ghci> unline runReader readString "abc"

abc

hi! abc

abc

ask的作用類似于State的get,而local則類似于put。這兩個函數(shù)簽名如下:


ask :: Reader r r

local :: (r -> r) -> Reader r a -> Reader r a

從上述例子中可以看到,即使調(diào)用了local方法,但是每次從環(huán)境中(也就是Reader簽名中的e)獲取的變量都不會改變。所以,Reader的作用大致可以理解為:提供給一個或多個綁定在一起的計(jì)算相同的輸入值。

Reader的簡單使用

在深入了解Reader和輔助函數(shù)的實(shí)現(xiàn)之前,先看一個來自官方文檔的例子。


type Bindings = Map String Int

isCountCorrect :: Bindings -> Bool

isCountCorrect = runReader calc_count -- point-free

calc_count :: Reader Bindings Bool

calc_count = do

bindings <- ask

let count = lookupVar "count" bindings

return (count == (Map.size bindings))

lookupVar :: String -> Bindings -> Int

lookupVar s = fromJust . Map.lookup s  -- point-free

sampleBindings :: Bindings

sampleBindings = Map.fromList [("abc", 1), ("cbd", 2), ("count", 3)]

ghci> isCountCorrect sampleBindings

True

isCountCorrect通過使用calc_count這個Reader獲取結(jié)果,所以我們來分析下calc_count到底做了什么。

calc_count的簽名為Reader Binding Bool,也就是


Reader Binding Bool = Reader {

runReader :: Binding -> Bool

}

在接受類型為Binding的變量后,calc_count會返回Bool變量。這是從外部看calc_count的行為,那它具體做了什么呢?

首先,調(diào)用ask函數(shù)獲取周圍環(huán)境,也就是獲得Binding變量;接著,調(diào)用lookupVar函數(shù)來獲取Map中key為"count"的值,也就是3。最后,對count變量和Map的長度進(jìn)行比較,返回Bool值。

lookupVar函數(shù)則是根據(jù)給定的key來查詢值是否存在,如果存在就返回,不存在就拋出異常。

Reader實(shí)現(xiàn)

了解Reader的結(jié)構(gòu)和基本使用方法后,將Reader聲明為Monad、Applicative等類的instance以獲取更抽象的表達(dá)能力。


instance Monad (Reader e) where

return a = Reader $ const a

m >>= f = Reader $ \e ->

runReader (f $ runReader m e) e

instance Applicative (Reader e) where

pure a = Reader $ const a

f <*> m = Reader $ \e ->

runReader f e $ runReader m e

instance Functor (Reader e) where

fmap f m = Reader $ \e -> f $ runReader m e

除此之外,Reader還有一系列的輔助函數(shù),比如ask、asks、local等。


ask :: Reader r r

ask = Reader $ \e -> e

asks :: (r -> a) -> Reader r a

asks f = do

e <- ask

return $ f e

local :: (r -> r) -> Reader r a -> Reader r a

local f r = do

e <- ask

return $ runReader r (f e)

如之前所說,ask的作用是從獲取環(huán)境變量,也就是e;local是更改一個Reader的環(huán)境變量,但不會local所在的Reader中其他操作獲取到的環(huán)境變量。而asks的作用可由一個例子來展示:


ghci> runReader (asks length) “hi”

2

Reader in Haskell

Haskell中的Reader和State一樣,是通過transformer來實(shí)現(xiàn)的。ReaderT和Reader的函數(shù)簽名是:


newtype ReaderT r m a = ReaderT {

runReaderT :: r -> m a

}

type Reader e a = ReaderT e Identity a

更多的使用例子和ReaderT的實(shí)現(xiàn)細(xì)節(jié)請分別參照

  1. https://hackage.haskell.org/package/mtl-2.2.1/docs/Control-Monad-Reader.html

  2. https://hackage.haskell.org/package/transformers-0.5.4.0/docs/Control-Monad-Trans-Reader.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容