[web-devel] Uniform substitution for URLs, templates, messages and other expressions in Shakespearean templates

Michael Snoyman michael at snoyman.com
Sun Sep 23 14:02:40 CEST 2012


On Sun, Sep 23, 2012 at 11:14 AM, Sebastian Fischer <mail at sebfisch.de> wrote:
> Hello,
>
> when writing my comparison of Hamlet with Heist and HXT
> (https://gist.github.com/3757918) I noticed that Hamlet and HXT have
> similar approaches to variable substitution but Hamlet's uses
> different syntax for substituting URLs, templates, and other
> expressions. I'd like to understand why.
>
> In HXT Haskell expressions can be substituted using the notation <%
> ... %>. The rendering of substituted expression is type based and
> specified separately for attribute- and element-positions via type
> classes `EmbedAsAttribute` and `EmbedAsChild`.
>
> In Hamlet Haskell expressions can be substituted using the notation
> #{...}. The rendering of substituted expressions is type based and
> specified via the type class `ToMarkup` from the blaze-html package.
> URLs are substituted using the notation @{...}, other templates using
> ^{...} and messages for i18n using _{...}.
>
> What are the reasons for providing four different syntaxes for
> variable subsitution if the substitution is type based? For example,
> different escaping mechanisms (URL escaping for routes, HTML escaping
> for strings) could already be accomplished with a single syntactical
> construct based on different type-class instances to generate markup.
>
> Best,
> Sebastian

The most basic reason is because I think multiple syntaxes is a good
thing. Embedding a URL is inherently a different activity than
embedding a piece of text, and I'd like the code to reflect that. In
Hamlet, the following is clear:

    <a href=@{foo}>#{bar}

I know that foo is a URL value, and bar is not. If I accidentally
included the wrong type for `foo`, Hamlet can catch it.

I actually contacted Jeremy off list about this issue to get a better
understanding of how HXT works, and it seems to me that it's pushing a
lot of the infrastructure into a monad with typeclass instances. I
don't know all the details, so I can't really speak to that approach.
But I *can* speak to how Hamlet was designed, and I think the fact
that context is irrelevant is very powerful. Each Hamlet template is
passed a URL rendering function, which ensures automatically that all
embedded URLs are of the correct type.

This is especially useful for cases such as subsites. Suppose that you
have a subsite for static files, and you want to give a user a link to
an image. You could construct such a static route along the lines of:

    StaticRoute ["myimage.png"]

(Yesod users will probably recognize that these links are
automatically generated, so you could just use the `myimage_png`
identifier, which ensures you do not have any typos.)

The question is: what does `myimage_png` render to? And the answer
depends entirely on your application. There are a number of
possibilities:

1. You stuck your static subsite at the "standard" location of
/static, so it renders to /static/myimage_png
2. You used a nonstandard location: /foo
3. You don't even have a static subsite set up
4. You have multiple static subsites set up

In any event, trying to use `<img src=@{myimage_png}>` will fail in
*all* of these cases with a message along the lines of "Route Static
does not match Route App" which, if you're familiar with GHC error
messages, means that you tried to use a route for the static subsite
when you should have used a route for the application. The only way to
create the link is to wrap up `myimage_png` in the appropriate
constructor, which in common nomenclature would work out to `<img
src=@{StaticR myimage_png}>`.

The same kinds of arguments apply to i18n messages as well. Though it
would be technically possible to instead just use a single typeclass
and force all templates to run in a certain monad, I think the
approach we have no is superior.

So tl;dr: Passing around an explicit functions and making different
types of interpolation look different is IMO better.

Michael



More information about the web-devel mailing list