Upgrade to Pro — share decks privately, control downloads, hide ads and more …

The Future Is Comonadic!

The Future Is Comonadic!

My talk about comonadic UIs, from May Day-Convoluted Weekend.

Phil Freeman

May 27, 2018
Tweet

More Decks by Phil Freeman

Other Decks in Programming

Transcript

  1. 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
  2. Agenda • An intuition for comonads • How can we

    specify user interfaces? • How can we talk about specifying user interfaces? • Some other interesting ideas
  3. 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
  4. 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
  5. 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
  6. 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 ◇))
  7. 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/
  8. 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.
  9. 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 )
  10. Components We can build components: data Component model = Component

    { initialState ∷ model , render ∷ model → VDOM model }
  11. E.g. Counter counter ∷ Component Int counter updateState = Component

    { initialState: 0 , render = \value → button [onClick \_ → value + 1] [text ("Current value = " ⧺ show value)] }
  12. 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
  13. 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
  14. 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
  15. 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
  16. Exploring the Future explore ∷ State model () → Store

    model Component → Component model explore state (Store here go) = go there where (_, there) = runState state here
  17. Components using State data Component model = Component { initialState

    ∷ model , render ∷ model → VDOM (State model ()) }
  18. Counter using State counter ∷ Component Int counter updateState =

    Component { initialState: 0 , render = \value → button [onClick \_ → modify (\n → n + 1)] [text ("Current value = " ⧺ show value)] }
  19. 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
  20. 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!)
  21. 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
  22. 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
  23. 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
  24. 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)))
  25. 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
  26. 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
  27. 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
  28. Day Convolution Day (Store s) w Day (Traced w’) w

    Day (Env e) w StoreT s w TracedT w’ w EnvT e w ≅
  29. Symmetric Monoidal Categories A symmetric monoidal category ( , ⨂,

    I) is defined by a bifunctor ⨂ : ⨉ → with unit I (*) which is • Associative (*) • Symmetric (*) * up to isomorphism
  30. Closed Symmetric Monoidal Categories A closed symmetric monoidal category has

    right adjoints for each tensoring functor A ⨂ - : → A ⇒ - : →
  31. 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.
  32. 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
  33. 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) ()
  34. 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 !)
  35. 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
  36. 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