transformers handlers in disguise Nicolas Wu University of Bristol - - PowerPoint PPT Presentation

transformers
SMART_READER_LITE
LIVE PREVIEW

transformers handlers in disguise Nicolas Wu University of Bristol - - PowerPoint PPT Presentation

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


slide-1
SLIDE 1

transformers

handlers in disguise

Nicolas Wu University of Bristol nicolas.wu@bristol.ac.uk with Tom Schrijvers Tallinn, 26 November 2015

slide-2
SLIDE 2
slide-3
SLIDE 3

effect handlers

slide-4
SLIDE 4

Effect Handlers

Syntax Semantics

data Free f a = Var a | Con (f (Free f a)) data StateF s k = GetF (s → k) | PutF s (() → k) instance Functor (StateF s) where fmap f (GetF k) = GetF (f . k) fmap f (PutF s k) = PutF s (f . k)

s c a f f

  • l

d i n g s t r u c t u r e

s → (a, s)

c a r r i e r

genState :: a → (s → (a, s)) algState :: StateF s (s → (a, s)) → (s → (a, s))

g e n e r a t

  • r

a l g e b r a

handleState :: Free (StateF s) a → (s → (a, s)) handleState = handle algState genState

h a n d l e r

slide-5
SLIDE 5

Syntax

data Free f a = Var a | Con (f (Free f a)) data StateF s k = GetF (s → k) | PutF s (() → k)

get put s+1 get s’

() prog :: Free (StateF Int) Int prog = Con (GetF (\s → Con (PutF (s + 1) (\() → Con (GetF (\s' → Var s')))))) s s’

slide-6
SLIDE 6

Syntax

data Free f a = Var a | Con (f (Free f a))

get put s+1 get s’

() prog :: Free (StateF Int) Int prog = Con (GetF (\s → Con (PutF (s + 1) (\() → Con (GetF (\s' → Var s')))))) s s’ data StateF s k = GetF (s → k) | PutF s (() → k)

slide-7
SLIDE 7

Syntax

data Free f a = Var a | Con (f (Free f a))

get put s+1 get s’

() prog :: Free (StateF Int) Int prog = Con $ GetF $ \s → Con $ PutF (s + 1) $ \() → Con $ GetF $ \s' → Var s' s s’ data StateF s k = GetF (s → k) | PutF s (() → k)

slide-8
SLIDE 8

Syntax

data Free f a = Var a | Con (f (Free f a))

get put s+1 get s’

() 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’ data StateF s k = GetF (s → k) | PutF s (() → k)

slide-9
SLIDE 9

Syntax

data Free f a = Var a | Con (f (Free f a)) data StateF s k = GetF (s → k) | PutF s (() → k)

get put s+1 get s’

() 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’

slide-10
SLIDE 10

Syntax

data Free f a = Var a | Con (f (Free f a)) data StateF s k = GetF (s → k) | PutF s (() → k)

get put s+1 get s’

() 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’

w e w a n t t

  • g

i v e a s e m a n t i c s t

  • t

h i s c

  • d

e

slide-11
SLIDE 11

Semantics

prog :: Int → (Int, Int) prog = con $ GetF $ \s → con $ PutF (s + 1) $ \() → con $ GetF $ \s' → var s' where con = algState var = genState genState :: a → (s → (a, s)) genState x = \s → (x, s) 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) algState :: StateF s (s → (a, s)) → (s → (a, s)) algState (GetF k) = \s → k s s algState (PutF s' k) = \s → k () s'

w e w a n t t

  • g

i v e a s e m a n t i c s t

  • t

h i s c

  • d

e

slide-12
SLIDE 12

Semantics

prog :: Int → (Int, Int) prog = con $ GetF $ \s → con $ PutF (s + 1) $ \() → con $ GetF $ \s' → var s' where con = algState var = genState genState :: a → (s → (a, s)) genState x = \s → (x, s) 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) algState :: StateF s (s → (a, s)) → (s → (a, s)) algState (GetF k) = \s → k s s algState (PutF s' k) = \s → k () s'

t

  • d
  • s
  • ,

t h e s c a f f

  • l

d i n g b e c

  • m

e s a n a l g e b r a a n d g e n e r a t

  • r

a n d t h e t y p e b e c

  • m

e s t h e c a r r i e r w e w a n t t

  • g

i v e a s e m a n t i c s t

  • t

h i s c

  • d

e

slide-13
SLIDE 13

Effect Handlers

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)

prog :: Int → (Int, Int) prog = con $ GetF $ \s → con $ PutF (s + 1) $ \() → con $ GetF $ \s' → var s' where con = algState var = genState prog :: Free (StateF Int) Int prog = con $ GetF $ \s → con $ PutF (s + 1) $ \() → con $ GetF $ \s' → var s' where con = Con var = Var

handle algState genState

t h e s e m a n t i c s i s g i v e n b y a f

  • l

d

slide-14
SLIDE 14

Effect Handlers

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)

prog :: Int → (Int, Int) prog = con $ GetF $ \s → con $ PutF (s + 1) $ \() → con $ GetF $ \s' → var s' where con = algState var = genState prog :: Free (StateF Int) Int prog = con $ GetF $ \s → con $ PutF (s + 1) $ \() → con $ GetF $ \s' → var s' where con = Con var = Var

handle algState genState

t h e s e m a n t i c s i s g i v e n b y a f

  • l

d

slide-15
SLIDE 15

prog :: Int → (Int, Int) prog = con $ GetF $ \s → con $ PutF (s + 1) $ \() → con $ GetF $ \s' → var s' where con = algState var = genState

Effect Handlers

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)

prog :: Free (StateF Int) Int prog = con $ GetF $ \s → con $ PutF (s + 1) $ \() → con $ GetF $ \s' → var s' where con = Con var = Var

handle algState genState

2 . w r a p u p t h e s e m a n t i c s 1 . c l e a n u p t h e s y n t a x t h e s e m a n t i c s i s g i v e n b y a f

  • l

d h

  • w

m i g h t w e i m p r

  • v

e t h i s ?

slide-16
SLIDE 16

Enter the monad

1 . c l e a n u p t h e s y n t a x

slide-17
SLIDE 17

Monads

class Monad m where return :: m a () :: m a → (a → m b) → m b

monads are a standard way of encoding sequential operations typically we use bind to say that one action must be performed before another 1 . c l e a n u p t h e s y n t a x

slide-18
SLIDE 18

Free Monad

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 1 . c l e a n u p t h e s y n t a x

slide-19
SLIDE 19

Syntax

get put s+1 get s’

() prog :: Free (StateF Int) Int prog = Con $ GetF $ \s → Con $ PutF (s + 1) $ \() → Con $ GetF $ \s' → Var s' s s’

1 . c l e a n u p t h e s y n t a x this is a monolithic piece of code:

slide-20
SLIDE 20

Syntax

get put s+1 get s’

() prog :: Free (StateF Int) Int prog = Con (GetF Var) \s → Con (PutF (s + 1) Var) \() → Con (GetF Var) \s' → Var s' s s’

the free monad allows us to turn it into smaller pieces of code that compose together to make a whole 1 . c l e a n u p t h e s y n t a x

slide-21
SLIDE 21

Syntax

get put s+1 get s’

() prog :: Free (StateF Int) Int prog = do s ← Con (GetF Var) Con (PutF (s + 1) Var) s' ← Con (GetF Var) Var s' s s’

since it’s monadic, we can use Haskell’s do notation to make the syntax look nicer 1 . c l e a n u p t h e s y n t a x

slide-22
SLIDE 22

Syntax

get put s+1 get s’

() prog :: Free (StateF Int) Int prog = do s ← get put (s + 1) s' ← get return s' s 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 1 . c l e a n u p t h e s y n t a x

slide-23
SLIDE 23

monadic semantics

2 . w r a p u p t h e s e m a n t i c s

slide-24
SLIDE 24

State

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')) newtype State s a = State { runState :: s → (a, s) }

2 . w r a p u p t h e s e m a n t i c s the carrier can be wrapped in a newtype

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

we know that this happens to be a monad it also helps to create a specification (with laws) around the functionality this monad brings

slide-25
SLIDE 25

State

genState :: a → State s a genState = return algState :: StateF s (State s a) → State s a algState (GetF k) = get k algState (PutF s' k) = put s' k

the definition of a state handler becomes easy

genState :: a → (s → (a, s)) genState x = \s → (x, s) algState :: StateF s (s → (a, s)) → (s → (a, s)) algState (GetF k) = \s → k s s algState (PutF s' k) = \s → k () s'

2 . w r a p u p t h e s e m a n t i c s

newtype State s a = State { runState :: s → (a, s) } State

slide-26
SLIDE 26

State

prog :: State Int Int prog = do s ← get put (s + 1) s' ← get return s'

the syntax for our program is the same

prog :: Free (StateF Int) Int prog = do s ← get put (s + 1) s' ← get return s' prog :: MonadState Int m m Int prog = do s ← get put (s + 1) s' ← get return s'

we can bring these into a common framework*

*we’ve had to bend the rules since the handler put and get do not satisfy the laws

monadic style effect handler style

slide-27
SLIDE 27

Effect classes

slide-28
SLIDE 28

Classy Data

data StateS s a where Get :: StateS s s Put :: s → StateS s ()

the signature can be encoded with a GADT:

class Monad m MonadState s m | m → s where get :: m s put :: s → m ()

The monadic specification for State is: and we can tie the syntax to a monadic semantics:

class Monad m MonadEff f m | m → f where eff :: f a → m a instance MonadEff (StateS s) (State s) where eff (Get) = get eff (Put s) = put s

  • Q. how does this relate to handlers?

can we abstract?

Plotkin & Power call this a generic effect

slide-29
SLIDE 29

GADTs vs Syntax

data StateS s a where Get :: StateS s s Put :: s → StateS s () data StateF s k where GetF :: (s → k) → StateF s k PutF :: s → (() → k) → StateF s k

there are some similarities between the two forms of syntax but one problem is that StateS s is not always functorial!

  • Q. can we somehow force it to have functor structure?
  • A. Yes, we Kan!

effect handlers use a functor: the type class induces a GADT:

slide-30
SLIDE 30

CoYoneda*

data CoYoneda f r = forall a . CoYoneda (f a) (a → r) instance Functor (CoYoneda f) where fmap f (CoYoneda op k) = CoYoneda op (f . k)

*left Kan extension along Id

data StateF s k where GetF :: (s → k) → StateF s k PutF :: s → (() → k) → StateF s k instance MonadState s (Free (CoYoneda (StateS s))) where get = Con (CoYoneda Get Var) put s' = Con (CoYoneda (Put s') Var) CoYoneda Get :: (s → k) → CoYoneda (StateS s) k CoYoneda (Put s') :: s → (() → k) → CoYoneda (StateS s) k

the CoYoneda construction adds functorial structure for free

the secret is to store the

  • utgoing continuation

now we recover constructors that are essentially the same

  • Q. how does this relate to handlers?
slide-31
SLIDE 31

Monad Homomorphisms

algCY :: MonadEff f m CoYoneda f (m a) → m a algCY (CoYoneda op k) = eff op k

the handler is now trivial! in fact, what we have here is a monad homomorphism

handleCY :: MonadEff f m Free (CoYoneda f) a → m a handleCY = handle algCY return

slide-32
SLIDE 32

Composition

slide-33
SLIDE 33

Compositional Handlers

data (f + g) a = Inl (f a) | Inr (g a)

Free (f + g) a → Free g b → c

Ideally, we’d like something a bit like this: in practice, this is too simple for an arbitrary f and g So far, we’ve shown how handlers relate to monads, things get interesting when we consider handlers over composed effects

slide-34
SLIDE 34

Compositional Handlers

handleState2 :: forall a s g . Functor g Free (StateF s + g) a → s → Free g (s, a) handleState2 = handle algState2 varState2 where algState2 :: ((StateF s) + g) (s → Free g (s, a)) → s → Free g (s, a) algState2 (Inl (GetF k)) s = k s s algState2 (Inl (PutF s' k)) s = k () s' algState2 (Inr op) s = Con (fmap ($ s) op) varState2 :: a → s → Free g (s, a) varState2 a s = return (s, a) handleExcState :: Free (StateF s + ExcF) a → s → Maybe (s, a) handleExcState p s = handleExc (handleState2 p s)

for State, we need to augment the carrier significantly

(s → Free g (s, a)) this handler generates a second tree wrapped in structure

can this be simplified?

slide-35
SLIDE 35

transformers

slide-36
SLIDE 36

State Transformer

newtype StateT s m a = StateT { runStateT :: s → m (a, s) } instance Monad m MonadState s (StateT s m) where get = StateT (\ s → return (s, s)) put s = StateT (\ _ → return ((), s)) class MonadTrans t where lift :: Monad m m a → t m a instance MonadTrans (StateT s) where lift m = StateT $ \ s → do a ← m return (a, s) instance Monad m MonadEff (StateS s) (StateT s m) where eff (Get) = get eff (Put s) = put s

h m m , t h i s t y p e l

  • k

s f a m i l i a r

slide-37
SLIDE 37

Semantics Transformers

handleT :: (MonadTrans t, MonadEff f (t (Free g)), Functor g) Free (CoYoneda f + g) a → t (Free g) a handleT (Var x) = return x handleT (Con (Inl (CoYoneda x k))) = eff x handleT . k handleT (Con (Inr op)) = join (lift (inj (fmap handleT op))) inj :: Functor f f a → Free f a inj = Con . fmap Var

slide-38
SLIDE 38

Chaining Transformers

class HFunctor h where hmap :: (Functor f, Functor g) (forall a . f a → g a) → (forall a . h f a → h g a)

this is a transformer stack, but where we work to a specification

handleT :: (MonadTrans t, MonadEff f (t (Free g)), Functor g) Free (CoYoneda f + g) a → t (Free g) a handle2 :: ( HFunctor t, MonadTrans t , MonadEff f (t (Free (CoYoneda g))) , MonadEff g m ) Free (CoYoneda f + CoYoneda g) a → t m a handle2 = hmap handleCY . handleT

how can we compose these handlers?

slide-39
SLIDE 39

Chaining Transformers

handleT3 :: (Functor g, Functor (t2 (t1 (Free g))), HFunctor t2, HFunctor t3, MonadTrans t3, MonadTrans t1, MonadTrans t2, MonadEff f1 (t1 (Free g)), MonadEff f2 (t2 (Free (CoYoneda f1 + g))), MonadEff f3 (t3 (Free (CoYoneda f2 + (CoYoneda f1 + g))))) Free (CoYoneda f3 + (CoYoneda f2 + (CoYoneda f1 + g))) a → t3 (t2 (t1 (Free g))) a

Gahh!

So what does a stack of size 3 look like?

handleT3 = hmap (hmap handleT) . hmap handleT . handleT

actually, it’s really not that bad: we generally have parametricity in m

instance Monad m MonadEff (StateS s) (StateT s m) where eff (Get) = get eff (Put s) = put s

What’s the type?

slide-40
SLIDE 40

conclusion

slide-41
SLIDE 41
slide-42
SLIDE 42