[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