Halogen is a popular choice for building front-end user-interfaces with PureScript. Often described as a purely functional version of React, Halogen allows building user-interfaces by composing declarative, self-contained components, including effectful components those built from third-party Javascript libraries.
In this presentation, John presents a high-level summary of where Halogen has come from, how it works right now, and what are the main drawbacks to both FRP and React. John then suggests that incremental computation should be the foundation for the next major version of Halogen, and sketches out a possible way of achieving that in a declarative fashion.
2. 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
3. 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
5. FRP & React
Common Elements
data HTML i
= Text String
| Element TagName (A.Attribute i) (Array (HTML i))
data HTML
= Text String
| Element TagName A.Attribute (Array HTML)
6. FRP & React
FRP in a Type
data Signal a = Signal (Time -> a)
instance applicativeSignal :: ...
myApp :: Signal HTML
myApp = ...
7. FRP & React
React in a Type
data React s m i
= React { render :: s -> HTML i,
update :: i -> s -> m s }
myApp :: React MyState EffectMonad MyEvent
myApp = ...
8. The 100k Problem
Look Closely...
Signal HTML
s -> HTML a
Types necessarily imply a potentially massive, in-memory HTML
structure that can neither be created nor updated incrementally.
10. The 100k Problem
Data Visualization
Neither React nor FRP offer a performant means of incrementally
visualizing large data sets. Rendering or even storing that much data is
prohibitive.
11. Halogen: Past
History
• Popular, production-ready UI library for PureScript
• Commissioned by SlamData
• Blank-slate design originally architected by Phil Freeman
• Powers the SlamData application
12. Halogen: Past
Signal Functions
data 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
13. Halogen: Past
If You Squint...
type Function i o = i -> o
type UI i = Cofree (Function i) (HTML i)
14. Halogen: Present
View + DSL
type Component s f g =
{ view :: s -> HTML (f Unit),
eval :: forall a. f a -> (HalogenDSL s g) a }
Grossly simplified. :)
15. Halogen: Present
In 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
16. Halogen: Future
Next-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? :)
17. Halogen: Future
Incremental 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 }
18. Halogen: Future
Simplify
data 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.
22. Halogen: Future
Profunctor Algebras
data 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
23. Halogen: Future
Profunctor Algebras
data File a b
= ReadByte (a -> Byte)
| WriteByte Byte (Unit -> b)
readByte :: FreePro File Byte Byte
readByte = liftFP $ ReadByte id
writeByte :: forall a. Byte -> FreePro a Unit
writeByte b = liftFP $ WriteByte b id
25. Halogen: Future
Profunctor Algebras
data 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
27. Halogen: Future
Lens-ish
data Nest ds' s' ds s = Nest (PrismP ds' ds) (LensP s' s)
The nesting of a smaller state differential inside a larger one.
28. Halogen: Future
Combinators
embed :: 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 ds
child :: forall s ds. Component s ds -> Component s ds -> Component s ds
infix 5 siblings as <~>
infix 6 child as </>
30. 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!