
On Sun, Sep 23, 2012 at 10:15 PM, Jeremy Shaw
On Sun, Sep 23, 2012 at 7:02 AM, Michael Snoyman
wrote: On Sun, Sep 23, 2012 at 11:14 AM, Sebastian Fischer
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}>
Of course not. There are times when something like that is completely valid: <img src=@{foo} title=@{foo}> let bar = "http://www.google.com/" <a href=#{bar}> But it makes the common case look correct, and the exceptional cases look exceptional, which definitely helps with catching mistakes.
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:
And the following would be rejected:
<a href=Home>home</a> not a url</a> <a><% Home %></a> <a><% (At Home) %></a>
Though, the following would still be accepted:
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 `
`.
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:
in a template that requires the App route? you instead need:
?
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 = my image</a>
It would fail. You would need:
foo :: MyApp App XML foo = my image</a>
If you already had a template like:
foo :: MyApp Static XML foo = 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
As an aside, Yesod has the concept of a Widget which works very similarly to how you describe HSP+web-routes. I avoided bringing it up so far so as not to confuse the topic, but the concept of embedding the rendering function inside the monad exists in the Hamlet/Yesod world as well. What I'm confused about is this. There's no distinction between URL and other kinds of embedding. So when I see: <a href=foo> I don't know if the variable `foo` is supposed to be a URL or some other instance of `EmbedAsAttr`. So how is it possible for you to statically disallow as `StaticRoute` from being used? I understand that it will fail because there is no instance of `EmbedAsAttr` available, but the error message will say that there is an instance missing, as opposed to in Hamlet where it will say that the wrong route is being used. My point being, that as I understand it, a sufficiently determined and/or confused user could create such an instance. I'm also not certain how your nestURL works, for similar reasons. In Hamlet, the idea would be to pass in a modified rendering function, but again I don't see how it's possible to do that for HSP when you don't know which interpolations are arbitrary EmbedAsAttr values and which are specifically routes. (Note: These questions likely speak more to my ignorance of things than to anything else, I'm just trying to understand how HSP gets these features.) Michael