Tuesday, January 22, 2008

HAppS Experiences

Last year I was working on a series on how to use HAppS, a haskell web applications framework, to build simple webapps. I was basing that series on my experiences developing the new version of the Openomy apis, which we finally launched a few weeks ago!

HAppS is a pretty nice framework.. even though its meant to be for generic web apps, I really think that building REST apis is its sweet spot: it's really good at generating xml out of your haskell types, and the HaXml xml parsing library is one of the nicest ones I've ever used (using combinators really beats hand rolling a SAX parser or hammering out an XSD).

Unfortunately, HAppS is moving very very quickly. The posts I wrote last March are almost completely obsolete now. We ended up having to rewrite a sizable chunk of the API service when we upgraded to the much nicer HAppS 0.9.1.

Ian wrote up some more details about our experience on the Openomy blog.

Labels: , , ,

Tuesday, March 27, 2007

Building a Haskell Web Service, Part 2: The "Hello,World" Service

This is the second post in a series about implementing the next major revision of the Openomy external API as a web service written in Haskell. Instead of focusing on the problems we faced getting started as I mentioned in the last post, I figured it would be more useful to show some code instead. I'll leave the rants for some other time ;)

HAppS is a web container in Haskell. Along with HTTP, it supports receiving emails via SMTP, has libraries to make DNS requests, has code to add ACID capabilities to your arbitrary in memory data structures, and apparently they are implementing integration with Amazon S3 and EC2: very awesome. At first glance it does seem to be a slightly bloated framework -- there is a Google Summer of Code project proposal out there to implement a more lightweight web framework -- but in practice all of that extra stuff doesn't really get in your way if you don't want to use it.

My only real beef with HAppS is that the documentation could use some major updating-- it seems that the API is changing so quickly that the HAppS tutorial isn't keeping up. Thankfully, the authors of the framework all hang out on #happs on irc.freenode.org and are really helpful. The tutorial does have some working examples and the general concepts are still accurate, but some of the code samples don't even compile. Here is the working the "Hello world" web service in HAppS, that I wrote when trying to learn the framework.
From now on I'm going to assume basic knowledge of working in Haskell and that you're using HAppS 0.8.8.

HelloWorld.hs:
module Main where

import HAppS

-- define the data type you will be returning from your handlers.
-- for simplicity, we will just use a File, which is represented by
-- its filename and its id number.
data ServiceValue = File String Integer

-- a handler that just responds with a File named test.txt
getFile () () = respond $ File "test.txt" 1

-- since our web service will return XML, we need to tell HAppS
-- how to convert a ServiceValue to XML. This is done by creating
-- an instance of the ToElement typeclass. HAppS provides
-- a few XML primitives that makes generating XML pretty simple.
instance ToElement ServiceValue where
toElement (File filename fileId) = listElem "file" [] [
textElem "id" [] (show fileId)
, textElem "name" [] filename
]

-- we also dont want any stylesheets attached to our returned XML.
-- HAppS uses the ToMessage type class to convert values to
-- the actual message sent down the wire. this calls
-- the default toMessageM which knows how to use ToElement,
-- and adds other things such as the right content-type and
-- an XML prolog
instance ToMessage ServiceValue where
toMessageM = toMessageM . XML NoStyle

-- glue it all together: make the getFile function listen to
-- the GETs to the /file url. noState tells the type system and
-- HAppS that this service is stateless.
main = stdHTTP [ h ["file"] GET $ ok getFile, noState ]
The comments should give a basic overview of how it works. To compile this with ghc, run:
ghc -package HAppS -o helloWorld HelloWorld.hs
Running it starts serving our simple requests from http://localhost:8000/file.

HAppS models the handling of web requests as a list of filters, which are passed into the stdHTTP function which implements the server's main event loop. The "h" function lets you bind a particular handler to a URL and HTTP method, in this case our simple getFile function. The rest is telling HAppS how to turn your internal values into XML, and generating these values based on the requests being received. The use of type classes makes the framework extremely flexible: returning other formats is just a matter of creating your own instances of ToMessage (HAppS also includes support for JSON out of the box).

In the next post of the series, I'll show how we're using HAppS' filters to validate incoming service calls and handle user authentication.

Labels: , , ,

Monday, March 26, 2007

Building a Haskell Web Service

This is the first post in a series about implementing the next major revision of the Openomy external API as a web service written in Haskell.

One of the cool things about having a website outside of your normal job is the ability to experiment with different technologies. We've done this pretty successfully with Openomy: it started out as a monolithic C# mod_mono + mysql application that stored files on the local disk and has now evolved into a much smaller mod_mono frontend, 4 services (3 in rails, 1 in java) and backed by Amazon S3 storage.

The next part of the original application we wanted to reimplement was the handlers for the Openomy API. The current implementation was inflexible, hard to extend, and was poorly suited to our new backend infrastructure.

We chose to implement the API bridge in Haskell for a few reasons:
  1. You can model the external API as two functions: one that maps the client's input to the XML we send to our services, and another for our services return XML to our external output. This made us think that it may be fun to do it in a functional style.
  2. We were worried about library support, but it turned out that Haskell had all of the libraries we needed: a web container, a powerful and flexible way to transform XML, an http client, and simple cryptography libraries.
  3. After reading the paper on HaXml, I was really looking forward to using their combinator library to produce the XML mapping functions we needed. It seemed like a very powerful and flexible framework, without being as tedious and error-prone as working with other XML libraries in other languages.
  4. Most importantly, it would be the most interesting: I've been wanting to learn Haskell for some time now.
However, there were also a few arguments against picking Haskell:
  1. It's one of those languages that will stretch your mind, especially if you're coming from a mostly object-oriented programming background. Picking it for a major project is a large risk and may delay getting the new service out the door.
  2. Haskell is mostly a research language: the community isn't very large and we didn't know any people that had successfully used it for a mainstream application.
  3. The API bridge is heavily IO dependent (making service calls over HTTP). Haskell's monadic IO system takes a little time to get used to and understand.
About a month ago, I bought a copy of the Haskell School of Expression and started learning the language. Shortly afterwards, I convinced Ian to start learning it as well, and soon we had started. We were both a little nervous about it-- the day we started working on it, we observed that this may be the beginning of the worst software project in the years we've been working together. We've also been tracking how many times we've said "we're f*cked" during development. I'm sure we're well into the double digits by now :)

After a few initial bumps on the road, the project is moving along nicely now. The problems we had getting started will be the subject of the next post.

UPDATE: Part 2 has been posted.

Labels: , , , ,