Tuesday, April 10, 2012

A Hopefully Fair and Useful Comparison of Haskell Web Frameworks

Recently there has been a lot of discussion and questions about the differences between the big three Haskell web frameworks: Happstack, Yesod, and Snap. Different pieces of the answer have been discussed in a number of places. In this post, I'd like to try to give a more complete comparison. Hopefully it will be relatively unbiased, but without being so watered down that it is useless. I've succeeded if you can't tell which framework I'm a major contributor to based solely on the text of this post.

Happstack

First, up is Happstack, the oldest of the three with original commit history as early as February, 2005. Happstack used to be called HAppS. It was primarily composed of a web server based on lazy IO, a fairly radical (for the time) in-memory persistence approach for storing and versioning native Haskell data types, and a library called IxSet for making it easier to use this state system with relational data access patterns. I've heard a number of people describe HAppS (and Happstack) as being synonymous with this unconventional state system, but today the state system has been completely rewritten and rebranded as a standalone persistence system called acid-state.

Of the three frameworks, Happstack is probably the least monolithic and exists mostly as a loose collection of libraries that can be mixed and matched to suit one's needs. A quick search for "happstack" on hackage will reveal a large number of libraries providing a diverse range of functionality. This includes support for at least four template systems: Hamlet, Heist, HSP, and StringTemplate. The main Happstack maintainer also maintains a type-safe URL called web-routes which, if carefully used, provides compile-time guarantees that your site won't have broken internal links.

While Happstack includes support for the widest range of choices, its most visible members do seem to favor a few in particular. Based on the current Happstack Crash Course, I would guess that the canonical libraries[1] for Happstack probably include BlazeHtml and HSX/HSP for templating, web-routes for routing, and acid-state for persistence. Acid-state and its all-haskell persistence philosophy combine with the type-safe routing and templates to create a set of libraries for web development where Haskell's expressive type system is king.

Yesod

The next of the big three to hit the scene was Yesod, which released in March of 2010. While Happstack (and later Snap) applications only worked as standalone binaries that were their own web server, Yesod provided FastCGI support which enabled Yesod applications to run on shared hosting where running a standalone web server was not an option. Perhaps Yesod's defining characteristic is its strong emphasis on static type checking for all aspects of the web app enabled by a number of custom DSLs used in different areas. In January 2011, the Yesod team released a new modern web server called Warp sporting the fastest benchmark numbers of any Haskell web server, a title still held today.

Yesod's type safety starts with the Hamlet compile time template language with syntax derived from HAML, a markup language that is lighter than (X)HTML. Hamlet's syntax makes it really simple to put dynamic data in templates while remaining strongly typed. This fits really well with Yesod's type-safe routing DSL that generates links for you. The paradigm is very similar to using Happstack with HSX/HSP and web-routes, but with a custom DSL that is more compact. Yesod has taken things a step further though with their Shakespearean template system which they have used to create Cassius and Julius, two strongly typed compile-time DSLs for generating CSS and Javascript.

In working with this paradigm, Yesod developers observed that some amount of boilerplate code was usually required to accomplish all this type safety. Leveraging the power of TemplateHaskell and quasiquoting, Yesod provides small domain-specific languages with syntax customized to each unique purpose that let you specify your site with very little unnecessary cruft.

Additionally, Yesod created a generalized database library called persistent, which uses these same design patterns to create a DSL for specifying your site's data model and lets you easily use MySQL, PostgreSQL, SQLite, or MongoDB to store your data and interact with it in a type safe manner.

This week Yesod released 1.0 milestone, communicating that they have reached a point where they will be maintaining longer-term API stability and support.

Snap

Snap is the youngest of the three frameworks. It went public in May of 2010, two months after Yesod. Its initial release featured a template system called Heist, and the first web server implementation to use the new modern concept of left-fold enumerators, a technique for achieving more predictable space usage than lazy IO. The web server API provides a set of combinators that provides a flexible framework for creating both simple and complex routes. This API is essentially a slimmed down and simplified version of the API that Happstack has been using since the early days of HAppS, and as such it should be fairly straightforward to port applications and libraries between the two.

The Heist template system differs from Hamlet and HSP in that it reads templates at runtime rather than compile-time, essentially thinking of templates as data rather than code. This means that it can't do truly type-safe URLs like the other two, but it enforces a much stronger division between code and view. It also makes it possible for templates to be reloaded without recompiling the application. Aside from the fact that it allows you to define your own tags dynamically, Heist's template syntax is valid HTML.

More recently, Snap released a component system called Snaplets which is designed to allow web apps to be built from self-contained modular components. The Snaplets API provides a composable way to handle things like built-in filesystem resources and installation, config file support, and in-memory state. Since the release of the Snaplets API, the Snap team and third-party developers have released a number of snaplets providing support for sessions, authentication, database integration, etc. Yesod provides something called subsites that looks similar, and based on Happstack's roadmap it sounds like they will be working on another take on this problem in the future.

Snap's canonical libraries appear to be just Snap and Heist. The Snap web server is actually split out into two separate libraries snap-core and snap-server while the Snaplet API is the umbrella project. Other than that, the Snap team hasn't pledged allegiance to any particular database library so you should be free to use anything.

Conclusion

Each of the three frameworks has their own unique perspective. They make different tradeoffs, and have different senses of code aesthetic. There's the tradeoff of static, type-safe, and determined at compile-time versus less type-safe, more flexible, and determined at run-time. There's also the tradeoff of concise custom DSLs versus expressive and powerful combinator libraries. Do you go with a generalized database library that provides unified and necessarily watered-down interface to a number of different backends or tie yourself to a single specific database and leverage its full power? These are not always binary choices either. Each of these tradeoffs may be an axis on which there are several or a continuum of valid points to choose from.

Keep in mind that in most cases, different components of the above frameworks can be mixed and matched to suit your needs. Hopefully this post paints a clearer picture of the landscape and helps you find the framework (or combination of frameworks) that fits your needs and preferences best.

[1] When I use the term "canonical libraries" in this article I'm talking about what I infer is probably the preference used by the main developers maintaining each web framework, and this is likely to be the direction the framework as a whole moves in with a more holistic solution if it chooses to do so.

WASH is continuation based. but unfortunately it is no longuer maintained. the entire navigation can be expressed in a single WASH procedure.

MFlow is a new Haskell Web framework. It is also stateful. So state handling is straight forward. It uses an extensión of formlets that admit embedded formatting. formlets can have callbacks (called 'actions'). Links are also formlets that return values to the calling flow, as a result not only each page is safe, but the entire navigation is. formlets form widgets that have their own server handling code and can be combined. This example show three pages in a loop. The back button can be used: