O'Reilly logo

Developing Web Applications with Haskell and Yesod by Michael Snoyman

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

Types

Before we jump into syntax, let’s take a look at the various types involved. We mentioned in the introduction that types help protect us from XSS attacks. For example, let’s say that we have an HTML template that should display someone’s name; it might look like this:

<p>Hello, my name is #{name}

Note

#{...} is how we do variable interpolation in Shakespeare.

What should happen to name, and what should its data type be? A naive approach would be to use a Text value, and insert it verbatim. But that would give us quite a problem when name="<script src='http://nefarious.com/evil.js'></script>". What we want is to be able to entity-encode the name, so that < becomes &lt;.

An equally naive approach is to simply entity-encode every piece of text that gets embedded. What happens when you have some preexisting HTML generated from another process? For example, on the Yesod website, all Haskell code snippets are run through a colorizing function that wraps up words in appropriate span tags. If we entity escaped everything, code snippets would be completely unreadable!

Instead, we have an Html data type. In order to generate an Html value, we have two options for APIs: the ToHtml typeclass provides a way to convert String and Text values into Html, via its toHtml function, automatically escaping entities along the way. This would be the approach we’d want for the name above. For the code snippet example, we would use the preEscaped family of functions.

When you use variable interpolation in Hamlet (the HTML Shakespeare language), it automatically applies a toHtml call to the value inside. So if you interpolate a String, it will be entity-escaped. But if you provide an Html value, it will appear unmodified. In the code snippet example, we might interpolate with something like #{preEscapedText myHaskellHtml}.

Note

The Html data type, as well as the functions mentioned, are all provided by the blaze-html package. This allows Hamlet to interact with all other blaze-html packages, and lets Hamlet provide a general solution for producing blaze-html values. Also, we get to take advantage of blaze-html’s amazing performance.

Similarly, we have Css/ToCss, as well as Javascript/ToJavascript. These provide some compile-time sanity checks that we haven’t accidentally stuck some HTML in our CSS.

Note

One other advantage on the CSS side is some helper data types for colors and units. For example: .red { color: #{colorRed} } Please see the Haddock documentation for more details.

Type-Safe URLs

Possibly the most unique feature in Yesod is type-safe URLs, and the ability to use them conveniently is provided directly by Shakespeare. Usage is nearly identical to variable interpolation, we just use the at-sign (@) instead of the hash (#). We’ll cover the syntax later; first, let’s clarify the intuition.

Suppose we have an application with two routes: http://example.com/profile/home is the homepage, and http://example.com/display/time displays the current time. And let’s say we want to link from the homepage to the time. I can think of three different ways of constructing the URL:

  1. As a relative link: ../display/time

  2. As an absolute link, without a domain: /display/time

  3. As an absolute link, with a domain: http://example.com/display/time

There are problems with each approach: the first will break if either URL changes. Also, it’s not suitable for all use cases; RSS and Atom feeds, for instance, require absolute URLs. The second is more resilient to change than the first, but still won’t be acceptable for RSS and Atom. And while the third works fine for all use cases, you’ll need to update every single URL in your application whenever your domain name changes. You think that doesn’t happen often? Just wait till you move from your development to staging and finally production server.

But more importantly, there is one huge problem with all approaches: if you change your routes at all, the compiler won’t warn you about the broken links. Not to mention that typos can wreak havoc as well.

The goal of type-safe URLs is to let the compiler check things for us as much as possible. In order to facilitate this, our first step must be to move away from plain old text, which the compiler doesn’t understand, to some well defined data types. For our simple application, let’s model our routes with a sum type:

data MyRoute = Home | Time

Instead of placing a link like /display/time in our template, we can use the Time constructor. But at the end of the day, HTML is made up of text, not data types, so we need some way to convert these values to text. We call this a URL rendering function, and a simple one is:

renderMyRoute :: MyRoute -> Text
renderMyRoute Home = "http://example.com/profile/home"
renderMyRoute Time = "http://example.com/display/time"

Note

URL rendering functions are actually a bit more complicated than this. They need to address query string parameters, handle records within the constructor, and more intelligently handle the domain name. But in practice, you don’t need to worry about this, since Yesod will automatically create your render functions. The one thing to point out is that the type signature is actually a little more complicated when handling query strings:

type Query = [(Text, Text)]
type Render url = url -> Query -> Text
renderMyRoute :: Render MyRoute
renderMyRoute Home _ = ...
renderMyRoute Time _ = ...

OK, we have our render function, and we have type-safe URLs embedded in the templates. How does this fit together exactly? Instead of generating an Html (or Css or Javascript) value directly, Shakespearean templates actually produce a function, which takes this render function and produces HTML. To see this better, let’s have a quick (fake) peek at how Hamlet would work under the surface. Supposing we had a template:

<a href=@{Time}>The time

this would translate roughly into the Haskell code:

\render -> mconcat ["<a href='", render Time, "'>The time</a>"]

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required