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
Agenda ● An intuition for comonads ● How can we specify user interfaces? ● How can we talk about specifying user interfaces? ● Some other interesting ideas
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
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
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
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 ◇))
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/
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
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
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
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
Exploring the Future explore ∷ State model () → Store model Component → Component model explore state (Store here go) = go there where (_, there) = runState state here
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
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!)
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
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
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)))
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
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
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
Symmetric Monoidal Categories A symmetric monoidal category ( , ⨂, I) is defined by a bifunctor ⨂ : ⨉ → with unit I (*) which is ● Associative (*) ● Symmetric (*) * up to isomorphism
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.
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 !)
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
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