[web-devel] Type-safe URL handling

Jeremy Shaw jeremy at n-heptane.com
Tue Mar 16 18:00:41 EDT 2010

On Tue, Mar 16, 2010 at 11:54 AM, Chris Eidhof <chris at eidhof.nl> wrote:

> 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

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
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://www.haskell.org/pipermail/web-devel/attachments/20100316/29bb36a9/attachment.html

More information about the web-devel mailing list