New Shakespeare Chapter

August 19, 2011

GravatarBy Michael Snoyman

Status Update

We're coming along very nicely on the Yesod 0.9 release. The goal is to have the release candidate out on Monday. However, at this point, the code in the repo is in active use in a number of different sites, and we aren't running into any issues any more. So all signs indicate that this will be the most solid release of Yesod to date.

One thing that's still necessary is updating the book for the changes in the packages. There are four chapters that need to be reworked: templates, widgets, forms and persistent. I present to you the first of these revised chapters; as usual, feedback appreciated.

Shakespearean Templates

Yesod uses the Shakespearean family of template languages as its standard approach to HTML, CSS and Javascript creation. This language family shares some common syntax, as well as overarching principles:

  • As little interference to the underlying template language as possible, while providing conveniences where possible.
  • Compile-time guarantees on well-formed content.
  • Strict type safety, extending to the prevention of XSS (cross-site scripting) attacks.
  • Automated checking of valid URLs, whenever possible, through type-safe URLs.

There's is nothing inherently tying Yesod to these languages, or the other way around: each can be used independently of the other. This chapter will address these template languages on their own, while the remainder of the book will use them to enhance Yesod application development.

Synopsis

Hamlet (HTML)

!!!
<html>
    <head>
        <title>#{pageTitle} - My Site
        <link rel="stylesheet" href=@{Stylesheet}
    <body>
        <h1 .page-title>#{pageTitle}
        <p>Here is a list of your friends:
        $if null friends
            <p>Sorry, I lied, you don't have any friends.
        $else
            <ul>
                $forall friend <- friends
                    <li>#{friendName friend} (#{show $ friendAge friend} years old)
        <footer>^{copyright}

Cassius (CSS)

#myid
    color: #{red}
    font-size: #{bodyFontSize}
foo bar baz
    background-image: url(@{MyBackgroundR})

Lucius (CSS)

section.blog {
    padding: 1em;
    border: 1px solid #000;
    h1 {
        color: #{headingColor};
    }
}

Julius (Javascript)

$(function(){
    $("section.#{sectionClass}").hide();
    $("#mybutton").click(function(){document.location = "@{SomeRouteR}";});
    ^{addBling}
});

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}

What should happen to name, and what should its datatype 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, in this book, all Haskell code snippets are run through a colorizing that wraps up words in appropriate span tags. If we entity escaped everything, code snippets would be completely unreadable!

Instead, we have an Html datatype. 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}.

Similarly, we have Css/ToCss, as well as Javascript/ToJavascript. In those cases, the goal is not really security, as it is very uncommon to be interpolating user input to CSS and Javascript. Instead, this just provides some nice compile-time guarantees that we haven't accidently stuck some HTML in our CSS.

Type-safe URLs

One of the biggest unique features in Yesod is type-safe URLs, and the ability to use them conveniently is provided directly by Shakespeare. Usage is very similar to variable interpolation, we just use the at-sign (@) instead. We'll cover the syntax later; first, let's clarify the intuition.

Let's say that we've got 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 datatypes. For our simple application, let's model our routes with a simple sum type:

data MyRoute = Home | Time

Now instead of placing a link like /display/time in our template, we can just 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"

URL rendering functions are actually a bit more complicated than this. They need to address query string parameters, handle records within the constructor, 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 to handle query strings:

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

OK, we have our render function, and we have type-safe URLs embedded in the templates. How does this fit together exactly? Well, instead of generating an Html (or Css or Javascript) value directly, Shakespearean templates actually produce a function, that 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>"]

Syntax

Hamlet Syntax

Tags

Obviously tags will play an important part of any HTML template language. In Hamlet, we try to stick very close to existing HTML syntax to make the language more comfortable. However, instead of using closing tags to denote nesting, we use indentation. So something like this in HTML:

<body>
<p>Some paragraph.</p>
<ul>
<li>Item 1</li>
<li>Item 2</li>
</ul>
</body>
would be
<body>
    <p>Some paragraph.
    <ul>
        <li>Item 1
        <li>Item 2

In general, we find this to be easier to follow than HTML once you get accustomed to it. The only tricky part comes with dealing with whitespace before and after tags. For example, let's say you want to create the HTML

<p>Paragraph <i>italic</i> end.</p>
We want to make sure that there is a whitespace preserved after the word "Paragraph" and before the word "end". To do so, we use two simple escape characters:
<p>
    Paragraph #
    <i>italic
    \ end.
The whitespace escape rules are actually very simple:
  1. If the first non-space character in a line is a backslash, the backslash is ignored.
  2. If the last character in a line is a hash, it is ignored.

One other thing. Hamlet does not escape entities within its content. This is done on purpose to allow existing HTML to be more easily copied in. So the example above could also be written as:

<p>Paragraph <i>italic</i> end.
Notice that the first tag will be automatically closed by Hamlet, while the inner "i" tag will not. You are free to use whichever approach you want, there is no penalty for either choice.

Interpolation

What we have so far is a nice, simplified HTML, but it doesn't let us interact with our Haskell code at all. How do we pass in variables? Simple: with interpolation:

<head>
    <title>#{title}
The hash followed be a pair of braces denotes variable interpolation. In the case above, the title variable from the scope in which the template was called will be used. Let me state that again: Hamlet automatically has access to the variables in scope when it's called. There is no need to specifically pass variables in.

But it gets better still. You can apply functions within an interpolation. You can use string and numeric literals in an interpolation. You can use qualified modules. Both parentheses and the dollar sign can be used to group statements together. And at the end, the toHtml function is applied to the result, meaning any instance of ToHtml can be interpolated.

Variable Interpolation
-- Just ignore the quasiquote stuff for now, and that shamlet thing.
-- It will be explained later.
{-# LANGUAGE QuasiQuotes #-}
import Text.Hamlet (shamlet)
import Text.Blaze.Renderer.String (renderHtml)
import Data.Char (toLower)
import Data.List (sort)

data Person = Person
    { name :: String
    , age  :: Int
    }

main :: IO ()
main = putStrLn $ renderHtml [shamlet|
<p>Hello, my name is #{name person} and I am #{show $ age person}.
<p>
    Let's do some funny stuff with my name: #
    <b>#{sort $ map toLower (name person)}
<p>Oh, and in 5 years I'll be #{show $ (+) 5 (age person)} years old.
|]
  where
    person = Person "Michael" 26

What about our much-touted type-safe URLs? They are almost identical to variable interpolation in every way, except they start with an at-sign (@) instead. In addition, there is embedding via a caret (^) which allows you to embed another template of the same type.

URL Interpolation and Embedding
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE OverloadedStrings #-}
import Text.Hamlet (HtmlUrl, hamlet)
import Text.Blaze.Renderer.String (renderHtml)
import Data.Text (Text)

data MyRoute = Home

render :: MyRoute -> [(Text, Text)] -> Text
render Home _ = "/home"

footer :: HtmlUrl MyRoute
footer = [hamlet|
<footer>
    Return to #
    <a href=@{Home}>Homepage
    .
|]

main :: IO ()
main = putStrLn $ renderHtml $ [hamlet|
<body>
    <p>This is my page.
    ^{footer}
|] render

Attributes

If you paid close attention there, you'll have noticed that we put an href attribute on the "a" tag. That syntax is pretty straight-forward, but let's clarify some things:

  • You can have interpolations on the right-hand-side of the equals sign.
  • The equals sign and value for an attribute are optional, just like in HTML. So <input type=checkbox checked> is perfectly valid.
  • There are two convenience attributes: for id, you can use the hash, and for classes, the period. In other words, <p #paragraphid .class1 .class2>.
  • While quotes around the attribute value are optional, they are required if you want to embed spaces.
  • You can add an attribute optionally by using colons. To check a checkbox only checked if the variable isChecked is True, you would write <input type=checkbox :isChecked:checked>. To have a paragraph be optionally red, you could use <p :isRed:style="color:red">.

Conditionals

Eventually, you'll want to put in some logic in your page. The goal of Hamlet is to make the logic as minimalistic as possible, pushing the heavy lifting into Haskell. As such, our logical statements are very basic... so basic, that it's if, elseif, and else.

$if isAdmin
    <p>Welcome to the admin section.
$elseif isLoggedIn
    <p>You are not the administrator.
$else
    <p>I don't know who you are. Please log in so I can decide if you get access.
All the same rules of normal interpolation apply to the content of the conditionals.

Maybe

Similarly, we have a special construct for dealing with Maybe values. This could technically be dealt with using if, isJust and fromJust, but this is more convenient and avoids partial functions.

$maybe name <- maybeName
    <p>Your name is #{name}
$nothing
    <p>I don't know your name.
The left hand side of the <- must be a simple identifier; the right can be anything that goes in an interpolation.

Forall

And what about looping over lists? We have you covered there too:

$if null people
    <p>No people.
$else
    <ul>
        $forall person <- people
            <li>#{person}

With

Rounding out our statements, we have with. It's basically just a convenience for declaring a synonym for a long expression.

$with foo <- some very (long ugly) expression that $ should only $ happen once
    <p>But I'm going to use #{foo} multiple times. #{foo}

Doctype

Last bit of syntactic sugar: the doctype statement. Just use the triple exclamation point on a line by itself. Hamlet uses HTML5 by default, so the generated doctype is <!DOCTYPE html>.

!!!
<html>
    <head>
        <title>Hamlet is Awesome
    <body>
        <p>All done.

Cassius Syntax

Cassius is the original CSS template language. It uses simple whitespace rules to delimit blocks, making braces and semicolons unnecessary. It supports both variable and URL interpolation, but not embedding. The syntax is very straight-forward:

#banner
    border: 1px solid #{bannerColor}
    background-image: url(@{BannerImageR})

Lucius Syntax

While Cassius uses a modified, whitespace-sensitive syntax for CSS, Lucius is true to the original. You can take almost any CSS file out there and it will be a valid Lucius file. There are, however, two additions to Lucius:

  • Like Cassius, we allow both variable and URL interpolation.
  • CSS blocks are allowed to nest.

That second point requires a bit of explanation. Let's say you want to have some special styling for some tags within your article. In plain ol' CSS, you'd have to write:

article code { background-color: grey; }
article p { text-indent: 2em; }
article a { text-decoration: none; }
In this case, there aren't that many clauses, but having to type out article each time is still a bit of a nuisance. Imagine if you had a dozen or so of these. Not the worst thing in the world, but a bit of an annoyance. Lucius helps you out here:
article {
    code { background-color: grey; }
    p { text-indent: 2em; }
    a { text-decoration: none; }
}

Other than that, Lucius is identical to CSS.

Julius Syntax

Julius is the simplest of the languages discussed here. In fact, some might even say it's not really its own language. Julius allows the three forms of interpolation we've mentioned so far, and otherwise applies no transformations to your content.

Calling Shakespeare

The question of course arises at some point: how do I actually use this stuff? There are three different ways to call out to Shakespeare from your Haskell code:

Quasiquotes
Quasiquotes allow you to embed arbitrary content within your Haskell, and for it to be converted into Haskell code at compile time.
External file
In this case, the template code is in a separate file which is referenced via Template Haskell.
Debug mode
Both of the above modes require a full recompile to see any changes. In debug mode, your template is kept in a separate file and referenced via Template Haskell. But at runtime, the external file is reparsed from scratch each time.

One of the first two approaches should be used in production. They both embed the entirety of the template in the final executable, simplifying deployment and increasing performance. The advantage of the quasiquoter is the simplicity: everything stays in a single file. For short templates, this can be a very good fit. However, in general, the external file approach is recommended because:

  • It follows nicely in the tradition of separate logic from presentation.
  • You can easily switch between external file and debug mode with some simple CPP macros, meaning you can keep rapid development and still achieve high performance in production.

Since these are special quasiquoters and TH functions, you need to be sure to enable the appropriate language extensions and use correct syntax. A simple example of each.

Quasiquoter
{-# LANGUAGE OverloadedStrings #-} -- we're using Text below
{-# LANGUAGE QuasiQuotes #-}
import Text.Hamlet (HtmlUrl, hamlet)
import Data.Text (Text)
import Text.Blaze.Renderer.String (renderHtml)

data MyRoute = Home | Time | Stylesheet

render :: MyRoute -> [(Text, Text)] -> Text
render Home _ = "/home"
render Time _ = "/time"
render Stylesheet _ = "/style.css"

template :: Text -> HtmlUrl MyRoute
template title = [hamlet|
!!!
<html>
    <head>
        <title>#{title}
        <link rel=stylesheet href=@{Stylesheet}>
    <body>
        <h1>#{title}
|]

main :: IO ()
main = putStrLn $ renderHtml $ template "My Title" render
External file
{-# LANGUAGE OverloadedStrings #-} -- we're using Text below
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE CPP #-} -- to control production versus debug
import Text.Lucius (CssUrl, luciusFile, luciusFileDebug, renderCss)
import Data.Text (Text)
import qualified Data.Text.Lazy.IO as TLIO

data MyRoute = Home | Time | Stylesheet

render :: MyRoute -> [(Text, Text)] -> Text
render Home _ = "/home"
render Time _ = "/time"
render Stylesheet _ = "/style.css"

template :: CssUrl MyRoute
#if PRODUCTION
template = $(luciusFile "template.lucius")
#else
template = $(luciusFileDebug "template.lucius")
#endif

main :: IO ()
main = TLIO.putStrLn $ renderCss $ template render
-- @template.lucius
foo { bar: baz }

The naming scheme for the functions is very consistent.

Language Quasiquoter External file Debug
Hamlet hamlet hamletFile N/A
Cassius cassius cassiusFile cassiusFileDebug
Lucius lucius luciusFile luciusFileDebug
Julius julius juliusFile juliusFileDebug

Alternate Hamlet Types

So far, we've seen how to generate an HtmlUrl value from Hamlet, which is a piece of HTML with embedded type-safe URLs. There are currently three other values we can generate using Hamlet: plain HTML, HTML with URLs and internationalized messages, and widgets. That last one will be covered in the widgets chapter.

To generate plain HTML without any embedded URLs, we use "simplified Hamlet". There are a few changes:

  • We use a different set of functions, prefixed with an "s". So the quasiquoter is shamlet and the external file function is shamletFile. How we pronounce those is still up for debate.
  • No URL interpolation is allowed. Doing so will result in a compile-time error.
  • Embedding (the caret-interpolator) no longer allows arbitrary HtmlUrl values. The rule is that the embedded value must have the same type as the template itself, so in this case it must be Html. That means that for shamlet, embedding can be completely replaced with normal variable interpolation (with a hash).

Internationalized (from now on, i18n) is a bit more complicated than normal Hamlet. This is based around the idea of a message datatype, very similar in concept and implementation to a type-safe URL. As a motivating example, let's say we want to have an application that tells you hello and how many apples you have eaten. We could represent those messages with a datatype.

data Msg = Hello | Apples Int
Next, we would want to be able to convert that into something human-readable, so we define some render functions:
renderEnglish :: Msg -> Text
renderEnglish Hello = "Hello"
renderEnglish (Apples 0) = "You did not buy any apples."
renderEnglish (Apples 1) = "You bought 1 apple."
renderEnglish (Apples i) = T.concat ["You bought ", T.pack $ show i, " apples."]
Now we want to interpolate those Msg values directly in the template. For that, we use underscore interpolation.
!!!
<html>
    <head>
        <title>i18n
    <body>
        <h1>_{Hello}
        <p>_{Apples count}

This kind of a template now needs some way to turn those values into HTML. So just like type-safe URLs, we pass in a render function. To represent this, we define a new type synonym:

type Render url = url -> [(Text, Text)] -> Text
type Translate msg = msg -> Html
type HtmlUrlI18n msg url = Translate msg -> Render url -> Html
At this point, you can pass off renderEnglish, renderSpanish, or renderKlingon to this template, and it will generate nicely translated output (depending, of course, on the quality of your translators). A full sample program follows.

i18n Example
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE OverloadedStrings #-}
import Data.Text (Text)
import qualified Data.Text as T
import Text.Hamlet (HtmlUrlI18n, ihamlet)
import Text.Blaze (toHtml)
import Text.Blaze.Renderer.String (renderHtml)

data MyRoute = Home | Time | Stylesheet

renderUrl :: MyRoute -> [(Text, Text)] -> Text
renderUrl Home _ = "/home"
renderUrl Time _ = "/time"
renderUrl Stylesheet _ = "/style.css"

data Msg = Hello | Apples Int

renderEnglish :: Msg -> Text
renderEnglish Hello = "Hello"
renderEnglish (Apples 0) = "You did not buy any apples."
renderEnglish (Apples 1) = "You bought 1 apple."
renderEnglish (Apples i) = T.concat ["You bought ", T.pack $ show i, " apples."]

template :: Int -> HtmlUrlI18n Msg MyRoute
template count = [ihamlet|
!!!
<html>
    <head>
        <title>i18n
    <body>
        <h1>_{Hello}
        <p>_{Apples count}
|]

main :: IO ()
main = putStrLn $ renderHtml
     $ (template 5) (toHtml . renderEnglish) renderUrl

General Recommendations

Here are some general hints from the Yesod community on how to get the most out of Shakespeare. (If you have more, please add them in a comment to this paragraph:

  • For actual sites, use external files. For libraries, it's OK to use quasiquoters, assuming they aren't too long.
  • Patrick Brisbin has put together a Vim code highlighter that can help out immensely.
  • You should almost always start Hamlet tags on their own line instead of embedding start/end tags after an existing tag. The only exception to this is the occasional <i> or <b> tag inside a large block of text.

Comments

comments powered by Disqus

Archives