I am back again trying to learn Haskell and, oh boy it is difficult! I am a trying to do a simple mongoDB insertion inside a Scotty endpoint. Problem is the type return by the insert function is not accepted in the Scotty do statement. The program is quite simple:
{-# LANGUAGE OverloadedStrings #-}
import Web.Scotty
import Data.Monoid (mconcat)
import Control.Monad.Trans(liftIO,lift,MonadIO)
import System.IO
import Data.Text.Lazy.Encoding (decodeUtf8)
import Data.Text.Lazy (pack,unpack)
import Data.Maybe
import Data.Time.Clock.POSIX
import Database.MongoDB (Action, Document, Document, Value, access,
allCollections,insert, close, connect, delete, exclude, find,
host,findOne, insertMany, master, project, rest,
select, liftDB, sort, Val, at, (=:))
main :: IO ()
main = scotty 3000 $ do
post "/logs" $ do
id <- liftIO $ getTimeInMillis
b <- body
let decodedBody = unpack(decodeUtf8 b)
i <- liftIO $ insertLog id decodedBody
text $ "Ok"
--setup database connection
run::MonadIO m => Action m a -> m a
run action = do
pipe <- liftIO(connect $ host "127.0.0.1")
access pipe master "data" action
getTimeInMillis ::Integral b => IO b
getTimeInMillis = round `fmap` getPOSIXTime
insertLog::MonadIO m => Int -> String -> Action m Value
insertLog id body = run $ insert "logs" ["id" =: id, "content" =: body]
the problem comes in the line
i <- liftIO $ insertLog id decodedBody
And the type error is
Expected type: Web.Scotty.Internal.Types.ActionT
Data.Text.Internal.Lazy.Text IO Value
Actual type: Action m0 Value
Any help or tip will be welcome!
I see a different error message with that code. Maybe you made some changes (like adding
liftIO
).In the line:
the
liftIO
function expects a genuineIO
action, of typeIO a
for somea
. However, the expressioninsertLog id decodedBody
doesn't represent an IO action. It is Mongo action of typeAction m Value
for somem
that has aMonadIO
constraint. You need to use some function run MongoAction
values inIO
. It looks like you've already written such a function, namedrun
. It's written for a generalMonadIO m
but can be specialized to:so if you first run your Mongo action (to turn it into
IO
) and then lift that action (to run it in the Scotty action underpost
), the following should type check:Update: Whoops! I missed the
run
in theinsertLog
function. You either want to write:OR you want to write:
That will avoid the double-
run
problem.The reason
run
didn't work as intended in your original code is a little complicated...The problem is that
run
has flexibility to convert its Mongo action to many possible monads by returningm a
for anym
that supportsMonadIO m
. Because you gaveinsertLog
a type signature with return typeMonadIO m' => Action m' Value
(where I changed the variable to keepm
andm'
distinct), the type checker matched the return type ofrun
to the return type ofinsertLog
:by setting
a ~ Value
andm ~ Action m'
. So, yourrun
ininsertLog
was actually used with the following bizarre type:Normally, this would have caused a type error, but the type of
insert
is also flexible. Instead of returning an action of typeAction IO Value
, which would be the "usual" type, it happily adapted itself to return an action of typeAction (Action IO) Value
to match whatrun
was expecting.