如何在Happstack中使用"IO String"作为HTTP响应?



>我正在使用HDBC从数据库中检索数据,然后尝试使用Happstack将此数据发送到Web客户端。

myFunc :: Integer -> IO String
myFunc = ... fetch from db here ...
handlers :: ServerPart Response
handlers =
do decodeBody (defaultBodyPolicy "/tmp/" 0 1000 1000)
msum [ 
dir "getData" $ ok $ toResponse $ myFunc $ toInteger 1
]
mainFunc = simpleHTTP nullConf handlers

当我构建上面的代码时,我收到此错误:

没有 (ToMessage (IO 字符串)) 的实例因使用 "响应">

我试了什么?

  1. 我尝试将IO String转换为String(例如使用liftIO)。
  2. 我试图在这里找到任何类似的问题。
  3. 我试图在Happstack速成课程中找到类似的例子。
  4. 我用谷歌搜索了所有不同组合的所有相关关键字。

提前谢谢。

你必须围绕这样一个事实来设计你的handlers,即从数据库中获取是一个神奇的操作,可能不会给你期望的。(例如,数据库可能会崩溃。这就是为什么它的结果是作为IO,这是单子的一个特例。

monad是一个脖子很窄的罐子,甚至很窄,一旦你把东西放进去,你就无法解开它。(除非它碰巧也是comonad,但那是另一回事,IOServerPart都不是这样。因此,您永远不会将IO String转换为String。不是说你不能,而是你的程序会变得不正确。

你的情况有点棘手,因为你有两个monad在那里发挥作用:IOServerPart。幸运的是,ServerPart建立在IO的基础上,它是">更大的",在某种意义上可以吸收IO:我们可以把一些IO放进一个ServerPart,它仍然会是一个ServerPart,所以我们可以把它交给simpleHTTP。在happstack中,这种转换可以通过require函数来完成,但也有一个更通用的解决方案,涉及单元变压器lift

 

让我们先看一下require的解决方案。它的类型(简化为我们的情况)是:

IO (Maybe a) -> (a -> ServerPart r) -> ServerPart r

— 因此,它需要一个带有一些参数的IO罐,并使其适合存在于ServerPart罐中的函数。我们只需要稍微调整类型并创建一个lambda 抽象

myFunc :: Integer -> IO (Maybe String)
myFunc _ = return . Just $ "A thing of beauty is a joy forever."
handlers :: ServerPart Response
handlers = require (myFunc 1) $ x ->
do decodeBody (defaultBodyPolicy "/tmp/" 0 1000 1000)
msum [
dir "getData" $ ok $ toResponse x
]
mainFunc = simpleHTTP nullConf handlers

如您所见,我们必须进行 2 项修改:

  • 调整myFunc,使其返回Maybe,根据require的需要。这是一个更好的设计,因为myFunc现在可能会以两种方式失败:

    • 作为Maybe,它可以返回Nothing,这意味着404之类的。这种情况相当普遍。
    • 作为IO,它可能会出错,这意味着数据库崩溃了。现在是提醒 DevOps 团队的时候了。
  • 调整handlers,使myFunc在它们外部。可以更具体地说:来自handlers的抽象myFunc。这就是为什么这种语法被称为lambda抽象

 

require是专门处理happstack中的单子的方法。不过,一般来说,这只是单子转换为较大单子的情况,这是通过lift.lift的类型(再次简化)为:

IO String -> ServerPart String

因此,我们可以将myFunc 1 :: IO Stringlift到正确的 monad 上,然后像往常一样与>>=组合:

myFunc :: Integer -> IO String
myFunc _ = return $ "Its loveliness increases,.."
handlers :: ServerPart Response
handlers = lift (myFunc 1) >>= x ->
do decodeBody (defaultBodyPolicy "/tmp/" 0 1000 1000)
msum [
dir "getData" $ ok $ toResponse x
]
mainFunc = simpleHTTP nullConf handlers

就这么简单。我再次使用了相同的lambda抽象技巧,但您也可以使用do表示法

myFunc :: Integer -> IO String
myFunc _ = return $ "...it will never pass into nothingness."
handlers :: ServerPart Response
handlers = do
x <- lift (myFunc 1)
decodeBody (defaultBodyPolicy "/tmp/" 0 1000 1000)
msum [
dir "getData" $ ok $ toResponse x
]
mainFunc = simpleHTTP nullConf handlers

 

附言回到大罐子和小罐子的故事:你可以把IO放进ServerPartServerPart正是因为它也是一个IO的monad——它是MonadIO类的一个实例。这意味着你可以在IO中做的任何事情你也可以在ServerPart中做,而且,除了一般lift,还有一个专门的liftIO功能,你可以在我lift使用的任何地方使用。您可能会遇到许多其他 monads 是MonadIO实例,因为它是在大型应用程序中构建代码的便捷方式。

在你的特殊情况下,我会坚持require的方式,因为我认为这就是happstack的设计者想要这样做的方式。不过,我对happstack不是特别了解,所以我可能是错的。

 

就是这样。快乐黑客!

相关内容

  • 没有找到相关文章

最新更新