我又回到了努力学习Haskell和,哦,孩子,这是困难的!我试图在Scotty端点中做一个简单的mongoDB插入。问题是insert函数的类型返回在Scotty语句中不被接受。这个程序非常简单:
{-# 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]问题就在这条线上
i <- liftIO $ insertLog id decodedBody类型错误是
Expected type: Web.Scotty.Internal.Types.ActionT
Data.Text.Internal.Lazy.Text IO Value
Actual type: Action m0 Value欢迎任何帮助或建议!
发布于 2020-08-01 15:21:47
我看到了一条不同的错误信息。也许您做了一些更改(比如添加liftIO)。
• Couldn't match type ‘Control.Monad.Trans.Reader.ReaderT
Database.MongoDB.Query.MongoContext m0 Value’
with ‘IO a0’
Expected type: IO a0
Actual type: Action m0 Value在队伍中:
i <- liftIO $ insertLog id decodedBodyliftIO函数需要一个真正的IO操作,某些a的类型为IO a。但是,表达式insertLog id decodedBody并不表示IO操作。对于具有Action m Value约束的某些m来说,是MonadIO类型的Mongo操作。您需要在Action中使用一些函数运行Mongo的IO值。看起来您已经编写了这样一个函数,名为run。它是为通用MonadIO m编写的,但可以专门用于:
run :: Action IO a -> IO a因此,如果您首先运行Mongo操作(将其转换为IO),然后解除该操作(在post下的Scotty操作中运行该操作),则应键入下列命令:
i <- liftIO $ run $ insertLog id decodedBody更新:喔!我错过了run函数中的insertLog。你要么想写:
-- use "run" here
main = do
...
i <- liftIO $ run $ insertLog id decodedBody
-- but no "run" here
insertLog::MonadIO m => Int -> String -> Action m Value
insertLog id body = insert "logs" ["id" =: id, "content" =: body]或您想要写:
-- no "run" here
main = do
...
i <- liftIO $ insertLog id decodedBody
-- change the type signature and use "run" here
insertLog :: Int -> String -> IO Value
insertLog id body = run $ insert "logs" ["id" =: id, "content" =: body]这将避免双run问题。
run没有按照原来代码的意图工作的原因有点复杂.
问题是,run具有灵活性,可以通过为任何支持MonadIO m的m返回m a,从而将其Mongo操作转换为许多可能的monads。因为您为insertLog提供了一个类型签名,返回类型为MonadIO m' => Action m' Value (在这里,我更改了变量以保持m和m'不同),所以类型检查器将run的返回类型与返回类型insertLog相匹配。
m a ~ Action m' Value通过设置a ~ Value和m ~ Action m'。因此,run in insertLog实际上是与以下奇怪类型一起使用的:
run :: Action (Action m') Value -> Action m' Value通常,这会导致类型错误,但是insert的类型也是灵活的。它没有返回Action IO Value类型的操作(这将是“通常”类型),而是高兴地调整自己,返回Action (Action IO) Value类型的操作,以匹配run所期望的操作。
https://stackoverflow.com/questions/63206279
复制相似问题