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
liftIOfunction expects a genuineIOaction, of typeIO afor somea. However, the expressioninsertLog id decodedBodydoesn't represent an IO action. It is Mongo action of typeAction m Valuefor somemthat has aMonadIOconstraint. You need to use some function run MongoActionvalues inIO. It looks like you've already written such a function, namedrun. It's written for a generalMonadIO mbut 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
runin theinsertLogfunction. You either want to write:OR you want to write:
That will avoid the double-
runproblem.The reason
rundidn't work as intended in your original code is a little complicated...The problem is that
runhas flexibility to convert its Mongo action to many possible monads by returningm afor anymthat supportsMonadIO m. Because you gaveinsertLoga type signature with return typeMonadIO m' => Action m' Value(where I changed the variable to keepmandm'distinct), the type checker matched the return type ofrunto the return type ofinsertLog:by setting
a ~ Valueandm ~ Action m'. So, yourrunininsertLogwas actually used with the following bizarre type:Normally, this would have caused a type error, but the type of
insertis 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) Valueto match whatrunwas expecting.