【问题标题】:Haskell scotty Action to IOHaskell scotty Action to IO
【发布时间】:2020-08-01 14:24:37
【问题描述】:

我又回来尝试学习 Haskell 了,天哪,这很难! 我正在尝试在 Scotty 端点内进行简单的 mongoDB 插入。问题是在 Scotty do 语句中不接受插入函数返回的类型。程序很简单:

{-# 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

欢迎任何帮助或提示!

【问题讨论】:

    标签: mongodb haskell scotty


    【解决方案1】:

    我看到与该代码不同的错误消息。也许你做了一些改变(比如添加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 decodedBody
    

    liftIO 函数需要真正的 IO 操作,对于某些 a,类型为 IO a。但是,表达式insertLog id decodedBody 并不代表 IO 操作。对于具有MonadIO 约束的某些m,这是Action m Value 类型的Mongo 操作。您需要使用一些函数运行 IO 中的 Mongo Action 值。看起来您已经编写了一个名为run 的函数。它是为一般MonadIO m 编写的,但可以专门用于:

    run :: Action IO a -> IO a
    

    因此,如果您首先运行 Mongo 操作(将其转换为 IO)然后解除该操作(在 post 下的 Scotty 操作中运行它),则应键入以下检查:

    i <- liftIO $ run $ insertLog id decodedBody
    

    更新: 哎呀!我错过了insertLog 函数中的run。你要么想写:

    -- 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 可以灵活地将其 Mongo 操作转换为许多可能的 monad,方法是为支持 MonadIO m 的任何 m 返回 m a。因为您给了insertLog 一个返回类型为MonadIO m' =&gt; Action m' Value 的类型签名(我将变量更改为保持mm' 不同),所以类型检查器将run 的返回类型与@ 的返回类型相匹配987654355@:

    m a ~ Action m' Value
    

    通过设置a ~ Valuem ~ Action m'。因此,insertLog 中的 run 实际上与以下奇异类型一起使用:

    run :: Action (Action m') Value -> Action m' Value
    

    通常,这会导致类型错误,但insert 的类型也很灵活。它没有返回 Action IO Value 类型的操作,这将是“通常的”类型,而是很高兴地调整自己以返回 Action (Action IO) Value 类型的操作以匹配 run 的预期。

    【讨论】:

    • 完全正确!代码按照您的建议运行。但是我不是很明白,insertLog已经使用了run函数。为什么那已经不起作用了?无论如何,非常感谢你,Haskellers 摇滚!
    • 糟糕。我错过了。我已经更新了我的答案。您将希望通过使用两种替代方法之一来避免双重run。使用两个runs,它会进行类型检查,但实际上你会执行一些你并不真正想要的到 Mongo 数据库的奇怪双连接。
    猜你喜欢
    • 2018-11-15
    • 2015-06-11
    • 2015-02-04
    • 2019-03-28
    • 2015-04-14
    • 1970-01-01
    • 2020-02-03
    • 2012-03-28
    • 1970-01-01
    相关资源
    最近更新 更多