<html><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space; "><div>I've had a number of requests to talk about the bookmarkable URL package on-list. The library, which is currently unnamed, is basically the "dispatcher" and "request pipeline" parts from Noel's ICFP 2007 paper:</div><div><div><br class="webkit-block-placeholder"></div></div><div> <a href="http://www.untyped.com/downloads/icfp068-welsh.pdf">http://www.untyped.com/downloads/icfp068-welsh.pdf</a></div><div><br></div><div>Here's a little more detail. I shall refer to the nameless library as "Dispatch" for the purposes of this email. Dispatch has three main functions:</div><div><br class="webkit-block-placeholder"></div><div><br class="webkit-block-placeholder"></div><div><br class="webkit-block-placeholder"></div><div>The first function is to provide a mapping from bookmarkable URLs to "controller" procedures that handle requests.</div><div><br></div><div>Suppose I have a blog. Two things people like to do with blogs are: view a list of posts and view a single post. I want to create bookmarkable URLs for these things. In a Ruby-on-Rails aesthetic these would be something like:</div><div><br class="webkit-block-placeholder"></div><div><font class="Apple-style-span" face="Monaco"> <a href="http://www.example.com/">http://www.example.com/</a> for the index</font></div><div><font class="Apple-style-span" face="Monaco"> <a href="http://www.example.com/posts/slug">http://www.example.com/posts/slug</a> for the post</font></div><div><br class="webkit-block-placeholder"></div><div>For convenience I want to write separate procedures to handle requests to these URLs:</div><div><br class="webkit-block-placeholder"></div><div><span class="Apple-style-span" style="font-family: Monaco; "> list-posts : request -> response</span></div><div><div><span class="Apple-style-span" style="font-family: Monaco; "> review-post : request string -> response</span></div><div><br></div><div>(the string is the post slug).</div><div><br class="webkit-block-placeholder"></div><div>Dispatch provides a really quick way to set up mappings from URLs to controllers. First I write a "site" declaration in a module called site.ss:</div><div><br class="webkit-block-placeholder"></div><div><div><font class="Apple-style-span" face="Monaco"> #lang scheme/base</font></div><div><font class="Apple-style-span" face="Monaco"><br class="webkit-block-placeholder"></font></div><div><span class="Apple-style-span" style="font-family: Monaco; "> (define-site blog</span></div><div><font class="Apple-style-span" face="Monaco"> ([(url "/") list-posts]</font></div><div><font class="Apple-style-span" face="Monaco"> [(url "/posts/" (string-arg)) review-post]))</font></div><div><br></div><div>then I write my controllers in, say, controller.ss:</div><div><br class="webkit-block-placeholder"></div><div><div><font class="Apple-style-span" face="Monaco"> #lang scheme/base</font></div><div><font class="Apple-style-span" face="Monaco"><br class="webkit-block-placeholder"></font></div><div><span class="Apple-style-span" style="font-family: Monaco; "> (require (file "site.ss"))</span></div><div><font class="Apple-style-span" face="Monaco"><br class="webkit-block-placeholder"></font></div><div><span class="Apple-style-span" style="font-family: Monaco; "> (define-controller (list-posts request)</span></div><div><font class="Apple-style-span" face="Monaco"> ...)</font></div><div><font class="Apple-style-span" face="Monaco"><br class="webkit-block-placeholder"></font></div></div><div><div><div><span class="Apple-style-span" style="font-family: Monaco; "> (define-controller (review-posts request slug)</span></div><div><font class="Apple-style-span" face="Monaco"> ...)</font></div><div><br></div><div>Finally, I write my servlet and use a procedure to dispatch the request to the relevant controller:</div><div><br class="webkit-block-placeholder"></div><div><div><font class="Apple-style-span" face="Monaco"> #lang scheme/base</font></div><div><font class="Apple-style-span" face="Monaco"><br class="webkit-block-placeholder"></font></div><div><font class="Apple-style-span" face="Monaco"> (require (file "site.ss")</font></div><div><font class="Apple-style-span" face="Monaco"> (file "controller.ss"))</font></div><div><font class="Apple-style-span" face="Monaco"><br class="webkit-block-placeholder"></font></div><div><span class="Apple-style-span" style="font-family: Monaco; "> (define (start initial-request)</span></div><div><font class="Apple-style-span" face="Monaco"> (dispatch initial-request blog))</font></div><div><br></div><div>The dispatch function runs through the URL rules in the site until it finds one that matches the request. Once it's there it calls the controller.</div><div><br class="webkit-block-placeholder"></div><div><br class="webkit-block-placeholder"></div><div><br class="webkit-block-placeholder"></div></div></div></div></div></div><div>The second function of Dispatch is to provide a reverse mapping from controllers to URLs:</div><div><br class="webkit-block-placeholder"></div><div>One of the beauties of continuation-oriented web programming is that you don't have to worry about URL structures. I want the same solution for my permanent bookmarkable URLs. Dispatch provides a procedure, controller-url, that generates a URL from a controller and a list of arguments:</div><div><br class="webkit-block-placeholder"></div><div><div><font class="Apple-style-span" face="Monaco"> controller-url : controller any ... -> string</font></div><div><font class="Apple-style-span" face="Monaco"><br class="webkit-block-placeholder"></font></div><div><font class="Apple-style-span" face="Monaco"> (controller-url review-post "hello-world") -> "/posts/hello-world"</font></div></div><div><br class="webkit-block-placeholder"></div><div>You don't get the flexibility of state-passing that you get with continuations, but you do get a nice, friendly, bookmarkable URL.</div><div><br class="webkit-block-placeholder"></div><div><br class="webkit-block-placeholder"></div><div><br class="webkit-block-placeholder"></div><div>The third function of Dispatch is to get around an irritating problem I've had in the past:</div><div><br class="webkit-block-placeholder"></div><div>I like writing web apps in MVC style. I typically divide my apps into three sets of modules: one set for the model, one set for the view, and one set for the controllers. The model is usually a separate entity, but the view and the controller tend to refer to one another a lot. I get fed up chasing cyclic require bugs very quickly.</div><div><br class="webkit-block-placeholder"></div><div>Some people might rightly say "use units", but I find units to be quite complicated for this kind of thing. They require the creation of extra signatures that muddy the waters a little, and they don't integrate properly with DrScheme's "Check Syntax" view, which I tend to use heavily.</div><div><br class="webkit-block-placeholder"></div><div>Dispatch solves this problem with a quick and dirty bit of mutation. All the actual definitions are provided by define-site in site.ss: the define-controller macro simply updates the relevant definition with the body procedure. You can require "site.ss" all over your software and call any controller directly, by continuation or via controller-url.</div><div><br></div><div><br class="webkit-block-placeholder"></div><div><br class="webkit-block-placeholder"></div><div>There are another couple of features too. Ones that spring to mind:</div><div>- the arguments in URLs can generate controller arguments of arbitrary types;</div><div>- and there's built-in support for the "request pipelines" described in the ICFP paper.</div><div><br class="webkit-block-placeholder"></div><div><br></div><div><br class="webkit-block-placeholder"></div><div><div><div>Anyway, Dispatch is pretty much ready for release. I really just need a witty name and a little more documentation. I'm going to have a few hours on the train over the next couple of days so hopefully I'll make some headway and get it out this week.</div><div><br class="webkit-block-placeholder"></div><div><br class="webkit-block-placeholder"></div><div><br class="webkit-block-placeholder"></div><div>Cheers,</div><div><br class="webkit-block-placeholder"></div><div>-- Dave</div></div></div></body></html>