Daniel Patterson

Demystifying hvac – Making sense of all the symbols.

In Uncategorized on September 8, 2009 at 7:22 pm

hvac is a new, extremely lightweight web framework written in haskell. To learn more about it, check out the hvac announcement . The purpose of this short article is to try to make sense of all the symbols that you will encounter when looking through the basic examples. Almost all of these are used for dispatching, which is done in a parser style way, as opposed to being in the form of a list of regexps. To read more about why this decision was made, check out the post “Some concepts behind hvac” .

For a starter example, here is some code that is running this website (at least at the time of writing this article) -

siteController tmpl =
      (h |/ "articles" *> (
       h |\ (\articleId -> 
             h |// "GET" *> 
               (join $ renderf (tmpl "articlePage")
                       <$> "post"  |= selectRow "* from posts where id = ?" (Box (articleId :: Integer))
                       <*> "info" |= selectRow "* from info where id = ?" (Box (articleId :: Integer)) 
						    ))
       <|> 
       h |. (join $ renderf (tmpl "articlesPage")
                     <$> "posts" |= select "time,title,id from posts order by time desc" ())
                          ))
      <|>
      (h |/ "portfolio" |. renderf (tmpl "portfolio"))
      <|>
      (h |. renderf (tmpl "indexPage"))
      <|>
      renderf (tmpl "404")

This is a pretty big example, that probably looks very close to unreadable, especially if you are unfamiliar with haskell (even if you are familiar with it). The purpose of this article is to make the above make sense, so that you could have written it yourself.

The first thing to recognize is that this is a controller – if you read the second blog post linked to above, you’ll know that all controllers in hvac are parse trees – that should be enough to start going through the tree. The tree is made up of parser combinators, which allows for the fancy backtracking and concise nature of the tree, but shouldnt otherwise matter unless you are interested in creating new combinators, or changing the existing ones.

‘h’ is at the beginning of almost every line, and it is indeed necessary – it signals the beginning of a combinator, but in itself doesnt do anything – it is an empty combinator. Just realize that to begin matching a line, you need to use it.

Next, you’ll notice a lot of ‘|/’ showing up. This literally means, match the next word, delineated by a ‘/’. This means that to match /articles/my/something, you would write h |/ “articles” |/ “my” |/ “something” – which reads quite clearly, and should be pretty apparent in what it is doing.

‘|\’ borrows from haskell (logically), where \ is the symbol for lambda, and matches the next element in the request path (delineated by ‘/’ of course) as the argument of a function. It can be an anonymous function (as the example \articleId -> … is), or it could be a normally defined one. The only catch is that it can only match one argument. To match two, you would need to write something like this: h |\ (\arg1 -> h |\ (\arg2 -> …)). Something very powerful about this is that the argument is typed. In my above example, I explicitly tell that it is an Integer (on the following lines, when I use it), which means if someone puts in /articles/blah “blah” will not be bound to articleId – it simply will not match, and will simply back out (keeping “blah” as the next potential match) and keep walking the parse tree.

‘|//’ matches against the method. This could be “GET”, “POST”, etc. Not as immediately obvious as many of the combinators, but equally not illogical.

‘|.’ matches against nothing. This is very useful in many cases, and also keeps the urls clean. It is used so that /articles/ matches above, but /articles/blah matches nothing, and falls through to the bottom, to the default, the 404 page. As you can see, almost every match uses it at the end, with the easiest example probably being ‘h |/ “portfolio” |. …’ – this matches the beginning (h), / “portfolio” and then it ends. This means /portfolio/something will not match, and will fall through to the 404, as would be expected.

There are a few other combinators not present in my example: namely, ‘|?’ and ‘|\\’. ‘|?’ takes a pair (“some”,”thing”) and matches for params. IE, url?some=thing would be matched by h |/ “url” |? (“some”,”thing”). ‘|\\’ is very similar to ‘|\’ except it does not try to parse the value into some type – it just passes it as a string.

Those are all the hvac specific combinators, but there is still a lot of symbol noise: ‘*>’, ‘<|>’, ‘<$>’, ‘<*>’, and ‘|=’. With the exception of the last one, all of these come from Control.Applicative.

‘*>’ – this apparently is in this use functionally identical to ‘>>’, the monadic chaining discarding output of the previous action, but with a higher precedence than ‘<|>’, to make it easier to use.

‘<|>’ serves as an ‘or’ operation. If you have multiple parse options, you separate them with ‘<|>’. Whatever matches first is what is returned in the context of the http response, so you can think of them as series of tests, with the ‘<|>’ separating them.

‘<$>’ serves to inject a value into a stringtemplate (which is created with the renderf), in combination with the join (which joins nested monads into single level ones). It is used as the first injection, with subsequent ones done with ‘<*>’ – this is because (if I understand correctly) the output of ‘<$>’ cannot be chained into itself – but ‘<*>’ can use that output and keep chaining. forgive the poor explanation – beyond being able to use it, my understanding is not very good.

lastly, ‘|=’ – this is a part of StringTemplate, which simply names a value to be injected into a template. ie, selectRow returns a monad with the results of the sql query in it, “post” |= … labels those values as “post” for use in the template.

About these ads

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: