Hello
● Hello, I’m Phil
● I write Haskell and PureScript at Lumi
● I like building user interface libraries in PureScript:
○ React-Basic
○ Thermite
○ SDOM
○ Behaviors
○ Purview
○ React-Explore
● I also like to study category theory
Slide 3
Slide 3 text
Agenda
● An intuition for comonads
● How can we specify user interfaces?
● How can we talk about specifying user interfaces?
● Some other interesting ideas
Slide 4
Slide 4 text
Monads
Mon
Image: NASA
Slide 5
Slide 5 text
No content
Slide 6
Slide 6 text
Monads
class Functor m ⇒ Monad m where
return ∷ a → m a
join ∷ m (m a) → m a
(>>=) ∷ m a → (a → m b) → m b
(>=>) :: Monad m ⇒ (a → m b) → (b → m c) → a → m c
(f >=> g) a = f a >>= g
f >=> return = f
return >=> f = f
f >=> (g >=> h) = (f >=> g) >=> h
Slide 7
Slide 7 text
Comonads
class Functor w ⇒ Comonad w where
extract ∷ w a → a
duplicate ∷ w a → w (w a)
(=>>) ∷ w a → (w a → b) → w b
(=>=) :: Comonad m ⇒ (w a → b) → (w b → c) → w a → c
(f =>= g) w = g (w =>> f)
f =>= extract = f
extract =>= f = f
f =>= (g =>= h) = (f =>= g) =>= h
Slide 8
Slide 8 text
Examples?
Slide 9
Slide 9 text
The Store Comonad
data Store s a = Store s (s → a)
instance Comonad (Store s) where
extract (Store here go) = go here
duplicate (Store here go) =
Store here $ \there → Store there go
Slide 10
Slide 10 text
The Traced Comonad
data Traced w a = Traced (w → a)
instance Monoid w ⇒ Comonad (Traced w) where
extract (Traced f) = f mempty
duplicate (Traced f) =
Traced $ \w → Traced (f . (w ◇))
Slide 11
Slide 11 text
Intuition?
Slide 12
Slide 12 text
No content
Slide 13
Slide 13 text
More Reading
● Cofree meets Free
http://blog.sigfpe.com/2014/05/cofree-meets-free.html
● Comonads in Everyday Life
https://fmapfixreturn.wordpress.com/2008/07/09/comonads-in-everyday-life/
Slide 14
Slide 14 text
Declarative UIs
Trends in UIs:
● No more direct manipulation
● One-way data flow
● Describe what the UI state should be, not how to reach it.
Slide 15
Slide 15 text
Virtual DOM
The virtual DOM API in 4 lines:
data VDOM e
data Patch
diff ∷ VDOM e → VDOM e → Patch
apply ∷ Patch → IO Unit
(e f Eve s )
Slide 16
Slide 16 text
Components
We can build components:
data Component model = Component
{ initialState ∷ model
, render ∷ model → VDOM model
}
Slide 17
Slide 17 text
E.g. Counter
counter ∷ Component Int
counter updateState = Component
{ initialState: 0
, render = \value →
button
[onClick \_ → value + 1]
[text ("Current value = " ⧺ show value)]
}
Slide 18
Slide 18 text
A Comonad Appears
Hey, that looks like Store!
type Component model = Store model (VDOM model)
What do the Comonad functions do?
extract ∷ Component model → VDOM model
duplicate ∷ Component model → Store model Component
Slide 19
Slide 19 text
The Future is Comonadic
extract ∷ Component model → VDOM model
extract renders the component’s current state
duplicate ∷ Component model → Store model (Component model)
duplicate captures the possible future states of the component
Slide 20
Slide 20 text
Exploring the Future
How can we explore the future?
future ∷ Store model (Component model)
We need a function
explore ∷ Store model Component → Component model
Image: xkcd.com
Slide 21
Slide 21 text
Exploring the Future
To usefully implement
explore ∷ Store model Component → Component model
we can
● read the current state
● move to a new state
Which can be packaged up using the State monad
Slide 22
Slide 22 text
Exploring the Future
explore ∷ State model ()
→ Store model Component
→ Component model
explore state (Store here go) =
go there
where
(_, there) = runState state here
Slide 23
Slide 23 text
Components using State
data Component model = Component
{ initialState ∷ model
, render ∷ model
→ VDOM (State model ())
}
Slide 24
Slide 24 text
Counter using State
counter ∷ Component Int
counter updateState = Component
{ initialState: 0
, render = \value →
button
[onClick \_ → modify (\n → n + 1)]
[text ("Current value = " ⧺ show value)]
}
Slide 25
Slide 25 text
Let’s Summarize
● A component is described by
Store model (VDOM (State model ()))
● We can render a component using
extract
● We can observe possible future states of a component using
duplicate
Slide 26
Slide 26 text
Let’s Generalize!
● A component is described by
w (VDOM (m ()))
● We can render a component using
extract
● We can observe possible future states of a component using
duplicate
(Laz va ed!)
Slide 27
Slide 27 text
Pairings
We require the existence of a function
explore ∷ m () → w a → a
We can use the more general concept of
pairing ∷ m (a → b) → w a → b
Slide 28
Slide 28 text
Pairings
State s
Writer w
Reader e
Free f
Free ((,) i)
Store s (React)
Traced w (Incremental)
Env e
Cofree g (*) (Halogen)
Cofree ((→) i) (Redux, Elm)
* when f pairs with g
pairs with
Slide 29
Slide 29 text
Pairings For Free
We get a (law-abiding!) monad with a pairing for free:
(http://comonad.com/reader/2011/monads-from-comonads/)
data Co w a = Co (∀ r. w (a → r) → r)
instance Comonad w ⇒ Monad (Co w)
explore :: Co w (a → b) → w a → b
Slide 30
Slide 30 text
Let’s Summarize
● A component is described by w (VDOM (Co w ()))
● We can select the next future state using Co w ()
This is implemented in purescript-react-explore.
type Handler w = Co w ()
type Component w = w (VDOM (Handler w)))
Slide 31
Slide 31 text
So What?
● We’ve unified several common approaches to UIs under one
abstraction
● We have control over component state transitions
● We now have a language for studying the approaches themselves
○ E.g. comonad morphisms correspond to interpreters
○ E.g. every comonadic UI can be interpreted using Store (React)
● We can generalize this approach to comonads in other
categories
● We can use this intuition to find new comonads
Slide 32
Slide 32 text
Further Reading...
Slide 33
Slide 33 text
Combining Components
Slide 34
Slide 34 text
Day Convolution
Day (1970) defines the following convolution product of functors:
For example:
data Day f g a = forall x. Day (f (x → a)) (g x)
Day ((→) w) ((→) w’) a
~ exists x. (w → x → a, w’ → x)
~ w → w’ → a
~ (w, w’) → a
Slide 35
Slide 35 text
Day Convolution
Theorem
Day f g is a comonad whenever f and g are both comonads.
In fact, Day makes the comonad category into a (closed) symmetric
monoidal category.
TO : fi n u t e y
ex n is
Slide 36
Slide 36 text
Day Convolution
Day (Store s) w
Day (Traced w’) w
Day (Env e) w
StoreT s w
TracedT w’ w
EnvT e w
≅
Slide 37
Slide 37 text
Symmetric Monoidal Categories
A symmetric monoidal category ( , ⨂, I) is defined by a bifunctor
⨂ : ⨉ →
with unit I (*) which is
● Associative (*)
● Symmetric (*)
* up to isomorphism
Slide 38
Slide 38 text
Closed Symmetric Monoidal Categories
A closed symmetric monoidal category has right adjoints for each
tensoring functor
A ⨂ - : →
A ⇒ - : →
Slide 39
Slide 39 text
Closed Symmetric Monoidal Categories
We can use linear lambda calculus, the internal language of closed
symmetric monoidal categories to talk about Day convolution and
component composition.
Slide 40
Slide 40 text
Day is Closed
The internal Hom is given by
Notice that:
data Hom f g a = Hom (∀ r. f (x → r) -> g r)
Hom f Identity a ~ ∀ r. f (x → r) -> r
~ Co f a
Slide 41
Slide 41 text
move ∷ ((f ⇒ 1) ⇒ (g ⇒ 1) ⇒ (f ⨂ g ⇒ 1)) ()
move = ⟦ linear | \f† g† pair
let (f, g) = pair
() = f† f
in g† g
⟧
Monads Annihilate Comonads
To change application state, we need
E.g.
(f ⇒ 1) ()
Slide 42
Slide 42 text
Comonad Optics
We can form optics for comonads just like lenses for types:
type Optic s t a b = s ↝ a ⨂ (b ⇒ t)
day1 ∷ Optic (a ⨂ c) (b ⨂ c) a b
day2 ∷ Optic (c ⨂ a) (c ⨂ a) a b
store1 ∷ Optic (StoreT s a) (StoreT s b) a b
store2 ∷ Optic (StoreT s a) (StoreT s’ b)
(Store s) (Store s’)
(Lo k s ik L !)
Slide 43
Slide 43 text
Iterated Day Convolution
Day is our internal product type
Let’s form an internal record type!
See purescript-smash (also an implementation of extensible
coeffects)
data Smash (r ∷ # (Type -> Type)) a
Slide 44
Slide 44 text
Bits and Pieces
● Comonads are closed under certain equalizers
● There is a Sum construction for modeling UIs with multiple
optional states
○ Can also be used to construct UIs for lists