#{person}
----
===== Case
Pattern matching is one of the great strengths of Haskell. Sum types let you
cleanly model many real-world types, and +case+ statements let you safely
match, letting the compiler warn you if you missed a case. Hamlet gives you the
same power.
[source, hamlet]
----
$case foo
$of Left bar
It was left: #{bar}
$of Right baz
It was right: #{baz}
----
===== With
Rounding out our statements, we have +with+. It's basically just a convenience
for declaring a synonym for a long expression.
[source, hamlet]
----
$with foo <- some very (long ugly) expression that $ should only $ happen once
But I'm going to use #{foo} multiple times. #{foo}
----
===== Doctype
Last bit of syntactic sugar: the doctype statement. We have support for a
number of different versions of a +doctype+, though we recommend +$doctype 5+
for modern web applications, which generates ++.
[source, hamlet]
----
$doctype 5
Hamlet is Awesome
All done.
----
NOTE: There is an older and still supported syntax: three exclamation points
(+!!!+). You may still see this in code out there. We have no plans to remove
support for this, but in general find the +$doctype+ approach easier to read.
==== Lucius Syntax
Lucius is one of two CSS templating languages in the Shakespeare family. It is
intended to be a superset of CSS, leveraging the existing syntax while adding
in a few more features.
* Like Hamlet, we allow both variable and URL interpolation.
* CSS blocks are allowed to nest.
* You can declare variables in your templates.
* A set of CSS properties can be created as a mixin, and reused in multiple
declarations.
Starting with the second point: 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:
[source, css]
----
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:
[source, lucius]
----
article {
code { background-color: grey; }
p { text-indent: 2em; }
a { text-decoration: none; }
> h1 { color: green; }
}
----
Having Lucius variables allows you to avoid repeating yourself. A simple
example would be to define a commonly used color:
[source, lucius]
----
@textcolor: #ccc; /* just because we hate our users */
body { color: #{textcolor} }
a:link, a:visited { color: #{textcolor} }
----
Mixins are a relatively new addition to Lucius. The idea is to declare a mixin
providing a collection of properties, and then embed that mixin in a template
using caret interpolation (+^+). The following example demonstrates how we
could use a mixin to deal with vendor prefixes.
[source, haskell]
----
{-# LANGUAGE QuasiQuotes #-}
import Text.Lucius
import qualified Data.Text.Lazy.IO as TLIO
-- Dummy render function.
render = undefined
-- Our mixin, which provides a number of vendor prefixes for transitions.
transition val =
[luciusMixin|
-webkit-transition: #{val};
-moz-transition: #{val};
-ms-transition: #{val};
-o-transition: #{val};
transition: #{val};
|]
-- Our actual Lucius template, which uses the mixin.
myCSS =
[lucius|
.some-class {
^{transition "all 4s ease"}
}
|]
main = TLIO.putStrLn $ renderCss $ myCSS render
----
==== Cassius Syntax
Cassius is a whitespace-sensitive alternative to Lucius. As mentioned in the
synopsis, it uses the same processing engine as Lucius, but preprocesses all
input to insert braces to enclose subblocks and semicolons to terminate lines.
This means you can leverage all features of Lucius when writing Cassius. As a
simple example:
[source, cassius]
----
#banner
border: 1px solid #{bannerColor}
background-image: url(@{BannerImageR})
----
==== Julius Syntax
Julius is the simplest of the languages discussed here. In fact, some might
even say it's really just JavaScript. Julius allows the three forms of
interpolation we've mentioned so far, and otherwise applies no transformations
to your content.
NOTE: If you use Julius with the scaffolded Yesod site, you may notice that
your JavaScript is automatically minified. This is not a feature of Julius;
instead, Yesod uses the hjsmin package to minify Julius output.
=== 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.
Reload mode:: Both of the above modes require a full recompile to see any changes. In reload 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.
NOTE: Reload mode is not available for Hamlet, only for Cassius, Lucius and
Julius. There are too many sophisticated features in Hamlet that rely directly
on the Haskell compiler and could not feasibly be reimplemented at runtime.
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 separating 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 Template Haskell functions, you need
to be sure to enable the appropriate language extensions and use correct
syntax. You can see a simple example of each in the following code snippets.
.Quasiquoter
[source, haskell]
----
{-# LANGUAGE OverloadedStrings #-} -- we're using Text below
{-# LANGUAGE QuasiQuotes #-}
import Text.Hamlet (HtmlUrl, hamlet)
import Data.Text (Text)
import Text.Blaze.Html.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|
$doctype 5
#{title}
#{title}
|]
main :: IO ()
main = putStrLn $ renderHtml $ template "My Title" render
----
.External file
[source, haskell]
----
{-# LANGUAGE OverloadedStrings #-} -- we're using Text below
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE CPP #-} -- to control production versus debug
import Text.Lucius (CssUrl, luciusFile, luciusFileReload, 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 = $(luciusFileReload "template.lucius")
#endif
main :: IO ()
main = TLIO.putStrLn $ renderCss $ template render
----
[source, lucius]
----
/* @template.lucius */
foo { bar: baz }
----
The naming scheme for the functions is very consistent.
[options="header"]
|===============
|Language|Quasiquoter|External file|Reload
|Hamlet|hamlet|+hamletFile+|_N/A_
|Cassius|+cassius+|+cassiusFile+|+cassiusFileReload+
|Lucius|+lucius+|+luciusFile+|+luciusFileReload+
|Julius|+julius+|+juliusFile+|+juliusFileReload+
|===============
==== 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 more
detail 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 this 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).
Dealing with internationalization (i18n) in Hamlet is a bit complicated. Hamlet
supports i18n via 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 bought.
We could represent those messages with a datatype.
[source, haskell]
----
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:
[source, haskell]
----
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.
[source, hamlet]
----
$doctype 5
i18n
_{Hello}
_{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:
[source, haskell]
----
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 +renderEnglish+, +renderSpanish+, or
+renderKlingon+ to this template, and it will generate nicely translated output
(depending, of course, on the quality of your translators). The complete
program is:
[source, haskell]
----
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE OverloadedStrings #-}
import Data.Text (Text)
import qualified Data.Text as T
import Text.Hamlet (HtmlUrlI18n, ihamlet)
import Text.Blaze.Html (toHtml)
import Text.Blaze.Html.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|
$doctype 5
i18n
_{Hello}
_{Apples count}
|]
main :: IO ()
main = putStrLn $ renderHtml
$ (template 5) (toHtml . renderEnglish) renderUrl
----
=== Other Shakespeare
In addition to HTML, CSS and JavaScript helpers, there is also some more
general-purpose Shakespeare available. shakespeare-text provides a simple way
to create interpolated strings, much like people are accustomed to in scripting
languages like Ruby and Python. This package's utility is definitely not
limited to Yesod.
[source, haskell]
----
{-# LANGUAGE QuasiQuotes, OverloadedStrings #-}
import Text.Shakespeare.Text
import qualified Data.Text.Lazy.IO as TLIO
import Data.Text (Text)
import Control.Monad (forM_)
data Item = Item
{ itemName :: Text
, itemQty :: Int
}
items :: [Item]
items =
[ Item "apples" 5
, Item "bananas" 10
]
main :: IO ()
main = forM_ items $ \item -> TLIO.putStrLn
[lt|You have #{show $ itemQty item} #{itemName item}.|]
----
Some quick points about this simple example:
* Notice that we have three different textual datatypes involved (+String+,
strict +Text+ and lazy +Text+). They all play together well.
* We use a quasiquoter named +lt+, which generates lazy text. There is also
+st+.
* Also, there are longer names for these quasiquoters (+ltext+ and +stext+).
* The syntax for variable interpolation for Text.Shakespeare.Text is the same
as described above. Note that +\^{..}+ and +@{..}+ are also recognized in
+lt+ and +st+. If the output of a template should contain +\^{..}+, a
backslash can be used to prevent the interpolation,
e.g. +[lt|2^\\{23}|]+. The backslash is removed from the resulting text.
=== General Recommendations
Here are some general hints from the Yesod community on how to get the most out
of Shakespeare.
* 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
link:https://github.com/pbrisbin/html-template-syntax[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 ++ or ++ tag inside a large block of text.