On Tue, Mar 16, 2010 at 11:51 AM, Gregory Collins <span dir="ltr">&lt;<a href="mailto:greg@gregorycollins.net">greg@gregorycollins.net</a>&gt;</span> wrote:<br><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;">
<div class="im">Michael Snoyman &lt;<a href="mailto:michael@snoyman.com">michael@snoyman.com</a>&gt; writes:<br></div></blockquote><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;">
<div class="im">Yes, this approach smells a lot better to me: the types are types and</div>
the data are data. Just a brainstorm: if each handler monad were to<br>
carry its routing table around with it, you could define something like:<br>
<br>
    match :: ByteString<br>
          -&gt; (WhateverYourWebMonadIsCalled a)<br>
          -&gt; (WhateverYourWebMonadIsCalled a) </blockquote><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;">
And write<br>
<br>
    handler = match &quot;/foo/:bar/#int/&quot; $ do ...<br>
<br>
without the template haskell quasiquotation (i.e. do the checking at<br>
runtime.) </blockquote><div><br></div><div>Well, that is pretty much exactly how the ServerMonad in happstack works. That is why I wanted to free ServerMonad  so that it is not happstack specific. </div><div><br></div><div>
I am not sure what :bar is supposed to mean, but let&#39;s pretend it means match on the type Bar.</div><div><br></div><div>In the current code today you would could match on &quot;/foo/:bar/#int/&quot; like this:</div><div>
<br></div><div> dir &quot;foo&quot; $ path $ \(bar :: Bar) -&gt; path $ \(i :: Int) -&gt; ...</div><div><br></div><div>Now, one issue with your runtime match function is that it returns two parameters, in that example, but might return a different number of arguments in other cases. So the type would have to be something vararg-ish.</div>
<div><div><br></div><div>match :: (MonadPlus m, ServerMonad m, MatchArgs c) =&gt; String -&gt; (c -&gt; m a) -&gt; m a</div></div><div><br></div><div>Where the type &#39;c&#39; hopefully matches up with the values that &quot;/foo/:bar/#int/&quot; returns. Despite this annoyance, this could be implemented using ServerMonad today.</div>
<div><br></div><div>One advantage of using the QuasiQuote method instead of a plain string is that the QuasiQuote method would parse the string at compile time and generate a matcher with a specific type signature. If the handler function had different arguments you would get a compile time error.</div>
<div><br></div><div>I would very much like to see a QuasiQuote version of match added to the ServerMonad library. It&#39;s basically just syntactic sugar for using dir / path /etc. But it is very nice sugar.</div><div><br>
</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;">Is it always true that compile-time link checking is<br>
possible/desirable? All of these solutions also imply that each handler<br>
knows where it&#39;s hung on the routing table so that it can resolve<br>
relative URLs, etc.</blockquote><div><br></div><div>With URLT, the handler &#39;knows where it is hung&#39; because it is stored transparently by the URLT monad, and accessed via showURL. So in the blog library, for example, you would just write:</div>
<div><br></div><div> u &lt;- showURL (ViewPost 1)</div><div><br></div><div>showURL magically knows where it is hung. If the blog was incorporated into a master site:</div><div><br></div><div>data App = Blog BlogURL</div><div>
<br></div><div>it would be told like:</div><div><br></div><div>app (Blog blogURL) = nestURL Blog $ blogHandler blogURL</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;">

IMO no matter what a link-checker should be lightweight enough that you<br>
can refactor easily without rewriting a bunch of static tables; you<br>
should be able to move an entire &quot;subtree&quot; to another place in the<br>
routing table in O(1) time.<br></blockquote><div><br></div><div>That should be easy with URLT. You could just change the type:</div><div><br></div><div><div>data App = MyBlog BlogURL</div><div><br></div><div>Now, if you forget to update the nestURL handler from Blog to MyBlog, you will get a compile time error. </div>
<div><br></div><div>Or, might might not change the type at all. You might just change the AsURL instance so that </div><div><br></div><div> (Blog (ViewPost 1))</div><div><br></div><div>generates:</div><div><br></div><div>
/myblog/viewpost/1</div><div><br></div><div>So, you have the flexibility to change the way your urls look by modifying two functions and leaving everything else alone.</div></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;">

This rabbit hole goes pretty deep though; if you&#39;re serious about the<br>
bondage and discipline approach you&#39;d want to ensure that you can check<br>
query parameters; i.e. &quot;&#39;/foo&#39; takes a mandatory &#39;bar&#39; integer parameter<br>
on the queryString and an optional &#39;sort&#39; parameter which must be either<br>
&#39;asc&#39; or &#39;desc&#39;&quot;, etc. At some point I have to wonder: is the medicine<br>
worse than the disease?<br></blockquote><div><div><br></div><div>Using the the URLT, the issue of the mandatory and optional arguments is simple.</div><div><br></div><div>You would have types like:</div><div><br></div><div>
data Sort = Asc | Desc</div><div>data WebURL = Foo Int (Maybe Sort)</div><div><br></div><div>instance AsURL Foo where</div><div>   toURLS (Foo i mSort) = showString &quot;Foo?&quot; . showString &quot;bar=&quot; . shows n . showString &quot;;&quot; . (case mSort of Nothing -&gt; id ; (Just Asc) -&gt; showString &quot;sort=asc;&quot; ; (Just Desc) -&gt; showString &quot;sort=desc;&quot;)</div>
<div>   fromURLC = -- skipped for brevity</div><div><br></div></div><div>It seems like the medicine is quite nice here. Instead of having to remember if the &#39;bar&#39; is a parameter or a path component, we just do:</div>
<div><br></div><div>showURL (Foo 1 (Just Asc))</div><div><br></div><div>The details of how that looks are centralized in a single spot. if we decide that we want the url to instead look like:</div><div><br></div><div> /foo/1?sort=desc</div>
<div><br></div><div>We can change it in one spot instead of having to:</div><div>  1. change it everywhere</div><div>  2. tell everyone about the change and get them to remember that it changed</div><div><br></div><div>Additionally, with the typed version you can never forget the mandatory &#39;bar&#39; parameter because the compiler will tell you.</div>
<div><br></div><div>Writing the toURLS and fromURLC instances by hand gives you very precise control over how the URLs look -- though it can be a bit tedious. However I think some simple combinators could help with that. Either a DSL for constructing things by hand, or something based on a yesod style quasiquoter.</div>
<div><br></div><div>- jeremy</div></div>