Haskell and Snap … yet another cabal adventure

I wanted to try out some of Haskells web-frameworks for quite some time now. The problem so far was always the huge pile of things those come bundeled with (template haskell, conduit, comonads, etc.) and I somehow wanted to understand all this first.

To make this short: NO I do not understand all this but I think I will never really be ready to try it if I wait for me to turn genius overnight (guess will never happen) – so I just hold my breath and jump right into it!

but say … which one?

That leaves me with the choice of the framework. After looking a bit at the online tutorials I wanted to go for either snap or yesod.
But on first glance (well …) snap seemed to be a bit easier to get startet.
Having said that I think I will try yesod too someday soon.

Installation Odyssee

First try: Windows

I work mainly on Windows and so of course I tried to get this working in windows. I even can remember that I tried to install snap some time ago and it seemed to work … but …
Well after

 cabal-dev install snap 

I soon (well it took quite some time to compile all this) got the dreaded “failed to install because of …” message …

About an hour (with yet another complete reinstall of Haskell-Plattform and various test with different versions) later I finally contacted the snap guys on their IRC channel and after describing the problems the final verdict was like this:

we have no Windows developer on our team … this is about the help you can expect

To be honest: the guy was quite helpful and it seems that the problem is in some crypto-package and not with snap but I’m quite disappointed anyway.

Anyhow: forget Windows for now …

Ubuntu to the rescue….

Luckily I have quite a nice Ubuntu-VM installed on my machine (where I can just do a simple snapshot-restore to reset my cabal-hell) and so I went there next.
Here the installation was quite simple (that is

cabal-dev install snap

) worked just fine.

TIP OF THE DAY

I did not really want to do this installation for each project I am going to try but on Linux (have to try this on Windows as well) you can add a symbolic link to the created cabal-dev folder in each new project you are going to make (do not forget to place a symbolic link to the snap executable in cabal-dev/bin somewhere your PATH is pointing – or add this folder to your path.
This works quite nice for me and I do not soil my global or user cabal repository.

first toy project

For my first project I want to implement a basic calculator on a webpage.
Finally it should work with AJAX/jQuery or even do all it’s calculation in the browser using Fay.

First Iteration – getting something up

I worked through the two basic tutorials on the snap homepage: hello world and snaplets tutorial but two be honest – the second one just produced “WTF”s on my side – so I decided to keep it dumb for now and just get snap to serve me static HTML files together with a simple CSS stylesheet and of course a jQuery-plugin I am going to use (yeah … it’s JavaScript … for now).

You can find the project on github but so far it’s only a very basic extension of the first example above.

some details on the code

To get snap to serve me the stylesheets and possible scripts I had to extend the routes in the site-function:

site :: Snap ()
site =
    ifTop (serveDirectory "./content/") <|>
    route [ ("api/sum/:operants", sumHandler)
          ] <|>
    dir "scripts" (serveDirectory "./scripts/") <|>
    dir "styles" (serveDirectory "./styles/")

This basically says that everything requested in with only the root-path is served (with some so called default-configuration) via serveDirectory straight from the content-folder (the default configuration sees that index.html is served … btw: Index.html will get you a 404)

The next part will serve for AJAX-requests – it binds a handler sumHandler to a URL like this http://mySite/api/sum/2%202 (see below).

The rest are static routes: /scripts/xyz tries to find the file xyz in the ./scripts folder and similar for styles (this is where I put the javascript files and the stylesheets).

The handler for the very simple API is implemented like this:

sumHandler :: Snap ()
sumHandler = do
    param <- getParam "operants"
    maybe (writeJSON errorMsg)
          (writeJSON . sumString . unpack) param
    where errorMsg :: ByteString
          errorMsg = pack "please give some operants"

sumString :: String -> Integer
sumString = sumUp . words
    where sumUp :: [String] -> Integer
          sumUp = sum . map read

It gets the string xyz in http://mySite/api/sum/xyz, splits it by spaces (words) and sums the integers (no there is no exception handling yet) up.
It finally serves the answer as JSON (for this I use the snap-extras package).

I’m sure there is an easier way to do all this but this is what I was able to find in a reasonable time using my google-ninja-skills.

next Iteration: getting a basic web-page and AJAX queries to work …

well … but next time

to my dear reader:

I am sure some/most of you know better then me, so pls let me know what and how I can improve.
For example: is this the right way to get to some kind of RESTful API (using handlers like this) or is there some special snap-way I just have not found yet?