transformers handlers in disguise Nicolas Wu University of Bristol nicolas.wu@bristol.ac.uk with Tom Schrijvers Tallinn, 26 November 2015
effect handlers
Effect Handlers Syntax Semantics s c a f f o l d i n g c a r r i e r s �→ (a, s) data Free f a = Var a h a n d l e r | Con (f (Free f a)) handleState :: Free (StateF s) a s t r u c t u r e �→ (s �→ (a, s)) data StateF s k handleState = = GetF (s �→ k) handle algState genState | PutF s (() �→ k) n e r a t o r g e instance Functor (StateF s) where genState :: a �→ (s �→ (a, s)) fmap f (GetF k) = GetF (f . k) fmap f (PutF s k) = PutF s (f . k) a l g e b r a algState :: StateF s (s �→ (a, s)) �→ (s �→ (a, s))
Syntax data Free f a data StateF s k = Var a = GetF (s �→ k) | Con (f (Free f a)) | PutF s (() �→ k) prog :: Free ( StateF Int ) Int prog = Con (GetF (\s �→ Con (PutF (s + 1) (\() �→ Con (GetF (\s' �→ Var s')))))) s () s’ put get get s’ s+1
Syntax data Free f a data StateF s k = Var a = GetF (s �→ k) | Con (f (Free f a)) | PutF s (() �→ k) prog :: Free ( StateF Int ) Int prog = Con (GetF (\s �→ Con (PutF (s + 1) (\() �→ Con (GetF (\s' �→ Var s')))))) s () s’ put get get s’ s+1
Syntax data Free f a data StateF s k = Var a = GetF (s �→ k) | Con (f (Free f a)) | PutF s (() �→ k) prog :: Free ( StateF Int ) Int prog = Con $ GetF $ \s �→ Con $ PutF (s + 1) $ \() �→ Con $ GetF $ \s' �→ Var s' s () s’ put get get s’ s+1
Syntax data Free f a data StateF s k = Var a = GetF (s �→ k) | Con (f (Free f a)) | PutF s (() �→ k) prog :: Free ( StateF Int ) Int prog = con $ GetF $ \s �→ con $ PutF (s + 1) $ \() �→ con $ GetF $ \s' �→ var s' where con = Con var = Var s () s’ put get get s’ s+1
Syntax data Free f a data StateF s k = Var a = GetF (s �→ k) | Con (f (Free f a)) | PutF s (() �→ k) prog :: Free ( StateF Int ) Int prog = con $ GetF $ \s �→ con $ PutF (s + 1) $ \() �→ con $ GetF $ \s' �→ var s' where con = Con var = Var s () s’ put get get s’ s+1
Syntax data Free f a data StateF s k = Var a = GetF (s �→ k) | Con (f (Free f a)) | PutF s (() �→ k) prog :: Free ( StateF Int ) Int v e a w e w a n t t o g i prog = con $ GetF $ \s �→ i s c o d e s e m a n t i c s t o t h con $ PutF (s + 1) $ \() �→ con $ GetF $ \s' �→ var s' where con = Con var = Var s () s’ put get get s’ s+1
Semantics genState :: a �→ (s �→ (a, s)) algState :: StateF s (s �→ (a, s)) genState x = \s �→ (x, s) �→ (s �→ (a, s)) algState (GetF k) = \s �→ k s s algState (PutF s' k) = \s �→ k () s' prog :: Int �→ (Int, Int) v e a w e w a n t t o g i prog = con $ GetF $ \s �→ i s c o d e s e m a n t i c s t o t h con $ PutF (s + 1) $ \() �→ con $ GetF $ \s' �→ var s' where con = algState var = genState handle :: Functor f �� (f b �→ b) �→ (a �→ b) �→ Free f a �→ b handle alg gen (Var x) = gen x handle alg gen (Con op) = alg (fmap (handle alg gen) op)
Semantics genState :: a �→ (s �→ (a, s)) algState :: StateF s (s �→ (a, s)) genState x = \s �→ (x, s) �→ (s �→ (a, s)) algState (GetF k) = \s �→ k s s algState (PutF s' k) = \s �→ k () s' prog :: Int �→ (Int, Int) v e a w e w a n t t o g i prog = con $ GetF $ \s �→ i s c o d e s e m a n t i c s t o t h con $ PutF (s + 1) $ \() �→ con $ GetF $ \s' �→ var s' where e c o m e s t o d o s o , t h e s c a f f o l d i n g b con = algState a t o r a n a l g e b r a a n d g e n e r c a r r i e r var = genState a n d t h e t y p e b e c o m e s t h e handle :: Functor f �� (f b �→ b) �→ (a �→ b) �→ Free f a �→ b handle alg gen (Var x) = gen x handle alg gen (Con op) = alg (fmap (handle alg gen) op)
Effect Handlers prog :: Free (StateF Int) Int prog :: Int �→ (Int, Int) prog = con $ GetF $ \s �→ prog = con $ GetF $ \s �→ con $ PutF (s + 1) $ \() �→ con $ PutF (s + 1) $ \() �→ con $ GetF $ \s' �→ con $ GetF $ \s' �→ var s' var s' where where con = Con con = algState var = Var var = genState handle algState genState e n b y a f o l d t h e s e m a n t i c s i s g i v handle :: Functor f �� (f b �→ b) �→ (a �→ b) �→ Free f a �→ b handle alg gen (Var x) = gen x handle alg gen (Con op) = alg (fmap (handle alg gen) op)
Effect Handlers prog :: Free (StateF Int) Int prog :: Int �→ (Int, Int) prog = con $ GetF $ \s �→ prog = con $ GetF $ \s �→ con $ PutF (s + 1) $ \() �→ con $ PutF (s + 1) $ \() �→ con $ GetF $ \s' �→ con $ GetF $ \s' �→ var s' var s' where where con = Con con = algState var = Var var = genState handle algState genState e n b y a f o l d t h e s e m a n t i c s i s g i v handle :: Functor f �� (f b �→ b) �→ (a �→ b) �→ Free f a �→ b handle alg gen (Var x) = gen x handle alg gen (Con op) = alg (fmap (handle alg gen) op)
Effect Handlers g h t w e i m p r o v e t h i s ? p u p t h e s e m a n t i c s h o w m i 2 . w r a prog :: Free (StateF Int) Int prog :: Int �→ (Int, Int) prog = con $ GetF $ \s �→ prog = con $ GetF $ \s �→ con $ PutF (s + 1) $ \() �→ con $ PutF (s + 1) $ \() �→ con $ GetF $ \s' �→ con $ GetF $ \s' �→ var s' var s' l e a n u p t h e s y n t a x 1 . c where where con = Con con = algState var = Var var = genState handle algState genState e n b y a f o l d t h e s e m a n t i c s i s g i v handle :: Functor f �� (f b �→ b) �→ (a �→ b) �→ Free f a �→ b handle alg gen (Var x) = gen x handle alg gen (Con op) = alg (fmap (handle alg gen) op)
p t h e s y n t a x 1 . c l e a n u Enter the monad
Monads p t h e s y n t a x 1 . c l e a n u monads are a standard way of encoding sequential operations class Monad m where return :: m a ( ��� ) :: m a �→ (a �→ m b) �→ m b typically we use bind to say that one action must be performed before another
Free Monad p t h e s y n t a x 1 . c l e a n u data Free f a = Var a | Con (f (Free f a)) instance Functor f �� Monad (Free f) where return = Var Var x ��� k = k x Con op ��� k = Con (fmap ( ��� k) op) the bind for the free monad is used to graft syntax trees into variables
Syntax p t h e s y n t a x 1 . c l e a n u this is a monolithic piece of code: prog :: Free (StateF Int) Int prog = Con $ GetF $ \s �→ Con $ PutF (s + 1) $ \() �→ Con $ GetF $ \s' �→ Var s' s () s’ put get get s’ s+1
Syntax p t h e s y n t a x 1 . c l e a n u prog :: Free (StateF Int) Int prog = Con (GetF Var) ��� \s �→ Con (PutF (s + 1) Var) ��� \() �→ Con (GetF Var) ��� \s' �→ Var s' the free monad allows us to turn it into smaller pieces of code that compose together to make a whole s () s’ put get get s’ s+1
Syntax p t h e s y n t a x 1 . c l e a n u prog :: Free (StateF Int) Int prog = do s �← Con (GetF Var) Con (PutF (s + 1) Var) s' �← Con (GetF Var) Var s' since it’s monadic, we can use Haskell’s do notation to make the syntax look nicer s () s’ put get get s’ s+1
Syntax p t h e s y n t a x 1 . c l e a n u prog :: Free (StateF Int) Int prog = do s �← get put (s + 1) s' �← get return s' where put s = Con (PutF s Var) get = Con (GetF Var) finally, we can create smart constructors to hide away some of the mess s () s’ put get get s’ s+1
r a p u p t h e s e m a n t i c s 2 . w monadic semantics
State r a p u p t h e s e m a n t i c s 2 . w the carrier can be wrapped in a newtype newtype State s a = State { runState :: s �→ (a, s) } we know that this happens to be a monad instance Monad (State s) where return x = State (\s �→ (x, s)) State mx ��� f = State (\s �→ let (a, s') = mx s in runState (f a) s') it also helps to create a specification (with laws) around the functionality this monad brings class Monad m �� MonadState s m | m �→ s where get :: m s put :: s �→ m () instance MonadState s (State s) where get = State (\s �→ (s , s )) put s' = State (const ((), s'))
Recommend
More recommend