
Yes, that about sums it up. My explanation focused on URLs because,
IMO, they're the most important aspect, but a similar argument can be
made for embedded templates and i18n messages.
On Sun, Sep 23, 2012 at 2:37 PM, Sebastian Fischer
I rephrase to check if I understand correctly.
You want to make it an error to:
1. accidentally splice something that is no URL into URL positions and 2. splice sub-site URLs into the wrong sub site (or into the main sub site) without proper wrapping.
Make sense, thanks!
Sebastian
On Sun, Sep 23, 2012 at 2:02 PM, Michael Snoyman
wrote: On Sun, Sep 23, 2012 at 11:14 AM, Sebastian Fischer
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 `
`.
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