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

Incremental Programming in PureScript

Incremental Programming in PureScript


Phil Freeman

April 10, 2018


  1. purescript-incremental Phil Freeman

  2. Why? We want to avoid recomputing things unnecessarily

  3. Why? SELECT MIN(cost), MAX(cost) FROM products WHERE type = ‘Rubber

  4. Why? SELECT SUM(orders.total) FROM orders JOIN customer ON customers.id =

    orders.customer_id WHERE customer.state = ‘CA’;
  5. Idea Instead, we can try to compute changes in outputs

    from changes in inputs.
  6. None
  7. Incremental Lambda Calculus Extend types with change structures Change structures

    are monoids of changes which act on their carrier types (transitively).
  8. In Brief class Patch a da ⇐ Diff a da

    | a → da where diff :: a → a → da instance Diff Unit Unit instance (Diff a da, Diff a db) ⇒ Patch (Diff a b) (Diff da db) instance (Diff a da, Diff a db) ⇒ Diff (a → b) (a → da → db) instance Diff a da ⇒ Diff (Bag a) (Bag da) class Monoid da ⇐ Patch a da | a → da where patch :: a → da → a instance Patch Unit Unit instance (Patch a da, Patch a db) ⇒ Patch (Tuple a b) (Tuple da db) instance (Patch a da, Patch a db) ⇒ Patch (a → b) (a → da → db) instance Patch a da ⇒ Patch (Bag a) (Bag da)
  9. A Theory of Changes for Higher-Order Languages Interpret each type

    as a type and a change structure Construct a source-to-source transformation to interpret terms
  10. purescript-incremental Defines an embedded DSL for incremental programs • Uses

    higher-order abstract syntax • Inspired by automatic differentiation
  11. purescript-incremental unjet :: (Jet a da → Jet b db)

    → Tuple (a → b) (a → da → db) unjet jf = Tuple f df where f a = b where Jet b _ = jf (Jet a mempty) df a da = db where Jet _ db = jf (Jet a da) jet :: (a → b) → (a → da → db) → Jet a da → Jet b db jet f df (Jet a da) = Jet (f a) (df a da) data Jet a da = Jet a da
  12. Embedding We require that patch (f₀ a) db = f₀

    (patch a da) whenever Jet _ db = f (Jet a da) Tuple f₀ _ = unjet f “Constant” jet functions also satisfy snd (f (Jet a mempty)) = mempty Represent a function a → b by f :: Jet a da → Jet b db
  13. Example map :: (Jet a da -> Jet b db)

    → Jet (IMap k a) _ → Jet (IMap k b) _ filter :: (Jet a da -> Jet Boolean _) → Jet (IMap k a) _ → Jet (IMap k a) _ join :: Jet (IMap k a) _ → Jet (IMap k b) _ → Jet (IMap k (Tuple a b)) _ Embedded incremental relational algebra! data IMap k v data MapChange v dv = Add v | Remove | Update dv type MapChanges k v dv = Map k (Array (MapChange v dv)) instance Patch v dv ⇒ Patch (IMap k v) (MapChanges k v dv)
  14. Incremental DOM newtype ViewChanges eff = ViewChanges { text ::

    Last String , attrs :: MapChanges String (Atomic String) (Last String) , handlers :: MapChanges String (Atomic (EventListener eff)) (Last (EventListener eff)) , kids :: Array (ArrayChange (View eff) (ViewChanges eff)) } Translate model changes directly into DOM updates. See purescript-purview. newtype View eff = View { element :: String , text :: Atomic String , attrs :: IMap String (Atomic String) , handlers :: IMap String (Atomic (EventListener eff)) , kids :: IArray (View eff) }
  15. Stuff to think about • Some similar DOM libraries ◦

    paf31/purescript-sdom ◦ i-am-tom/purescript-panda • Implement this in the database • Incremental parsing • Incremental canvas graphics Questions?