Prev: State モナド | TOC: 目次 | Next: Writer モナド |
計算のタイプ: | 共有された環境から値を読み込む計算 |
---|---|
束縛の戦略: | モナドの値は環境から値への関数。束縛された関数は束縛された値に 適用され、両方とも共有された環境にアクセスする |
利用場面: | 変数束縛あるいは他の共有環境の保持 |
ゼロおよびプラス: | なし |
型の例: | Reader [(String,Value)] a |
いくつかのプログラミングの課題では、(変数束縛の集合のような)共有された 環境内での計算が必要となる。こうした計算では、典型的にはその環境から 値を読み、ときには変更された(たとえば、新しい束縛や、覆い隠す束縛など) 環境のなかで部分的な計算を実行しますが、完全に一般化された State モナド までは必要ありません。
Reader モナドはこのようなタイプの計算のために特化して設計されています。 そして、多くの場合 State モナドを使うよりも明瞭で簡単な機構です。
ここで示した定義では Haskell 98 の標準にはない、複数パラメータ型クラスお よび funDeps が使われています。この Reader モナドを利用するのに、これの 詳細を完全には理解する必要はありません。
newtype Reader e a = Reader { runReader :: (e -> a) } instance Monad (Reader e) where return a = Reader $ \e -> a (Reader r) >>= f = Reader $ \e -> runReader (f (r e)) e |
Reader モナド中の値はある環境からある値への関数です。Reader モナドの
計算から最終的な値を取り出すには (runReader reader)
を環境
の値に適用するだけですみます。
return
関数は環境を無視し与えられた値を生成する
Reader
を作成します。束縛演算子は、環境を使い、左側の引数
から値を取り出し、同じ環境で、その値に対して束縛された関数を適用する
Reader
を生成します。
class MonadReader e m | m -> e where ask :: m e local :: (e -> e) -> m a -> m a instance MonadReader (Reader e) where ask = Reader id local f c = Reader $ \e -> runReader c (f e) asks :: (MonadReader e m) => (e -> a) -> m a asks sel = ask >>= return . sel |
MonadReader
クラスは Reader モナドを使うときに大変便利な
関数をいくつも提供します。ask
関数は環境を検索します。
local
関数は変更された環境のなかで計算を実行します。
asks
関数は現在の環境を引数とする関数の返り値を得る便利関数で、
選択子関数あるいは探索関数とともに使うのが一般的です。
変数置換やテンプレートのインクルードを含むテンプレートを実体化する問題を
考えましょう。Reader モナドを使えば、既知のテンプレートあるいは既知の
変数束縛の環境を保持することができます。そうすれば、変数置換が起こった
ときに、asks
関数を使って変数の値を探索できます。
テンプレートが新しい変数の定義を伴って,インクルードされた場合、
local
関数を使って、追加された変数束縛を含む変更のあった
環境で、テンプレートの値を解決することができます。
example16.hs で利用できるコード |
---|
-- これはテンプレートの抽象構文表現です -- Text Variable Quote Include Compound data Template = T String | V Template | Q Template | I Template [Definition] | C [Template] data Definition = D Template Template -- 環境は名前付きテンプレートの連想リストと名前付きの変数値の -- 連想リストで構成されています data Environment = Env {templates::[(String,Template)], variables::[(String,String)]} -- 環境から変数を探索 lookupVar :: String -> Environment -> Maybe String lookupVar name env = lookup name (variables env) -- 環境からテンプレートを探索 lookupTemplate :: String -> Environment -> Maybe Template lookupTemplate name env = lookup name (templates env) -- 環境に解決された定義のリストを追加 addDefs :: [(String,String)] -> Environment -> Environment addDefs defs env = env {variables = defs ++ (variables env)} -- Definition を解決し、(name,value) の組を生成 resolveDef :: Definition -> Reader Environment (String,String) resolveDef (D t d) = do name <- resolve t value <- resolve d return (name,value) -- テンプレートを解決し文字列にする resolve :: Template -> Reader Environment (String) resolve (T s) = return s resolve (V t) = do varName <- resolve t varValue <- asks (lookupVar varName) return $ maybe "" id varValue resolve (Q t) = do tmplName <- resolve t body <- asks (lookupTemplate tmplName) return $ maybe "" show body resolve (I t ds) = do tmplName <- resolve t body <- asks (lookupTemplate tmplName) case body of Just t' -> do defs <- mapM resolveDef ds local (addDefs defs) (resolve t') Nothing -> return "" resolve (C ts) = (liftM concat) (mapM resolve ts) |
テンプレート t
を解決し、String
にするのに
必要なのは単に runReader (resolve t) env
を実行することです。
Prev: State モナド | TOC: 目次 | Next: Writer モナド |