Upload
john-de-goes
View
1.479
Download
0
Embed Size (px)
Citation preview
Halogen: Past, Present, FutureJohn A. De Goes — @jdegoes
Agenda• Functional Frontend: SlamData• FRP & React
• Common Elements• FRP in a Type• React in a Type• The 100k Problem• Turtles
• Halogen: Introduction• Halogen: Past• Halogen: Present• Halogen: Future• Conclusion
Functional Frontend: SlamData• Visual analytics for NoSQL
• Analytic workflows• Data exploration• Data visualization
• 100% PureScript• 248 modules• Largest known PureScript project in the world• Currently five full-time developers
Data VisualizationWe'll get back to that.
FRP & ReactCommon Elementsdata HTML i = Text String | Element TagName (A.Attribute i) (Array (HTML i))
data HTML = Text String | Element TagName A.Attribute (Array HTML)
FRP & ReactFRP in a Typedata Signal a = Signal (Time -> a)
instance applicativeSignal :: ...
myApp :: Signal HTMLmyApp = ...
FRP & ReactReact in a Typedata React s m i = React { render :: s -> HTML i, update :: i -> s -> m s }
myApp :: React MyState EffectMonad MyEventmyApp = ...
The 100k ProblemLook Closely...Signal HTML
s -> HTML a
Types necessarily imply a potentially massive, in-memory HTML structure that can neither be created nor updated incrementally.
The 100k ProblemDiffingdiff :: HTML -> HTML -> HTMLPatch
Diffing only helps with the DOM updates, nothing else!
The 100k ProblemData VisualizationNeither React nor FRP offer a performant means of incrementally visualizing large data sets. Rendering or even storing that much data is prohibitive.
Halogen: PastHistory• Popular, production-ready UI library for PureScript• Commissioned by SlamData
• Blank-slate design originally architected by Phil Freeman• Powers the SlamData application
Halogen: PastSignal Functionsdata HTML i = ...
newtype SF i o = SF (i -> SF1 i o)
newtype SF1 i o = SF1 { result :: o, next :: SF i o }
type UI i = SF1 i (HTML i)
runUI :: forall i eff. UI i -> Eff (HalogenEffects eff) Node
Halogen: PastIf You Squint...type Function i o = i -> o
type UI i = Cofree (Function i) (HTML i)
Halogen: PresentView + DSLtype Component s f g = { view :: s -> HTML (f Unit), eval :: forall a. f a -> (HalogenDSL s g) a }
Grossly simplified. :)
Halogen: PresentIn Practice• Strongly-typed component-driven design• Structure of entire app is encoded in type (!!!)
• ...And therefore static (pros & cons)• Types get complex, but reasoning is level-by-level• Hard to get compiling, but then usually works
Halogen: FutureNext-Generation Goals• Built on a foundation of incremental computation• Turtles all the way down — no magic• Native expressivity — no need for escape hatch• Unify web components with ordinary HTML
"components" (elements)• Simplify types a little? :)
Halogen: FutureIncremental React???data React s m i = React { render :: s -> HTML i, update :: i -> s -> m s }
data DReact s ds m i = DReact { render :: s -> ds -> ΔHTML i, effect :: i -> s -> m ds, update :: s -> ds -> s }
Halogen: FutureSimplifydata UI s p i ds = UI { render :: s -> p i ds, -- Push "effects" here! update :: s -> ds -> s } -- Monoid action!
• Rendering produces a machine that reads is and produces a state change.
• Updating produces a new state given an old state and a state change.
Halogen: FutureProfunctor Algebrasdata File a b = ReadByte (a -> Byte) | WriteByte Byte (Unit -> b)
Halogen: FutureProfunctor Algebras Contravariant |data File a b = ReadByte (a -> Byte) | WriteByte Byte (Unit -> b)
Halogen: FutureProfunctor Algebras Covariant |data File a b = ReadByte (a -> Byte) | WriteByte Byte (Unit -> b)
Halogen: FutureProfunctor Algebrasdata FreePro p a b -- :: (* -> * -> *) -> * -> * -> *
A computation in p that reads 0-to-many a's and produces a single b.
• Functor, Apply, Applicative, Monad (if desired)• Profunctor
Halogen: FutureProfunctor Algebrasdata File a b = ReadByte (a -> Byte) | WriteByte Byte (Unit -> b)
readByte :: FreePro File Byte BytereadByte = liftFP $ ReadByte id
writeByte :: forall a. Byte -> FreePro a UnitwriteByte b = liftFP $ WriteByte b id
Halogen: FutureProfunctor Algebrasdata DOM i o = ListenEvent Id EventType (i -> Event) | AppendChild Id (Id -> o) | ...
Halogen: FutureProfunctor Algebrasdata UI s p i ds = UI { render :: s -> p i ds, update :: s -> ds -> s }
type FreeDOM a b = FreePro DOM a b
type Component s ds = UI s FreeDOM DOMEvent ds
Halogen: FutureComponentsdata TextDiff = Insert Int String | Delete Int Int
textField :: Component String TextDiff
*Can be more polymorphic!
Halogen: FutureLens-ishdata Nest ds' s' ds s = Nest (PrismP ds' ds) (LensP s' s)
The nesting of a smaller state differential inside a larger one.
Halogen: FutureCombinatorsembed :: forall ds' s' ds s. Nest ds' s' ds s -> Components ds' s' -> Components ds s
siblings :: forall s ds. Component s ds -> Component s ds -> Component s dschild :: forall s ds. Component s ds -> Component s ds -> Component s ds
infix 5 siblings as <~>infix 6 child as </>
Halogen: FutureExampledata FormDiff = Email TextDiff | Password TextDiff
type FormState = { email :: String, password :: String }
webForm :: Component FormState FormDiffwebForm = div </> embed _Email (label "Email" <~> textField) <~> div </> embed _Password (label "Password" <~> passwordField)
_Email :: Nest String TextDiff FormState FormDiff_Password :: Nest String TextDiff FormState FormDiff
Conclusion• Practical requirements suggest an incremental theory of UI• FRP and React are problematic• A coinductive, profunctor-based approach looks promising
• But some details yet to be worked out...• Halogen 1.0 is coming, & you can help!
THANK YOU!John A. De Goes — @jdegoes