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

Michael Snoyman michael at snoyman.com
Sun Sep 23 14:45:44 CEST 2012


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 <mail at sebfisch.de> wrote:
> 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 <michael at snoyman.com> wrote:
>> 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