$30 off During Our Annual Pro Sale. View Details »

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. Phil Freeman
    COMONADIC!
    The e Is
    coday.today

    View Slide

  2. 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

    View Slide

  3. Agenda
    ● An intuition for comonads
    ● How can we specify user interfaces?
    ● How can we talk about specifying user interfaces?
    ● Some other interesting ideas

    View Slide

  4. Monads
    Mon
    Image: NASA

    View Slide

  5. View Slide

  6. 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

    View Slide

  7. 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

    View Slide

  8. Examples?

    View Slide

  9. 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

    View Slide

  10. 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 ◇))

    View Slide

  11. Intuition?

    View Slide

  12. View Slide

  13. 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/

    View Slide

  14. 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.

    View Slide

  15. 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 )

    View Slide

  16. Components
    We can build components:
    data Component model = Component
    { initialState ∷ model
    , render ∷ model → VDOM model
    }

    View Slide

  17. E.g. Counter
    counter ∷ Component Int
    counter updateState = Component
    { initialState: 0
    , render = \value →
    button
    [onClick \_ → value + 1]
    [text ("Current value = " ⧺ show value)]
    }

    View Slide

  18. 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

    View Slide

  19. 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

    View Slide

  20. 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

    View Slide

  21. 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

    View Slide

  22. Exploring the Future
    explore ∷ State model ()
    → Store model Component
    → Component model
    explore state (Store here go) =
    go there
    where
    (_, there) = runState state here

    View Slide

  23. Components using State
    data Component model = Component
    { initialState ∷ model
    , render ∷ model
    → VDOM (State model ())
    }

    View Slide

  24. Counter using State
    counter ∷ Component Int
    counter updateState = Component
    { initialState: 0
    , render = \value →
    button
    [onClick \_ → modify (\n → n + 1)]
    [text ("Current value = " ⧺ show value)]
    }

    View Slide

  25. 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

    View Slide

  26. 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!)

    View Slide

  27. 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

    View Slide

  28. 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

    View Slide

  29. 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

    View Slide

  30. 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)))

    View Slide

  31. 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

    View Slide

  32. Further Reading...

    View Slide

  33. Combining Components

    View Slide

  34. 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

    View Slide

  35. 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

    View Slide

  36. Day Convolution
    Day (Store s) w
    Day (Traced w’) w
    Day (Env e) w
    StoreT s w
    TracedT w’ w
    EnvT e w

    View Slide

  37. Symmetric Monoidal Categories
    A symmetric monoidal category ( , ⨂, I) is defined by a bifunctor
    ⨂ : ⨉ →
    with unit I (*) which is
    ● Associative (*)
    ● Symmetric (*)
    * up to isomorphism

    View Slide

  38. Closed Symmetric Monoidal Categories
    A closed symmetric monoidal category has right adjoints for each
    tensoring functor
    A ⨂ - : →
    A ⇒ - : →

    View Slide

  39. 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.

    View Slide

  40. 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

    View Slide

  41. 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) ()

    View Slide

  42. 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 !)

    View Slide

  43. 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

    View Slide

  44. 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

    View Slide

  45. Questions?

    View Slide