300 likes | 465 Views
Advanced Functional Programming. Tim Sheard Oregon Graduate Institute of Science & Technology. Lecture 7: Monads and Lazy State Defining return and (>>=) Modular languages and rich monads Lazy State Monad. Defining Monads.
E N D
Advanced Functional Programming • Tim Sheard • Oregon Graduate Institute of Science & Technology • Lecture 7: Monads and Lazy State • Defining return and (>>=) • Modular languages and rich monads • Lazy State Monad
Defining Monads • To define a new instance of the Monad class we need two functions return and (>>=). • If you have trouble doing this try this exercise. • Write a program without do or return that sequences two actions of type M a • Observe what's happening. Find the pattern in the plumbing. • Abstract over what's special about your example, and what’s common to all sequencing of two terms of type M a.
Modular Language • Our modular language interpretation depended on monads and making sub classes of the monad class. • class Monad m => Failure m where • fails :: m a • class Monad m => HasEnv m v where • inNewEnv :: String -> v -> m v -> m v • getfromEnv :: String -> m v • class Monad m => HasStore m v where • getfromStore :: String -> m v • putinStore :: String -> v -> m v • class (Monad m,Show v) => Prints m v where • write :: String -> v -> m v • Are there any monads which meet all these constraints?
The Term Language • data Term • = Arith (Arith Term) • | Divisible (Divisible Term) • | LocalLet (LocalLet Term) • | Assignment (Assignment Term) • | Print (Print Term) • instance (Monad m, Failure m, Integral v, • HasEnv m v, HasStore m v, Prints m v) => • Eval Term v m where • eval (Arith x) = eval x • eval (Divisible x) = eval x • eval (LocalLet x) = eval x • eval (Assignment x) = eval x • eval (Print x) = eval x
A rich monad • In order to evaluate Term we need a rich monad, and value types with the following constraints. • Monad m • Failure m • Integral v • HasEnv m v • HasStore m v • Prints m v
The Monad M • type Maps x = [(String,x)] • data M v x = • M(Maps v -> Maps v -> (Maybe x,String,Maps v)) • instance Monad (M v) where • return x = M(\ st env -> (Just x,[],st)) • (>>=) (M f) g = M h • where h st env = compare env (f st env) • compare env (Nothing,op1,st1) = (Nothing,op1,st1) • compare env (Just x, op1,st1) • = next env op1 st1 (g x) • next env op1 st1 (M f2) • = compare2 op1 (f2 st1 env) • compare2 op1 (Nothing,op2,st2) • = (Nothing,op1++op2,st2) • compare2 op1 (Just y, op2,st2) • = (Just y, op1++op2,st2)
Making M an instance of the subclasses • instance Failure (M v) where • fails = M(\ st env -> (Nothing,[],st)) • instance HasEnv (M v) v where • inNewEnv name v (M f) = M(\ st env -> f st ((name,v):env)) • getfromEnv name = M h • where h st env = (Just(get name env),[],st) • instance HasStore (M v) v where • getfromStore name = M h • where h st env = (Just(get name st),[],st) • putinStore name v = M h • where h st env = (Just v,[],(name,v):st) –- space leak • instance Show v => Prints (M v) v where • write message v = M h • where h st env = (Just v,message++(show v),st)
Mutable State in a pure language • State is important for efficiency • We would like to have mutable variables but encapuslate them so we could have pure functions, which deep down inside mutated state, but this could not be observed. • The factorial function is a good example of this.
A “State-based” Algorithm • Number the nodes of a tree in pre-order • data Tree a = Tip | Node (Tree a) a (Tree a) • number :: Tree a -> Int -> (Tree (a,Int), Int) • number Tip n = (Tip, n) • number (Node l x r) n = (Node l’ (x,n) r’, n’’) • where (l’,n’) = number l (n+1) • (r’,n’’) = number r n’ • All the number plumbing is done explicitly • Why is this easier in an imperative language? • Does this program look likre one of our hand threaded monad programs? which one?
Another Example • Consider the data type encoding expressions: • data Aop = Multiply | Add | Sub • data Exp • = Num Int • | Var String • | Oper (Exp, Aop, Exp ) • | Let (String, Exp, Exp) • Let x = 5 in x + (4 * 2) • e2 = Let("x",Num 5, • Oper(Var "x", Add, • Oper(Num 4,Multiply,Num 2)))
Definition of Free & Bound • A Variable is "bound" if it occurs in the body of a let where the variable is declared. • A variable is free in an Exp if it is not bound. • freeV :: Exp -> [ String] • freeV term = freevars [] term [] • where • freevars bound x free = case x of • Num(i) -> free • Var(s) -> if any (==s) bound • then free else add s free • Oper(x,y,z) ->freevars bound x (freevars bound z free) • Let(s,e,body) -> • freevars (s:bound) body (freevars bound e free) • add x [] = [x] • add x xs = if any (==x) xs then xs else x:xs
The Problem • Problem transform an Exp into another Exp which has all of its "bound" variables renamed. • For example: • let x = z * 2 • in let q = x + w • in x - q * v • Renames to: • let x2 = z * 2 • in let q3 = x2 + w • in x2 - q3 * v • How do I rename a string. Append an string representation of an integer to it. • rename n x = x ++ (show n)
A Functional Solution • To rename a term I need to pipe a supply of new integers through my renaming program. • find s [] = s • find s ((t,v):xs) = if s==t then v else find s xs • renameE x = snd(rnE [] 0 x) • where rnE bnd n x = case x of • Num(i) ->(n,Num i) • Var(s) -> (n,Var(find s bnd)) • Oper(x,y,z) -> let (n1,x') = rnE bnd n x • (n2,z') = rnE bnd n1 z • in (n2,Oper(x',y,z')) • Let(s,e,b) -> • let s' = rename n s • (n1,b') = rnE ((s,s'):bnd) (n+1) b • (n2,e') = rnE bnd n1 e • in (n2,Let(s',e',b'))
Example Use • e3 = • Let("x", Oper(Var "z",Mult,Num 2), • Let("q", • Oper(Var "x",Add,Var "w"), • Oper(Var "x",Sub, • Oper(Var "q", • Mult,Var "v")))) • ? renameE e3 • Let ("x0", Oper (Var "z",Mult,Num 2), • Let ("q1", • Oper (Var "x0",Add,Var "w"), • Oper (Var "x0",Sub, • Oper (Var "q1", • Mult,Var "v"))))
a ST s a State s State s Modelling State Transformers • A state transformer is modelled by a function • Takes an input state • Delivers a result value, and an output state • Ignore the s argument for now type ST s a -- (= State s -> (a, State s) )
a a State s State s return The Return State Transformer • Simplest state transformer • Does nothing to the state • Returns the value supplied • Acts as a unit for the monad • return :: a -> ST s a • return x s = (x,s)
ST s a a -> ST s b Combining State Transformers • A state transformer delivering a result value of type a • A parameterized state transformer (over type a) delivering a result value of type b
Using the State Monad • Lazy State in Haskell and the ST and STRef type constructors. • They are a monad • They are an ADT • They are implemented in a fashion that really mutates state • They can be abstracted so that things built with them are pure functions. • type STRef s a • newSTRef :: a -> ST s (STRef s a) • readSTRef :: STRef s a -> ST s a • writeSTRef :: STRef s a -> a -> ST s() • runST :: ST a x -> x ?????
A Simple Example • swap :: STRef s a -> STRef s a -> ST s () • swap v w = do { x <- readSTRef v • ; y <- readSTRef w • ; writeSTRef w x • ; writeSTRef v y • }
The Monadic Renaming Example • rn bnd (Num i) v = return(Num i) • rn bnd (Var s)v = return(Var(find s bnd)) • rn bnd (Oper(x,y,z)) v = • do { x’ <- rn bnd x v • ; z’ <- rn bnd z v • ; return(Oper(x’,y,z’)) } • rn bnd (Let(s,e,b)) v = • do { n <- readSTRef v • ; writeSTRef v (n+1) • ; s’ <- return(rename n s) • ; b’ <- rn ((s,s’):bnd) b v • ; e’ <- rn bnd e v • ; return(Let(s',e',b')) }
Example Run remember runST • re x = runST(do { v <- newSTRef 0; rn [] x v }) • e3 = Let("x", Oper(Var "z",Mult,Num 2), • Let("q", Oper(Var "x",Add,Var "w"), • Oper(Var "x", Sub, • Oper(Var “q",Mult,Var"v")))) • ? re e3 • Let ("x0", Oper (Var "z",Mult,Num 2), • Let ("q1", • Oper (Var "x0",Add,Var "w"), • Oper (Var "x0", Sub, • Oper (Var "q1", • Mult, Var "v"))))
Cons 2 • Cons 2 • Cons 2 • Linked Lists • List elements linked via the state • Provides for circular structures, etc. type UpdList s a = STRef s (ListVar s a, ListVar s a) type ListVar s a = STRef s (ListElt s a) data ListElt s a = Nil | Cons a (ListVar s a) Nil (•,•)
Linked List Implementation newList :: ST s (UpdList s a) newList = do {f <- newSTRef Nil; newSTRef (f,f)} frontList :: UpdList s a -> ST s a frontList l = do {(f,b) <- readSTRef l; Cons x f' <- readSTRef f; writeSTRef l (f',b); return x} backList :: UpdList s a -> a -> ST s () backList l x = do {(f,b) <- readSTRef l; b' <- newSTRef Nil; writeSTRef b (Cons x b'); writeSTRef l (f,b')} emptyList :: UpdList s a -> ST s Bool emptyList l = do {(f,b) <- readSTRef l; return (f == b)}
When is Encapsulation Ok? • The runSToperation claims to have a pure result • We had better make sure it really is pure • Key issue - what prevents references by one use of runST being interpreted by another • Segmentation faults • Security issues • Implementation method • Functional Modeling • Pure by construction • References are re-interpreted as local addresses • Single, global, destructively-updated store — prohibit cross thread referencing • Tag references with their creator X • Use the type system
Type-based encapsulation • Want to prohibit • let v = runST (newSTRef True) in • runST (readSTRef v) • (has both escaping and imported references) • Solution • runST :: "a . ("s . ST s a) -> a • The argument to runST • Cannot depend on any free variable whose type includes s • Cannot return a value whose type includes s • so, for example, • { . . . , v:STRef s Bool} |- readSTRef v : ST s Bool • { } |- newSTRef True : ST s (STRef s Bool) • both fail • Security proof is based on parametricity
the same variable Types of Primitives • Sequencing • (>>=) :: ST s a -> (a -> ST s b) -> ST s b • Mutable variables • newSTRef :: a -> ST s (STRef s a) • readSTRef :: STRef s a -> ST s a the same variable
Equational Reasoning • Across runST • f (runST p) = runST (mapST f p) • Across the monad structure • mapST f (do { x <- p; k[x] }) = • (do { x <- p; mapST f (k[x])}) • mapST f (return x) = return (f x) • Consequence of the “naturality” (polymorphism) of >>= and return • Definition of map • mapST f p = do { x <- p; return(f x) }
Rank 2 polymorphism • The Type of runSt is a rank 2 polymorphic type • runST :: "a . ("s . ST s a) -> a • The forall is not all the way to the outside. • There are other uses of rank 2 types.