ezyang / reflex-backpack

Reflex with Backpack
8 stars 0 forks source link

Reflex with Backpack

This package provides an implementation of Reflex specialized to Spider using Backpack, demonstrating how Backpack can be built on top of an existing, type-class based library without requiring any source modifications.

Building

Architecture

There are a few ways we could go about Backpack'ing Reflex, and this particular iteration has two goals:

  1. Use the existing Reflex library without modification. It should only be necessary to copy code when we want it to be specialized; any code which is not parametric over the Reflex backend should be reused from Reflex.

  2. Remove the performance tax of having a Reflex typeclass. All invocations of methods from Reflex should go directly to the actual implementation; we should be as fast as the "specialize Reflex to Spider" mode that the regular Reflex library can be run as.

The general idea is to replace the Reflex and ReflexHost type classes with Backpack signatures representing them, Reflex.Sig and Reflex.Host.Sig (respectively). We have two internal libraries: indef, which represents code that only depends on the Reflex interface, and host, which represnets code that also depends on ReflexHost (we split them because the Pure Reflex implementation does not support ReflexHost.)

Let's take a side-by-side look at the Reflex type class, and the corresponding Reflex.Sig signature:

-- Type class
class ( MonadHold t (PushM t)
      , MonadSample t (PullM t)
      , MonadFix (PushM t)
      , Functor (Dynamic t)
      , Applicative (Dynamic t)
      , Monad (Dynamic t)
      ) => Reflex t where
  data Behavior t :: * -> *
  data Event t :: * -> *
  data Dynamic t :: * -> *
  data Incremental t :: * -> *
  type PushM t :: * -> *
  type PullM t :: * -> *
  never :: Event t a
  ...

-- Signature
class HasTimeline t

data Impl t

type Event          t = C.Event         (Impl t)
type Dynamic        t = C.Dynamic       (Impl t)
type Behavior       t = C.Behavior      (Impl t)
type Incremental    t = C.Incremental   (Impl t)
type EventSelector  t = C.EventSelector (Impl t)
data PushM t a
data PullM t a

instance HasTimeline t => Functor       (Dynamic t)
instance HasTimeline t => Applicative   (Dynamic t)
instance HasTimeline t => Monad         (Dynamic t)
instance HasTimeline t => MonadSample   (Impl t) (PullM t)
instance HasTimeline t => MonadHold     (Impl t) (PushM t)
instance HasTimeline t => MonadSample   (Impl t) (PushM t)
instance HasTimeline t => MonadFix      (PushM t)
instance HasTimeline t => Monad         (PushM t)
instance HasTimeline t => Applicative   (PushM t)
instance HasTimeline t => Functor       (PushM t)
instance HasTimeline t => Monad         (PullM t)
instance HasTimeline t => Applicative   (PullM t)
instance HasTimeline t => Functor       (PullM t)

never :: HasTimeline t => Event t a
...

The Reflex type class is quite complicated, so let's step through all of its features:

We have a number of options for how to go about converting this into Backpack, but in the end I went with the following scheme: