On Fri, Mar 26, 2010 at 9:30 AM, Jeremy Shaw <jeremy@n-heptane.com> wrote:
On Fri, Mar 26, 2010 at 10:30 AM, Michael Snoyman <michael@snoyman.com> wrote:
Well, given that as a criterion, I agree with the rest of your analysis entirely. However, I think we're looking at the purpose of fromPathSegments very differently. I'm not quite certain I understand why we would want to output the unconsumed segments; if something is unconsumed, then it seems like it's an invalid URL and should fail.

In your example, if I request "/Foo/5/6/7", fromPathSegments would return (Right (Foo 5 6), ["7"]); but what is going to consume that 7 now? The use case I envisioned for something like this is:

data BlogRoutes = ...
data MySite = MyHome | MyBlog BlogRoutes
fromPathSegments ("blog":rest) = MyBlog `fmap` fromPathSegments


But what if you had, 

data BlogRoutes = ...
data Foo = ...
data MySite = MyHome | MyBlog Foo BlogRoutes

Where the MyBlog constructor has *two* arguments. In theory you want to write something like:

fromPathSegments ("MyBlog":rest) = MyBlog `fmap` fromPathSegments ?? `ap` fromPathSegments ???

The first fromPathSegments will parse the 'Foo' argument and the second fromPathSegments will parse the BlogRoutes argument. To make things more interesting, let's assume that Foo and BlogRoutes were defined in 3rd party modules that don't even know about each other your app.

The problem is, what arguments do you pass to each fromPathSegments call? The first call to fromPathSegments is going to consume some of the path segments, and the second call will consume the remaining. But we do not have enough information here to know in advance how to split up 'rest' between the two calls. Instead we to run the first fromPathSegments and have it tell us what part it did not consume.

If what I have said still does not make sense, then try this exercise:

create 3 modules, one each for:

data BlogRoutes = BlogHome
data Foo = Foo Int | Bar Char Int
data MySite =  MyBlog Foo BlogRoutes

now create fromPathSegments instances for each of those routes. I think you will find that it is difficult to implement the instance for MySite. Finally, can you now change the Foo type to:

data Foo = Foo Int Int | Bar Int Char Int

by *only* modifying the Foo module, and without breaking the MySite module?

Regarding the type:

data Foo = Foo Int Int

attempting to parse:

"/Foo/5/6/7"

I think that should be handled in, fromPathInfo :: (PathInfo u) => String -> Failing u, when it calls fromPathSegments.

At that point in time we know that all the segments should have been consumed... so if there is left over junk, something is wrong. 

The latest version of the code is now at:

http://src.seereason.com/web-routes/

I did the renaming but have not made all the other changes yet.

- jeremy

Can you give me any real-world examples where you would have URL routes built up like that? It seems like this is an optimization for the abnormal case.

Michael