On 16 mrt 2010, at 16:46, Michael Snoyman wrote:
> Took me a bit to appreciate what you just said there, but I see your point now. It's true, it does have some very nice features. I'm still concerned about creating something which involves too much boilerplate.
Yes. Generic programming (which is what the regular library provides) tries to hide the boilerplate (Template Haskell) code in a library and provides you with combinators so that you can program on the structure of a datatype. This is also at the core of my regular-web library [1], which generates forms/html/json/xml in the same way. You notice that on lines 34-36, I use the exact same TH calls. Once you did that, you get HTML and Formlets generation for free!
Using
URLT.TH you would just need the one-liner:
$(deriveAsURL ''UserRoute)
However, if that is asking too much, I have also added URLT.Regular:
So instead you do:
$(deriveAll ''UserRoute "PFUserRoute")
type instance PF UserRoute = PFUserRoute
instance AsURL UserRoute where
toURLS = gtoURLS . from
fromURLC = fmap (fmap to) gfromURLC
> Am I understanding correctly that the URLs will be derived from the names of the datatypes?
Exactly!
URLT currently allows you to generate the urls from the names via template haskell, generics, or by writing instances by hand. I would like to add support for QuasiQuotes similar to what is done in Yesod.
> Also, how would you address URL dispatch in this approach?
A URL is represented by a datastructure, e.g. ApplicationRoute. You would write a function "dispatch :: ApplicationRoute -> Application". Dispatch is not part of the library (and it shouldn't be, imo). In the module "MyApp.UserController" you might write a function "dispatchUser :: UserRoute -> Application", which is called by the original dispatch function.
That is how URLT works -- except better. Your function type will be like:
dispatchApp :: (ShowURL m, URL m ~ ApplicationRoute) => ApplicationRoute -> m a
MyApp.UserController might have a function like:
dispatchUser :: (ShowURL m, URL m ~ UserRoute) => UserRoute -> m a
the top level dispatchApp would call it like:
dispatchApp (User userURL) = nestURL User $ dispatchApp userURL
The constraints ensure that your app is also only generating URLs of type ApplicationRoute. If your app was generating urls of type UserRoute, but expecting incoming urls of type ApplicationRoute, that clearly would not work.
Imagine if you accidentally wrote:
dispatchApp Login =
do let url = toURL List
in <a href=list>list</a>
Here, in the dispatchApp function I accidentally called 'toURL List' instead of, 'toURL (User List)'. In your code that is *not* caught as a type error. With the ShowURL monad it is caught as a compile time error. If the goal is type-safe URLs I think it is essential to catch this error, don't you?
- jeremy