From Declarative UIs to Comonadic UIs

672ba495b3eadfdb0daee60ed354bdff?s=47 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)

672ba495b3eadfdb0daee60ed354bdff?s=128

wuct

July 14, 2018
Tweet

Transcript

  1. From Declarative UIs to Comonadic UIs CT Wu FEDC 2018

    wu_ct wuct
  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 }) !=> <div> { showBanana !&& <div className="banana" !/>} { showApple !&& <div className="apple" !/>} !</div>
  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?
  4. An abstraction over all declarative UI approaches

  5. Declarative Imperative abstract abstract

  6. None
  7. Declarative Imperative Comonadic abstract abstract

  8. Declarative UIs are about “what to render” { showBanana: true,

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

    showApple: true } { showBanana: false, showApple: false }
  10. Comonads as Spaces

  11. A State of an UI is a Point

  12. A Comonad is a Space of All Possible Points

  13. An User Action is a Movement Inside This Space

  14. Different Spaces are Different Declarative UI Approaches!

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

  16. 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)
  17. 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
  18. 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
  19. 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
  20. 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
  21. (Simplified) Comonad class Comonad w where extract !:: ∀ a.

    w a → a duplicate !:: ∀ a. w a → w (w a) extract duplicate
  22. 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
  23. 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
  24. 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)
  25. 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)
  26. 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
  27. 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'))
  28. 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
  29. 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 }
  30. 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
  31. 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')
  32. 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 }
  33. 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
  34. Other Examples • The Moore comonad is similar to Elm/Redux/ReasonReact.

    • The Cofree comonad is similar to Halogen. • The Sum comonad is for branching.
  35. 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!
  36. 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
  37. Learn once, apply everywhere. wu_ct wuct