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

From Declarative UIs to Comonadic UIs

wuct
July 14, 2018

From Declarative UIs to Comonadic UIs

Because of the increasing complexity of developing user interfaces (UI), declarative UI approaches have become more and more popular. However, we have lacked tools to compare and analyze different declarative approaches/frameworks/architectures or find a new one, until Phil Freeman showed comonads are what we need.

In this talk, I will start with declarative UIs, through PureScript, to introduce comonadic UIs and explain why they are fun and helpful.

The video is here: https://youtu.be/iEbCGdXwWDU (Chinese)

wuct

July 14, 2018
Tweet

More Decks by wuct

Other Decks in Programming

Transcript

  1. From Declarative UIs to
    Comonadic UIs
    CT Wu

    FEDC 2018
    wu_ct
    wuct

    View full-size slide

  2. Declarative UIs Abstract Out Imperative Detail
    const render = ({ showBanana, showApple }) !=> {
    if (showBanana) {
    $('.banana').show()
    } else {
    $('.banana').hide()
    }
    if (showApple) {
    $('.apple').show()
    } else {
    $('.apple').hide()
    }
    }
    const render = ({ showBanana, showApple }) !=>

    { showBanana !&& }
    { showApple !&& }
    !

    View full-size slide

  3. • How do we compare different declarative UI approaches?

    • How do we discover a new one?

    • Is there a way to “learn once, apply everywhere”?

    • Is there an universal approach?

    View full-size slide

  4. An abstraction over
    all declarative UI approaches

    View full-size slide

  5. Declarative
    Imperative
    abstract abstract

    View full-size slide

  6. Declarative
    Imperative Comonadic
    abstract abstract

    View full-size slide

  7. Declarative UIs are about “what to render”
    {
    showBanana: true,
    showApple: true
    }
    {
    showBanana: false,
    showApple: false
    }

    View full-size slide

  8. But how do we move among states?
    {
    showBanana: true,
    showApple: true
    }
    {
    showBanana: false,
    showApple: false
    }

    View full-size slide

  9. Comonads as Spaces

    View full-size slide

  10. A State of an UI is a Point

    View full-size slide

  11. A Comonad is a Space of All Possible Points

    View full-size slide

  12. An User Action is a Movement Inside This Space

    View full-size slide

  13. Different Spaces are Different Declarative UI
    Approaches!

    View full-size slide

  14. Let’s See Some (Simplified)
    Code!

    View full-size slide

  15. Type Signature Notation in PureScript
    “::” is for type annotations
    "PureScript" !:: String
    New types can be created via type constructors
    ["PureScript"] !:: Array String
    ![["Pure"], ["Script"!]] !:: Array (Array String)

    View full-size slide

  16. Type Signature Notation in PureScript
    → is an infix type constructor for functions
    String → Number
    A function with multiple → is a curried function
    String → String → String

    View full-size slide

  17. Type Signature Notation in PureScript
    ∀ (forall) is the universal quantifier and lowercase letters
    stand for type variables
    ∀ a. a → a
    㱺 expresses constraints on type variables
    ∀ a. Monoid a ⇒ a !-> a !-> a

    View full-size slide

  18. Type Signature Notation in PureScript
    We can also add constraints to type constructors
    ∀ f a. Functor f ⇒ f a
    “class” is for typeclasses
    class Functor f where
    map !:: ∀ a b. (a !-> b) !-> f a !-> f b

    View full-size slide

  19. Type Signature Notation in PureScript
    “data” is for types
    data Tuple a b = Tuple a b
    data Either a b = Left a | Right b
    “instance” is for typeclass instances
    instance Functor (Tuple a) where
    map f (Tuple a b) = Tuple a (f b)
    “type” is for type alias
    type Name = String
    type Audience = Array Name

    View full-size slide

  20. (Simplified) Comonad
    class Comonad w where
    extract !:: ∀ a. w a → a
    duplicate !:: ∀ a. w a → w (w a)
    extract
    duplicate

    View full-size slide

  21. A Comonad is a Dual of a Monad
    extract :: ∀ w a. w a ! a
    pure :: ∀ m a. a ! m a
    extend :: ∀ w a b. (w a ! b) ! w a ! w b
    bindFlipped :: ∀ m a b. (a ! m b) ! m a ! m b
    duplicate :: ∀ w a. w a ! w (w a)
    join :: ∀ m a. m (m a) ! m a

    View full-size slide

  22. Pairing is Just a Type Alias for
    A Higher Order Function
    type Pairing f g =
    ∀ a b c. (a → b → c) → f a → g b → c

    View full-size slide

  23. Move in a Comonadic Space by Pairing
    move !:: ∀ w m a b
    . Comonad w
    !=> Pairing m w → m a → w b → w b
    move pairing movement space =
    pairing (\_ newspace → newspace) movement (duplicate space)
    pairing !:: ∀ a b c. (a → b → c) → f a → g b → c
    duplicate !:: ∀ w a. w a → w (w a)

    View full-size slide

  24. Comonadic User Interfaces
    a is the type describing the user interface (ex: JSX)
    m is the functor to move in the comonad space
    (m Unit → Effect Unit) is the handler function (ex: onClick)
    type UI m a = (m Unit → Effect Unit) → a
    type Component w m a = w (UI m a)

    View full-size slide

  25. Explore Comonadic Spaces
    To explore a comonad component means:
    1. Initialize the UI with the initial state
    2. Waiting for user actions which updating the state
    3. Update the UI accordingly
    explore !:: ∀ w m a base
    . Comonad w
    !=> Component w m a → base Unit

    View full-size slide

  26. Traced Pairs With Writer
    data Writer a b = Writer (Tuple a b)
    data Traced a b = Traced (a → b)
    instance (Monoid a) !=> Comonad (Traced a) where
    extract !:: ∀ b. Traced a b → b
    extract (Traced f) = f mempty
    duplicate !:: ∀ b. Traced a b → Traced a (Traced a b)
    duplicate (Traced f) = Traced \t → (Traced \t' → f (t !<> t'))

    View full-size slide

  27. Writer Traced Pairing
    type Pairing f g = ∀ a b c. (a → b → c) → f a → g b → c
    writerTraced !:: ∀ a. Pairing (Writer a) (Traced a)
    writerTraced f (Writer (Tuple a w)) (Traced wb) =
    let b = wb w
    in f a b

    View full-size slide

  28. Use purescript-react-basic as an Example
    explore !:: ∀ w m
    . Comonad w
    !=> Component w m → ReactComponent {}
    explore component = react
    { initialState: { space: component }
    , render: \props state setState !->
    let
    dispatch movement = setState \s !-> {
    space: move writerTraced movement s.space
    }
    in extract state.space dispatch
    }

    View full-size slide

  29. Our First Comonadic UI Component
    tracedComponent !:: Component (Traced (Additive Int)) (Writer (Additive Int))
    tracedComponent = Traced render where
    render !:: Additive Int → UI (Writer (Additive Int))
    render (Additive count) dispatch =
    R.button
    { onClick: dispatch (Writer (Tuple (Additive 1) Unit))
    , children: [ R.text ("Increment: " !<> show count) ]
    }
    tracedReactComponent !:: ReactComponent {}
    tracedReactComponent = explore writerTraced tracedComponent

    View full-size slide

  30. A More General Case
    data State s a = State (s !-> Tuple a s)
    data Store s a = Store (Tuple (s !-> a) s)
    stateStore !:: ∀ s. Pairing (State s) (Store s)
    stateStore f (State sas) (Store (Tuple sb s)) =
    let Tuple a s' = sas s
    in f a (sb s')

    View full-size slide

  31. Explore Any Comonad With a Pairing
    explore !:: ∀ w m
    . Comonad w
    !=> Pairing m w !-> Component w m → ReactComponent {}
    explore pairing component = react
    { initialState: { space: component }
    , render: \props state setState !->
    let
    dispatch movement = setState \s !-> {
    space: move pairing movement s.space
    }
    in extract state.space dispatch
    }

    View full-size slide

  32. Store Comonad Component
    storeComponent !:: Component (Store Int) (State Int)
    storeComponent = Store (Tuple render 0) where
    render !:: Int !-> UI (State Int)
    render count dispatch =
    R.button
    { onClick: dispatch (State (\s !-> Tuple (s + 1) Unit)
    , children: [ R.text ("Increment: " !<> show count) ]
    }
    storeReactComponent !:: ReactComponent {}
    storeReactComponent = explore stateStore storeComponent

    View full-size slide

  33. Other Examples
    • The Moore comonad is similar to Elm/Redux/ReasonReact.

    • The Cofree comonad is similar to Halogen.

    • The Sum comonad is for branching.

    View full-size slide

  34. Q & A
    • How can this thing be useful?

    • It’s fun to know it!

    • Study existing declarative UI approaches.

    • Find new declarative UI approaches.

    • How to compose components?

    • Use monad transformers and comonad transformers.

    • Can I use it in JavaScript?

    • Yes, all needed algebras are in Fantasy Land Specification.

    • But it’s hard to find a monad-comonad pairing, isn’t it?

    • Cofree gives comonads from functors for free, and Co gives monads paired with given comonads for free!

    View full-size slide

  35. References
    • Freeman, P., 2017, Declarative UIs Are The Future - And the Future Is
    Comonadic!, https://github.com/paf31/the-future-is-comonadic/blob/
    master/docs/main.pdf

    • Xavier, A., 2017, Comonads for user interfaces, https://github.com/
    arthurxavierx/purescript-comonad-ui-todos/blob/master/
    ComonadsForUIs.pdf

    • The code in this slide https://github.com/wuct/purescript-comonad-ui-
    examples

    View full-size slide

  36. Learn once, apply everywhere.
    wu_ct
    wuct

    View full-size slide