Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save ryanartecona/1debe8d171ab9708d988714a440b1801 to your computer and use it in GitHub Desktop.
Save ryanartecona/1debe8d171ab9708d988714a440b1801 to your computer and use it in GitHub Desktop.

Purescript Halogen: Past, Present, and Future

Talk by John De Goes

Halogen is a Purescript UI framework in the spirit of React (but doesn’t wrap React) with the goal of expressing UIs in a declarative and type-safe way.

FRP & React

FRP (in typed languages) typically needs “reactive” data types to be parameterized over the type of the things they “produce” so the FRP machinery can control how values of that type get passed around. In the more React-y style, events are supported somehow internally by the main UI data type (so don’t need to be parameterized in that way).

FRP, in essence, is a bunch of kit around functions from a point in time to the state of a program at that time. FRP has nice semantics and some certain special properties, but it’s hard to implement efficiently without giving up those properties or complicating things for the user.

React, in essence, is a pair of a render function, which knows how to render a full HTML view given a description of the app’s state at a point in time, and an update function, which knows how to incorporate the update events that one of those views can produce into the app’s state, and produce a new state (potentially & usually with monadic effects).

data React s m i
  = React { render :: s -> HTML i,
            update :: i -> s -> m s }

myApp :: React MyState EffectMonad MyEvents
myApp = ...

The 100k Problem

Neither FRP nor React have good stories for incrementally visualizing large data sets (or apps with deep/broad DOM structures of user-interactive elements). The FRP model involves a huge graph of closures. React’s model relies on diffing the whole DOM (or virtual DOM) on each update, which gets expensive. Complex React components usually work around React’s own machinery (memoization, avoiding unnecessary diffs, etc.), which is an indicator that the fundamentals aren’t quite right.

Halogen

A UI framework for Purescript, commissioned by SlamData-the-company for SlamData-the-app from Phil Freeman, creator of Purescript. It was a clean-slate design for something specific to Purescript, and has become very popular (for Purescript lol).

First version of Halogen was built around signal functions, which kinda work like generators from JS or Python (a function which produces a pair of a value and a continuation which will produce the next value and next continuation, etc.).

Current Halogen is much simpler, and looks very much like the React model above. This has been an improvement, but still has some drawbacks. The structure of the entire app is encoded in a top-level type, which means you have to use aliases heavily and error messages are verbose and less helpful. You have to fight the compiler a lot because the types are so large and cumbersome, but once it compiles, it works well. Even though the types underneath are large, you can reason about the types for each component in isolation, which is key.

Next-gen Halogen

Goals

(personal goals of John, which are close to but not necessarily the goals of the whole OSS project)

  • Incremental computation
  • Composability, wihtout magic underneath - “turtles all the way down”
  • Expressivity - no need for an escape hatch
  • Unify web components with plain HTML elements
  • Simpler types

ΔReact

What if we didn’t rely on full-tree DOM diffing at every update to derive per-update patches to apply to the DOM, and instead computed the patches directly off of the events the app throws off?

-- 'ds' for 'delta state'
-- 'p' for 'profunctor'
-- i is the type of events
data UI i p ds s
  = UI { render :: s -> p i ds,
         update :: s -> ds -> s }

This opens up a bunch of new architectural possibilities! Components can be aliases over this UI, so that a component can be simply parameterized by the type s of the state it shows in a view and the type ds of the type of ‘delta state’s or updates it produces. With a bit of lens machinery, these components can be embedded and composed with each other by defining helpers which embed and compose their respective view state type and delta state types. The examples here from the presentation are too long to type but very compelling!

Another driving factor is that this new profunctor-based React-like structure is, in theory, conducive to being introspected, analyzed, and compiled down to something very efficient in a way that’s not possible with the past or current Halogen models. This new model aims not to be able to render a full view from scratch given a full description of app state at a point in time, but instead will be able to start from some initial app state and DOM state, and from there handle state updates in such a way that the DOM is updated when the app state is updated, and both are kept locked in sync. This also means Halogen should be able to drop the virtual-dom.js dependency.

@corporatepiyush
Copy link

Reflex already does by using GHCjs underneath. Good to see if PureScript community can have such a library too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment