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

Jeremy Shaw jeremy at n-heptane.com
Sun Sep 23 22:15:05 CEST 2012

On Sun, Sep 23, 2012 at 7:02 AM, Michael Snoyman <michael at snoyman.com> wrote:
> On Sun, Sep 23, 2012 at 11:14 AM, Sebastian Fischer <mail at sebfisch.de> wrote:

> 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.

But that doesn't prevent you from making these mistakes, right?

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

It seems like we could achieve a similar effect in HSP by using a
newtype wrapper?

newtype At url = At url

And then having our EmbedAsAttr look like:

> instance (XMLGenerator m, MonadRoute m, URL m ~ url) => EmbedAsAttr m (Attr key (At url)) where

So that you now have to write:

 <a href=(At Home)>home</a>

And the following would be rejected:

 <a href=Home>home</a>
 <a href=(At NotAUrl)>not a url</a>
 <a><% Home %></a>
 <a><% (At Home) %></a>

Though, the following would still be accepted:

 <a class=(At Home)>oops</a>
 <a href=NotAURL>

> 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}>`.

If I understand you correctly, you have a type like:

data Static = StaticRoute [FilePath]

And also a route like:

data App = StaticR StaticRoute

And you are saying that you can not do:

 <img src=@{StaticRoute ["myimage.png"]> in a template that requires
the App route? you instead need:

<img src=@{StaticR (StaticRoute ["myimage.png"])}> ?

That would be exactly the same as HSP. Assuming we have something like:

type MyApp url a = ...

if you have:

foo :: MyApp App XML
foo = <a href=(StaticRoute ["myimage.png"])>my image</a>

It would fail. You would need:

foo :: MyApp App XML
foo = <a href=(StaticR $ StaticRoute ["myimage.png"])>my image</a>

If you already had a template like:

foo :: MyApp Static XML
foo = <a href=(StaticRoute ["myimage.png"])>my image</a>

and you want to use it in 'MyApp App XML' template, you should be able
to wrap it in StaticR using: 'nestURL StaticR'

In Hamlet you explicitly pass in a url rendering function. In the
HSP+web-routes stuff we also pass in a url rendering function. The
primary difference, I think, is that in hamlet it is an explicit
argument to the template and in HSP+web-routes we put the render
function in a ReaderT monad?

- jeremy

More information about the web-devel mailing list