haskell as a javascript MVC framework

It’s a bit rough around the edges, but I’ve found the mvc-todo project to be quite fun so far. It contains a coding of the todoMVC standardized app using haskell via ghcjs. The resulting todo list page looks just like all the others1, but hidden in this boring are some tiny slices of awesomeness.

The first thing to like is that the page is 99% haskell2. It’s not a web page describing haskell code, nor haskell being used to generate a page, nor even a subset or language inspired by haskell. Full-blown haskell code smashed directly into a web page for anyone to run just feels a little bit special.

More concretely, the project is an opportunity to directly compare functional and imperative style, within the context of a well defined, popular problem domain (single-page front-end MVC development).

What is an Action?

One interesting comparator is how actions are treated in functional versus imperative styles. I started the spec grind with this basic plan in mind:

  • use the mvc library which prescribes a Controller ==> Model ==> View conveyor belt.
  • listen for UI actions (Controller)
  • apply an action to the todo list state (Model)
  • render the todo list (View)

Here’s the data type for UI actions, which represents all the various ways a user can produce an effect:

data Action a
  = ClearCompleted
  | DeleteItem ItemId
  | EditItem ItemId
  | EditItemCancel ItemId
  | EditItemDone ItemId a
  | Filter (Maybe ItemStatus)
  | NewItem a
  | NoAction
  | Refresh
  | Toggle ItemId
  | ToggleAll
  deriving (Show, Eq)

newtype ItemId = ItemId { unItemId :: Int }
  deriving (Show, Eq, Ord)

data ItemStatus 
  = Active
  | Completed
  deriving (Show, Eq)

Checking in with the other frameworks I discovered that only one3 of the 68 frameworks defined an Action, because javascript doesn’t have a sum type4. In imperative land, actions tend to be functions rather than things. So saying this:

hello, callback here. An action has occurred. Please can you process this action.

is unnatural, because there is no such thing as an action.

Verbs and nouns

Sum types are oh so neglected in mainstream programming. Search for sum types in wikipedia and you’re actually redirected to tagged union5! Why the blind spot? One clue comes from this quote from the inventors of MVC:

A particularly simplistic rule of thumb in early object-oriented design was: nouns (e.g. in the requirements document) are objects, and verbs are methods. This dichotomy naturally fit the two concepts that programming languages could express.

Simplistic maybe, but also simply wrong - no linguist would ever sign up to this rigid demarcation between action words and naming words. Category theory leads us to a better definition: objects can be anything (any thing). Even action words!

Avoiding success?

Thus armed with sum types, a language can completely represent an Action covering any and all UI effects, and overall design tightens up as a consequence. In this todoMVC space:

  • the Action data type cleanly separates UI signal capture from subsequent pure signal processing.
  • UI effects (receiving events produced by the user) are naturally separate from rendering state (sending events that are consumed by the user).
  • the API becomes much cleaner. For example, apply :: Action a -> Todos a -> Todos a is the one and only state transition.
  • actions can be serialized, recorded for later analytics, and generated automatically.

All of which makes functional programming an interesting newcomer in the one-page front-end development biz. It’s going to become much harder for haskell to avoid success with this level of real world interaction coming within reach.

  1. Well, almost true. There’s an extra checkbox that turns on the QuickCheck generator. The other unique feature of haskell vis-a-vis js frameworks is the extraordinary tooling around property-based testing. But that’s a blog for another day.

  2. 1% is deducted off because the page is actually a static html file with links to static javascript files that are the haskell app compiled to javascript. A two-step process where ghcjs compiles an app, and then the app is served by a haskell executable will get that extra 1%.

  3. elm of course!

  4. The only two popular non-functional languages I can find with sum types are julia and rust.

  5. tagged union sounds useful to save memory space for those of us still running commodore 64s perhaps?

May 22, 2015
Tony Day