A Brief And Partial Review Of Haskell In The Browser

Ah, the browser. Like iOS, it's a place of pleasure and pain. Love the audience it reaches; hate the default language. So I have a keen interest in the rise of "Compiles to JavaScript" languages. ClojureScript is my current weapon of choice, and I'm very happy with it. But I've also spent a chunk of time researching Haskell-like options for the browser, and I thought I'd write up my experiences.

Before we begin, for context, here is:

What I'm Looking For In A Browser Language

Sane language design

That rules out JavaScript, CoffeeScript, TypeScript, ScalaJS and several others.

Separation of logic & rendering

If your onClick handler references business-logic functions directly, something is wrong.

No manual DOM manipulation

Are you still doing that? Stop it. Unless you enjoy manual memory management too. (In which case good luck to you, my fine retronaught.)

No callbacks

Our brains are happiest with a single line of control flow. Yes you can get comfortable with callback-passing, but it never sits as easily as not having to. And like DOM manipulation, you don't have to anymore. It's a job for the language, not for you. Save your brain.

The Players

GHCJS

GHCJS reuses the GHC compiler we all know & love, but modifies it to emit JavaScript. The result is extremely impressive. You really do get a full Haskell experience. The language features are the same, the compiler messages the same, even most stuff on Hackage just works. It's good.

I've heard people complain about the size of the JS files it produces. Apparently in the early days it was particularly bad, but to me, now, it seems perfectly acceptable. Once a production webapp will minify & gzip to less than the size of an animated cat gif, it's time to stop yapping about file sizes.

There were three downsides with GHCJS for me. The first is, how should you structure a full client-side app? I did some work on this with Ben Ford, and then he took it much further, but it's still very much an area of research. I couldn't say to anyone, "This is the way to build it; you'll need these pieces."

The second problem is that GHCi isn't supported yet. Without that, I found incremental build times to be just a bit too long, even for a small app.

And the third problem, and for me the nail in the coffin, is it's a real pain to install. If you thought cabal hell was hell, I can tell you there's another layer of problems lying in wait. Honestly, I couldn't even say to anyone, "You should try GHCJS." Getting started is asking too much.

All these problems will be addressed, and quickly I expect. Simply because of the quality of compiler, GHCJS is the one to watch. But I've put it aside for now.

Haste

If not GHCJS, how about another, "Let's make regular GHC emit JavaScript!" approach? That's what Haste does.

I liked Haste initially, until I got to that absolutely necessary evil, JavaScript interop, and everything fell apart. I recall I wanted to share a simple list of integers between Haste and JavaScript, and I couldn't do it. The official position seems to be, "Yes, it ought to be easy. If you have a patch I'll merge it."

If Haste needs serious expertise to pass [1, 2, 3], then it's not for me.

PureScript

PureScript moves us away from GHC and into languages that are styled-after Haskell. PureScript seems to be winning a popular vote (in this ultra-niche), and I can see why. The language has the headline features from Haskell, interop is good, it all works, and the documentation is excellent.

There are a few downsides for me that eventually stacked up too high. Tooling's a bit of a pain. (You'll need Node. And Grunt. And Bower. Wait, are we actually building a JavaScript project?)

The compiler errors are...disappointing. Haskell development leans heavily on fast, high-quality feedback from the type system. If you squint, the GHC compiler is almost a REPL. From what I gather, GHC has taken a vast amount of engineering effort to get to where it is, so you can't blame PureScript for lagging behind. But lag it does, and when PureScript spits out a type error, I feel very much alone.

And finally, the "how do we structure/how do we render?" question felt unanswered. There is a virtual DOM library, but it was scrappy. I didn't have an Om-quality experience using it. I'm sure it will get better, but like GHCJS it feels like an untrodden path.

Bonus Tip: If you're playing with PureScript, Take a look at Bodil Stokke's Pulp for your building needs. That worked well for me.

Elm

Elm is nice. Very nice. The language is good. Control flow is sane. The documentation is expansive (though more scattered about than PureScript). Incremental compilation times are pleasing. The compiler feedback is good. (No GHC, but better than PureScript.) Interop is so easy it makes you wonder why interop is hard.

The tooling's excellent, even before you get into the fancy stuff like the time-travelling debugger. You can just write some code, compile it in seconds, get a single page you can upload straight to S3, and your website's running. And the path from that to a proper minified, production app is smooth enough.

I've heard people say Elm is great for game-like things, but little attention has been given to regular HTML apps. That's not true. The elm-html library is, give or take, a perfect substitute for Om/React.

And the FRP approach may prove a bit of a learning curve for some, but I found it very natural. There's a certain way that I've been structuring my ClojureScript apps, and Elm feels like it's taking the same route, but has baked it into the language. I heartily approve.

On the downside, I do really wish it had typeclasses. And not even for shiny things like putting my monoids in the category of endofunctors. Just for simple stuff. Having to qualify List.map, Signal.map and Set.map gets old very quickly. And Json decoding would be much simpler if it supported return-type polymorphism.

Conclusion

As you can probably guess, for now I've settled on Elm. I'm building an MVP for a startup of my own at the moment, and the front end is Elm. I'll let you know how it goes.

Oh, and I'll be keeping my eye on GHCJS...

Coda

Last week I went to an Elm night at Twitter HQ and Tom Ashworth was speaking. I asked him if he thought Elm was ready for production. He said no. I wish I'd also asked if he thought JavaScript was ready for production... :-)