1 / 30

Advanced Functional Programming

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.

luther
Download Presentation

Advanced Functional Programming

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 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

  2. 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.

  3. do {x' <- eval x; y' <- eval y; return(x' + y')}

  4. do {x' <- eval x; y' <- eval y; return(x' + y')}

  5. 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?

  6. 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

  7. 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

  8. 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)

  9. 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)

  10. 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.

  11. 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?

  12. 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)))

  13. 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

  14. 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)

  15. 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'))

  16. 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"))))

  17. 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) )

  18. 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)

  19. 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

  20. 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 ?????

  21. 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 • }

  22. 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')) }

  23. 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"))))

  24. 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 (•,•)

  25. 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)}

  26. 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

  27. 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

  28. 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

  29. 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) }

  30. 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.

More Related