-
-
Notifications
You must be signed in to change notification settings - Fork 414
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Update docs related to hoistServer #824
Conversation
0479fe4
to
15cc4f5
Compare
``` | ||
|
||
We unfortunately can't use `readerServerT` as an argument of `serve`, because | ||
`serve` wants a `Server ReaderAPI`, i.e., with handlers running in `Handler`. But there's a simple solution to this. | ||
|
||
### Enter `enter` | ||
### Welcome `hoistServer` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, "Hoistserver hoistServer
" and "Welcome welcome
" just don't have that ring to them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I used some time, but I didn't come up with any pun on hoist :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Up with hoist
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm happy enough with this. If there's anything confusing for some users, we can just expand on whatever's not clear enough then.
Was going to add this at #804 but it may be better here, as the documentation doesn't quite cover all I needed to replace the use of I've just adapted a server, and I'm having to rewrite all my API types. This change means my handlers can no longer have type annotations like The enforced use of the type family is causing difficulties. With this change, the standard way of joining fragments of an API, using So given the following example lifted almost directly out of the aforementioned server code (please excuse the relative cruft; I've just got it compiling with the changes),
In addition, The API for pages to be served is fairly straightforward. type PageAPI
= "index" :> Capture "page" Page :> Get '[HTML] Page
- :<|> "temp_pages" :> "socials" :> "dinner" :> CRUD "eventid" SocialEvent BookingAmend ("baId" `Tagged` Text) Signup
+ :<|> "temp_pages" :> "socials" :> "dinner" :> DinnerCRUD
- :<|> "admin" :> "dinner" :> Capture "eventid" SocialEvent :> "diets" :> Get '[HTML,JSON] DinnerDiets
+ :<|> "admin" :> "dinner" :> DietsEndpoint
:<|> "stylesheets" :> Capture "css" StyleSheet :> Get '[CSS] Css
+
+type DinnerCRUD = CRUD "eventid" SocialEvent BookingAmend ("baId" `Tagged` Text) Signup
+type DietsEndpoint = Capture "eventid" SocialEvent :> "diets" :> Get '[HTML,JSON] DinnerDiets These handlers requiring natural transformations now need to be provided with API endpoints. pageHandler :: Server PageAPI
pageHandler = pure
- :<|> serveDinner
+ :<|> serveDinner @DinnerCRUD @'[]
- :<|> serveDiets
+ :<|> serveDiets @DietsEndpoint @'[]
:<|> serveCss In this example, such handlers must interface with the database (accessed using -serveBackend :: ∀ backend a b . backend -> (a -> ReaderT backend IO b) -> a -> Handler b
-serveBackend backend = enter $ transformDB backend
+serveBackend
+ :: ∀ api context backend .
+ HasServer api context
+ => backend
+ -> ServerT api (ReaderT backend IO)
+ -> ServerT api Handler
+serveBackend backend = hoistServerWithContext (Proxy @api) (Proxy @context) $
+ transformDB backend
-transformDB :: ∀ backend . backend -> ReaderT backend IO :~> Handler
-transformDB backend = NT $ liftIO . flip runReaderT backend
+transformDB :: ∀ backend a . backend -> ReaderT backend IO a -> Handler a
+transformDB backend = liftIO . flip runReaderT backend I find it problematic that the The serveDiets
- :: SocialEvent
- -> Handler DinnerDiets
-serveDiets = serveBackend (readToUnknown . diets)
+ :: ∀ api context .
+ ( HasServer api context
+ , ServerT api (ReaderT SqlBackend IO)
+ ~ (SocialEvent -> ReaderT SqlBackend IO DinnerDiets)
+ )
+ => ServerT api Handler
+serveDiets = serveBackend @api @context backend (readToUnknown . diets)
diets
:: ∀ m . MonadIO m
=> SocialEvent
-> ReaderT SqlReadBackend m DinnerDiets (Implementation of |
I believe the issue is I'd been converting everything to operate in I'll probably have to refactor anyway as that is probably a more flexible approach, but it is not the approach reflected in the docs. Edit: Also |
@dbaynard I think it is (but maybe not clearly enough?) covered in this section of the tutorial, where handlers live in some monad that's not |
I believe you understand me correctly. I'm going to refactor, and then I may have feedback on the tutorial. My first instinct is that the section you linked should immediately follow the section about to |
Hmm that would make sense. The current structure is mostly due to me wanting the reader to get a good grip on |
I suspect ‘Using another monad for your handlers’ could fit immediately before ‘response headers’ without compromising such an intuition for In learning to use the From speaking with other developers and reading articles and code it seems very common in larger, more complicated servers for the entire set of handlers to operate in some sort of So I'll bear all this in mind as I make these changes, and will happily contribute to documentation once I figure it out. |
@dbaynard do I understand you right, the section I wrote in this PR is good (it mentions |
Yes - at least for me. It's clear on how to transform between monads but until I needed to do so it wasn't clear why I'd want to do that. Directly following the Handler section makes it look like the idiomatic way to add extra functionality. Actually, the haddocks for Once I can identify what wasn't obvious, I'll make a docs PR, then. |
A quick, barely related note: we'll soon have the beginning of a cookbook up on readthedocs, which hopefully will clearly illustrate in greater detail how to do a bunch of things like the ones you were looking to do but couldn't figure out how. See #867 for more. |
Just a heads up — I had some time to work on the project to which I referred above, and completed the refactor. With hindsight, the following things would have helped, at the time.
So I'll submit a documentation PR (likely including the cookbook, too). Keep up the good work! |
Lovely, thanks in advance for that! |
Related 3rd point: Hmm. I'm personally 👎 on FPCo's exception advice. It's not something community at large agrees to be a good general practice ( EDIT: OTOH please share the recipe, we can make it better when we see it! |
Agreed for exceptions, the less I deal with them the better, but servant will never make that decision for users anyway, so we don't have to agree on this :) |
my 2c: docs should use ReaderT with IO since it's more practical :) |
I'd like to disagree strongly but most people do seem to be working with an application monad with a |
No description provided.