-
-
Notifications
You must be signed in to change notification settings - Fork 410
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
How to use custom Monad with wai's Application
?
#1544
Comments
Have you seen the https://hackage.haskell.org/package/wai-transformers-0.0.5/docs/Network-Wai-Trans.html package? I wonder if it could help |
Well, the problem isn't how to transform my transformer into app :: Application
app = \req resp -> do
c <- newChan
serve api (hoistServer api (nt (AppState c)) $ server) req resp
where
nt :: AppState -> AppM a -> Handler a
nt s x = flip runReaderT s x So the natural transformation here is |
I'd say the core issue here is not in the “custom approach” monad per se, but rather in the way the
which would allow you to access your Reader environment. There may be some difficulty I didn't think of, but |
Yeah, I think that's true. The reason I use WAI application is because I utilize server-sent events via https://hackage.haskell.org/package/wai-extra-3.1.8/docs/Network-Wai-EventSource.html
Do you mean This seems to be the current implementation: servant/servant-server/src/Servant/Server/Internal.hs Lines 591 to 593 in 7ef9730
Is there anything else special about the |
I gave it a shot and I can't seem to find a way to implement this. The problem is: hoistServerWithContext :: Proxy RawHandler
-> Proxy context
-> (forall x. m x -> n x)
-> ServerT RawHandler m
-> ServerT RawHandler n This has one natural transformation. However, given type ApplicationT m = Request -> (Response -> m ResponseReceived) -> m ResponseReceived In order to transform hoistApplicationT :: ( Monad m
, Monad n
) => (forall a. m a -> n a)
-> (forall a. n a -> m a)
-> ApplicationT m
-> ApplicationT n
hoistApplicationT to from app req resp =
to $ app req (from . resp) |
Ok, I made it compile this way... instance HasServer RawHandler context where
type ServerT RawHandler m = Tagged m (Request -> (Response -> IO ResponseReceived) -> m ResponseReceived)
hoistServerWithContext _ _ n s =
Tagged $ \req resp -> (\app -> n $ app req resp) . unTagged $ s
route Proxy _ rawApplication = RawRouter $ \ env request respond -> runResourceT $ do
r <- runDelayed (rawApplication) env request
liftIO $ go r request respond
where go r request respond = case r of
Route app -> do
res <- runHandler $ (untag app) request (respond . Route)
case res of
Right r' -> pure r'
Left e -> liftIO $ respond $ FailFatal e
Fail a -> liftIO $ respond $ Fail a
FailFatal e -> liftIO $ respond $ FailFatal e Here we have to |
It has historically served two purposes: file serving and "low level endpoints" that can do anything they want with requests/responses (websockets, SSE, and all kinds of things that require direct access to arbitrary bits of the request, embedding an app indeed, etc). |
OK, a few comments / thoughts:
I have the following working here (which is very similar to what you have): instance HasServer RawM context where
type ServerT RawM m = Request -> m Response
route :: Proxy RawM -> Context context -> Delayed env (Request -> Handler Response) -> Router env
route _ _ handleDelayed = RawRouter $ \env request respond -> runResourceT $ do
routeResult <- runDelayed handleDelayed env request
liftIO $ do
ret <- case routeResult of
Route handler -> runHandler (handler request) >>=
\case
Left e -> pure $ FailFatal e
Right a -> pure $ Route a
Fail e -> pure $ Fail e
FailFatal e -> pure $ FailFatal e
respond ret
hoistServerWithContext _ _ = fmap I am still pondering whether that is the design we want, though. |
Say, you have an API built according to https://docs.servant.dev/en/stable/cookbook/using-custom-monad/UsingCustomMonad.html
Now you additionally have a
Raw
endpoint and want access to the ReaderT environment in the handler, which now is of typeApplication
and notHandler
.Whatever I tried, I could not make it work (of course, I can pass the environment explicitly to the
server
function so I can justrunReaderT
, but that's not the "using custom monad" approach).Example code:
Error:
The text was updated successfully, but these errors were encountered: