I'm using the servant
library for my JSON API. I need some help to get a ServerT MyAPI (ReaderT a IO)
monad stack working.
Here's an example using ReaderT
, but without integrating it with servant:
-- this code works
type TestAPI =
"a" :> Get '[JSON] String
:<|> "b" :> Get '[JSON] String
test2 :: EitherT ServantErr IO String
test2 = return "asdf"
testServer :: Int -> Server TestAPI
testServer code = test :<|> test2
where
test :: EitherT ServantErr IO String
test = liftIO $ runReaderT (giveMeAMessage) code
-- this is contrived. In my real application I want to use a Reader for the database connection.
giveMeAMessage :: ReaderT Int IO String
giveMeAMessage = do
code <- ask
name <- liftIO $ getProgName
return $ show code <> name
So, now I would like to get it working with ServerT, following the example in this article.
-- this code doesn't compile
testServerT :: ServerT TestAPI (ReaderT Int IO)
testServerT = test :<|> test
where
test :: EitherT ServantErr (ReaderT Int IO) String
test = lift $ giveMeAMessage
testServer' :: Int -> Server TestAPI
testServer' code = enter (Nat $ liftIO . (`runReaderT` code)) testServerT
I get the following error:
server/Serials/Route/Test.hs:43:15:
Couldn't match type ‘EitherT ServantErr (ReaderT Int IO) String’
with ‘ReaderT Int IO [Char]’
Expected type: ServerT TestAPI (ReaderT Int IO)
Actual type: EitherT ServantErr (ReaderT Int IO) String
:<|> EitherT ServantErr (ReaderT Int IO) String
In the expression: test :<|> test
In an equation for ‘testServerT’:
testServerT
= test :<|> test
where
test :: EitherT ServantErr (ReaderT Int IO) String
test = lift $ giveMeAMessage
Failed, modules loaded: none.
How can I get rid of the error?
Followup question: I understand monad transformers generally, but I'm lost. What topics or links should I study to know enough to answer my own question?
Recent versions of servant have simplified this a lot. See Using a custom monad in the servant cookbook.
After help from lots of folks and hours of reading random things here's a complete example of using Servant with ReaderT, done as fancy as I can (using newtype, and GeneralizedNewtypeDeriving, I also added ExceptT for exceptions).
You were almost there, test should be:
As for your other questions, I don't have time to answer just now but us servant developers should probably make it easier or better documented.
Could you please read through the source for whichever part confuses you, and then ask specific questions?