340 likes | 569 Views
Advanced Functional Programming. Tim Sheard Oregon Graduate Institute of Science & Technology. Lecture 7: Monad Transformers Monads as a data structure Transformers as functions Visualizing Transforms (using MetaML) Transformers as classes. Monad Transformers. A Monad is an ADT
E N D
Advanced Functional Programming • Tim Sheard • Oregon Graduate Institute of Science & Technology • Lecture 7: Monad Transformers • Monads as a data structure • Transformers as functions • Visualizing Transforms (using MetaML) • Transformers as classes
Monad Transformers • A Monad is an ADT • An ADT is a collection of related (typed) functions that interface some data. • A Monad Transformer is a function from one monad ADT to another. • In order to type a monad transformer we need 2 extensions to Haskell’s type system • Higher order type constructors • Rank2 (local) polymorphism
Recall definition of monad • data Mon m = • Mon (forall a . a -> m a) • (forall a b . m a -> (a -> m b) -> m b) • Monad is a type constructor that takes another type constructor as an argument • Mon : * -> * • Each component of the pair which is the argument to Mon is a polymorphic function. • (forall a . a -> m a)
Transformers • type Transformer s t = • Mon s -> Mon t • transform :: Mon S -> Mon T • The type T probably mentions S
Three monads • --Id • data Id x = Id x • --Env • data Env e x = Env (e -> x) • --State • data State s x = State (s -> (x,s))
Id monad • data Id x = Id x • idMon :: Mon Id • idMon = • let bind x f = case x of {(Id y) -> f y } • bind (Id y) f = f y • in Mon Id bind • runId :: Id a -> a • runId (Id x) = x
Env monad • data Env e x = Env (e -> x) • envMon :: Mon (Env e) • envMon = • let unit x = Env(\ _ -> x) • bind (Env g) f = • Env(\ e -> case f(g e) of • Env h -> h e) • in Mon unit bind • readEnv = Env(\ e -> e) • inEnv e (Env f) = Env(\ e2 -> f e)
State Monad • data State s x = State (s -> (x,s)) • stateMon :: Mon (State s) • stateMon = • let unit x = State(\ s -> (x,s)) • bind (State g) f = • State(\ s -> let (a,s') = g s • State h = f a • in h s') • in Mon unit bind • readSt = State(\ s -> (s,s)) • writeSt x = State(\ s -> ((),x))
Env Transformer • data WithEnv env m a = WithEnv (env -> m a) • m is the old monad • env is the type of the env being added to m • Also need to lift old computations and the function rdEnv and inEnv • data Fenv m = Fenv • (forall a e . m a -> WithEnv e m a) • (forall e . WithEnv e m e) • (forall a e . • e -> WithEnv e m a -> WithEnv e m a)
The transformer • transEnv :: Mon m -> (Mon (WithEnv e m),Fenv m) • transEnv (Mon unitM bindM) = • let unitEnv x = WithEnv(\ rho -> unitM x) • bindEnv x f = • WithEnv(\ rho -> • let (WithEnv h) = x • in bindM (h rho) • (\ a -> let (WithEnv g) = f a • in g rho)) • lift2 x = WithEnv(\ rho -> x) • rdEnv = WithEnv(\ rho -> unitM rho) • inEnv rho (WithEnv x) = WithEnv(\ _ -> x rho) • in (Mon unitEnv bindEnv,Fenv lift2 rdEnv inEnv)
The State Transformer • data WithStore s m a = • WithStore (s -> m (a,s)) • data Fstore m = Fstore • (forall a s . m a -> WithStore s m a) • (forall s . (s -> s) -> WithStore s m ()) • (forall s . WithStore s m s)
The transformer • transStore :: Mon m -> (Mon (WithStore s m),Fstore m) • transStore (Mon unitM bindM) = • let unitStore x = WithStore(\sigma -> unitM(x,sigma)) • bindStore x f = • WithStore(\ sigma0 -> • let (WithStore h) = x • in bindM (h sigma0) • (\ (a,sigma1) -> • let (WithStore g) = f a • in g sigma1 ) ) • lift2 x = WithStore(\ sigma -> • bindM x (\ y -> unitM(y,sigma))) • update f = WithStore(\ sigma -> unitM((),f sigma)) • get = WithStore(\sigma -> unitM(sigma,sigma)) • in (Mon unitStore bindStore,Fstore lift2 update get)
An example The problem is that the functions on the inner monad are not lifted. We can use Haskell Classes to fix this. • ex1 :: (Mon (WithStore a (WithEnv b Id)) • ,Fenv Id • ,Fstore (WithEnv c Id)) • ex1 = let (mon1,funs1) = transEnv idMon • (mon2,funs2) = transStore mon1 • in (mon2,funs1,funs2)
Making transformers “visible” in MetaML • datatype ('M : * -> * ) Monad2 = • Mon2 of • (['a]. <'a -> 'a 'M>) * • (['a,'b]. <'a 'M -> ('a -> 'b 'M) -> 'b 'M>); • val id2 = • (Mon2 (<Id>, • <fn x => fn f => • let val (Id y) = x in f y end>));
Env transformer at the code level • fun TransEnv2 (Mon2(unitM,bindM)) = • let val unitEnv = • <fn x => Tenv(fn rho => ~unitM x)> • in Mon2(unitEnv, • <fn x => fn f => • Tenv(fn rho => • let val (Tenv h) = x • in ~(force (force bindM <h rho>) • <fn a => • let val (Tenv g) = f a • in g rho end> • ) end)>) end;
State transformer at code level • fun TransStore2 (Mon2(unitM,bindM)) = • Mon2 • (<fn x => Tstore(fn sigma => ~unitM (x,sigma))>, • <fn x => fn f => • Tstore(fn sigma0 => • let val (Tstore h) = x • in ~(force (force bindM <h sigma0>) • <fn w => let val (a,sigma1) = w • val (Tstore g) = f a • in g sigma1 end> • ) • end)>);
An example • -| fun bindof (Mon2(x,y)) = y; • val bindof = Fn : ['a,'b,'c]. • 'c Monad2 -> <'b 'c -> ('b -> 'a 'c ) -> 'a 'c > • val ans = • bindof(TransStore2 (TransEnv2 id2));
-| val ans = • <(fn a => • (fn b => • Tstore • (fn c => • let val Tstore d = a • in Tenv • (fn e => • let val Tenv f = d c • val Id g = f e • val (i,h) = g • val Tstore j = b i • val Tenv k = j h • in k e end) end)))> • : <('3 ,'2 ,('1 ,Id) Tenv) Tstore -> • ('3 -> ('4 ,'2 ,('1 ,Id) Tenv) Tstore) -> • ('4 ,'2 ,('1 ,Id) Tenv) Tstore>
Using Haskell Classes • This part of the lecture is based upon the Monad library developed (in part) by Iavor Diatchki. • Uses classes instead of data stuctures to encode monads. • Instances then are used to encode monad transformers.
From IxEnvMT.hs • newtype WithEnv e m a = • E { unE :: e -> m a } • withEnv :: e -> WithEnv e m a -> m a • withEnv e (E f) = f e • mapEnv :: Monad m => (e2 -> e1) -> WithEnv e1 m a -> WithEnv e2 m a • mapEnv f (E m) = E (\e -> m (f e))
From IxEnvMT.hs cont. • instance Monad m => • Functor (WithEnv e m) where • fmap = liftM • instance Monad m => • Monad (WithEnv e m) where • return = lift . return • E m >>= f = E (\e -> do { x <- m e • ; unE (f x) e }) • E m >> n = E (\e -> m e >> withEnv e n) • fail = lift . fail
IxStateMT.hs • newtype WithState s m a = • S { ($$) :: s -> m (a,s) } • withSt :: Monad m => s -> WithState s m a -> m a • withSt s = liftM fst . withStS s • withStS :: s -> WithState s m a -> m (a,s) • withStS s (S f) = f s • mapState :: Monad m => • (t -> s) -> (s -> t) -> • WithState s m a -> WithState t m a • mapState inF outF (S m) = S (liftM outF' . m . inF) • where outF' (a,s) = (a, outF s)
IxStateMT.hs continued • instance Monad m => Functor (WithState s m) where • fmap = liftM • instance Monad m => Monad (WithState s m) where • return x = S (\s -> return (x,s)) • S m >>= f = S (\s -> m s >>= \(a,s') -> f a $$ s') • fail msg = S (\s -> fail msg)
To lift from one monad to another • Add a new class for monad transformers • class MT t where • lift :: (Monad m, Monad (t m)) => • m a -> t m a
Add instance for each Transformer • instance MT (WithState s) where • lift m = • S (\s -> do a <- m; return (a,s)) • instance MT (WithEnv e) where • lift = E . Const • Each Transformer also supports a class of operations – the HasXX classes
HasEnv • class Monad m => HasEnv m ix e | m ix -> e where • getEnv :: ix -> m e • inEnv :: ix -> e -> m a -> m a • inModEnv :: ix -> (e -> e) -> m a -> m a • inEnv ix e = inModEnv ix (const e) • inModEnv ix f m = do { e <- getEnv ix • ; inEnv ix (f e) m } • -- define at least one of: • -- * getEnv, inEnv • -- * getEnv, inModEnv
HasState • class Monad m => HasState m ix s | m ix -> s where • updSt :: ix -> (s -> s) -> m s -- returns the old state • updSt_ :: ix -> (s -> s) -> m () • getSt :: ix -> m s • setSt :: ix -> s -> m s -- returns the old state • setSt_ :: ix -> s -> m () • updSt ix f = do s <- getSt ix; setSt ix (f s) • setSt ix = updSt ix . const • getSt ix = updSt ix id • updSt_ ix f = do updSt ix f; return () • setSt_ ix s = do setSt ix s; return () • -- define at least one of: • -- * updSt • -- * getSt, setSt
Example use -- Interpreter • Syntax • type Name = String • type Message = String • data E = E :+: E | E :-: E | • E :*: E | E :/: E | Int Int • | Let Name E E | Var Name • | Print Message E • | ReadRef Name • | WriteRef Name E • | E :> E
The eval function • eval (e1 :+: e2) = liftM2 (+) (eval e1) (eval e2) • eval (e1 :-: e2) = liftM2 (-) (eval e1) (eval e2) • eval (e1 :*: e2) = liftM2 (*) (eval e1) (eval e2) • eval (e1 :/: e2) = liftM2 div (eval e1) $ • (do x <- eval e2 • when (x == 0) (raise "division by 0") • return x) • eval (Int x) = return x • eval (Let x e1 e2) = do v <- eval e1; inModEnv ((x,v):) (eval e2) • eval (Var x) = (maybe (raise ("undefined variable: " ++ x)) return) . • (lookup x =<< getEnv) • eval (Print x e) = do v <- eval e • output (x ++ show v) • return v • eval (ReadRef x) = maybe (return 0) return . lookup x =<< getSt • eval (WriteRef x e) = do v <- eval e • updSt_ $ \s -> (x,v) : filter ((/= x) . fst) s • return 0 • eval (e1 :> e2) = eval e1 >> eval e2
The Type of Eval • eval :: • (HasState a Z [([Char],Int)], • HasOutput a Z [Char], • HasEnv a Z [([Char],Int)], • HasExcept a [Char]) => • E -> a Int
What Monad has all these? • type Heap = [(Name,Int)] • type Env = [(Name,Int)] • type M = WithState Heap • ( WithEnv Env • ( WithOutput String • ( WithExcept String IO • )))
instance Monad m => HasEnv (WithEnv e m) Z e where • getEnv _ = E return • inModEnv _ = mapEnv • instance HasEnv m ix e => HasEnv (WithEnv e' m) (S ix) e where • getEnv (Next ix) = lift (getEnv ix) • inModEnv (Next ix) f m = E (\e -> inModEnv ix f (withEnv e m)) • instance HasState m ix s => HasState (WithEnv e m) ix s where • updSt ix = lift . updSt ix • instance HasOutput m ix o => HasOutput (WithEnv e m) ix o where • outputTree ix = lift . outputTree ix
Run for the monad • Then run for the monad is the code that actually specifies how the pieces are put together! • run :: M a -> IO a • run m = • do x <- removeExcept $ listOutput $ withEnv [] $ withSt [] m • case x of • Left err -> error ("error: " ++ err) • Right (v,o) -> mapM putStrLn o >> return v
Advanced Features • Multiple occurences of Env, State, etc.