
On Sun, Sep 23, 2012 at 11:14 AM, Sebastian Fischer
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 ``.
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