http4s / rho

A self documenting DSL built on http4s
Other
295 stars 65 forks source link

help me understand & document the shapelessness of rho #58

Open aryairani opened 9 years ago

aryairani commented 9 years ago

"Papa, where do TypedPaths come from?" "Well, that story starts a long time ago, in a distant island kingdom..."

bryce-anderson commented 9 years ago

The primary use of shapeless in rho is for the HList type signatures. Honestly it wouldn't be too hard to just make a phantom type that can be consed like a HList and use a normal List[Any] for the storage, but shapeless is a transient dependency of rho so why not use it?

TypedPath holds the 'phantom' HList type signature and stores the path AST nodes untyped. It is known by the HList type signature what order parameters will be produced by the path and therefor they are safe to cast to whatever they need to be. The other typed ASTs (query etc..) work the same way.

The power of the HList phantom type comes to bear when making an action using a RouteExecutable (many of the action building type extend this so long as they have enough information to meaningfully match a request). Here the phantom type is used to typecheck the function used to build the action. In this process you need a CompileService in scope to get anything useful. A CompileService can generate documentation, make a actual route etc.

That should get you started.

aryairani commented 9 years ago

Side question: what is an action, the part after the |>>, or the whole thing?

GET / "puppy" / "bySSN" / pathVar[SSN] |>> { owner: SSN => Ok(Puppy.cutest.filter(_.lovingOwner === owner).asJson) }

Edit: and what is this expression even doing to cause the service to remember it?

bryce-anderson commented 9 years ago

The whole thing. A RhoAction just holds all the bits of the path definition, the function F used to execute it (or whatever fits the HListToFunc paired with it: F could be a Task[Response] or even a constant that has a EntityEncoder), and the 'converter' which is the HListToFunc . In turn the HListToFunc must find a ResultMatcher which is where some of the 'status code' magic happens, but its primary purpose is take the Request and whatever the result of the function F (or whatever it is) and convert that to a Task[Response] to be shipped back to the client.

If you look at the type signature of |>> you'll notice that it takes an implicit CompileService which will receive the final RhoAction and can then do whatever it likes with it. The CompileService can be stateful and save the route (in the typical case, a route tree for matching against a request), but it doesn't have to be. For example it could simply compile it into a Request => Task[Response]] and you get to do what you want with it, or it could generate some documentation (swagger!), whatever you like.

aryairani commented 9 years ago

P.S. Get it? The distant island kingdom is the UK.

aryairani commented 9 years ago

Is there more than one CompileService? I could only find one, in RhoService, so I'm probably looking for the wrong thing. If one wanted to create a new output format, what would the critical format-specific pieces be?

aryairani commented 9 years ago

Wait, I guess the critical piece is overriding RhoService#append.