monad transformers -- Identity モナドと IO モナド

前回の記事で式を評価する関数 eval0 をモナド化して eval1 に改装した。eval1 の型は eval1 :: Env -> Exp -> Identity Value で eval1 は Identity モナドの関数だった。しかし、Iidentity モナドで記述された関数はモナド特有の関数として return と >>= しか使っていない。ということは、eval1 関数の記述を全く変更せずに eval1 :: Env -> Exp -> IO Value と eval1 の関数の型を IO モナドのものに変更するだけで eval1 関数は IO モナドの関数として使うことができる。Identity モナドのモナドとしての性質以外全く何もないという性格が、関数をそのまま IO モナドで使うという離れ業を実現させる。

ソースコードは最後に表示するが、以下の実行例のように eval1 の具体的な記述には一切手を入れずに、eval1 を IO モナドの関数として取り扱うことができることがわかる。

コードの main 関数

main = do
  x <- eval1 Map.empty exampleExp
  putStrLn $ show exampleExp
  putStrLn $ show x

実行例

~/programming/Haskell$ runghc evalio.hs
Plus (Lit 12) (App (Abs "x" (Var "x")) (Plus (Lit 4) (Lit 2)))
IntVal 18

ソース

module Transformers where
import Control.Monad.Identity
import Control.Monad.Except
import Control.Monad.Reader
import Control.Monad.State
import Control.Monad.Writer
import Data.Maybe
import qualified Data.Map as Map

type Name = String
data Exp =
    Lit Integer
  | Var Name
  | Plus Exp Exp
  | Abs Name Exp
  | App Exp Exp
  deriving (Show)
data Value =
    IntVal Integer
  | FunVal Env Name Exp
  deriving (Show)
type Env = Map.Map Name Value

exampleExp = Lit 12 `Plus` (App (Abs "x" (Var "x")) (Lit 4 `Plus` Lit 2))

eval1 :: Env -> Exp -> IO Value
eval1 env (Lit i) = return $ IntVal i
eval1 env (Var n) = return $ fromJust $ Map.lookup n env
eval1 env (Plus e1 e2) = do
  IntVal i1 <- eval1 env e1
  IntVal i2 <- eval1 env e2
  return $ IntVal (i1 + i2)
eval1 env (Abs n e) = return $ FunVal env n e
eval1 env (App e1 e2) = do
  val1 <- eval1 env e1
  val2 <- eval1 env e2
  case val1 of
    FunVal env' n body -> eval1 (Map.insert n val2 env') body

main = do
  x <- eval1 Map.empty exampleExp
  putStrLn $ show exampleExp
  putStrLn $ show x

それ自身ではあまり意味がないように思われる Identity モナドだが、Identity モナドで動く関数は、全てのモナドで動作させることができる。自前の関数のモナド化には Identity モナドでの実験が必須とも言える。次回の記事では、これを ExceptT モナドで動くようにする。

[PR]
by tnomura9 | 2018-12-06 21:25 | Haskell | Comments(0)
<< monad transform... Haskell の例外処理 >>