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

asp*_*asp 1 haskell happstack hdbc

我正在使用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
Run Code Online (Sandbox Code Playgroud)

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

使用"toResponse"时没有(ToMessage(IO String))的实例

我尝试了什么?

  1. 我试图将其转换IO StringString(liftIO例如使用).
  2. 我试图在这里找到任何类似的问题.
  3. 我试图在Happstack Crash Course中找到一个类似的例子.
  4. 我用各种不同的组合搜索了所有相关的关键字.

提前致谢.

Ign*_*rov 6

您必须设计handlers一个事实,即从数据库中获取是一个神奇的动作,可能无法满足您的期望.(例如,您的数据库可能会崩溃.)这就是为什么它的结果是作为IO一个monad的特殊情况.

monad是一个颈部非常狭窄的罐子,即便如此,一旦你把东西放在那里,你就无法输出它.(除非它也是一个comonad,但是这是一个整体的另一个故事,而不是与本案IO也没有用ServerPart.)所以,你永远不会转换的IO String一个String.不是你不能,但你的程序会变得不正确.

你的情况有点棘手,因为你在那里玩两个单子:IOServerPart.幸运的是,ServerPart建立在IO,它是" ",并可以在一定意义上,吸收 IO:我们可以把一些IOServerPart,这将是一个ServerPart还在,于是我们可以给它simpleHTTP.在happstack,这种转换可以通过require函数完成,但也有一个更通用的解决方案,涉及monad变换器lift.

 

我们先来看看解决方案require.它的类型(简化为我们的例子)是:

IO (Maybe a) -> (a -> ServerPart r) -> ServerPart r
Run Code Online (Sandbox Code Playgroud)

- 所以,它需要一个IO带有一些参数的jar,并使它适合生活在ServerPartjar中的函数.我们只需要稍微调整类型并创建一个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
Run Code Online (Sandbox Code Playgroud)

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

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

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

 

requirehappstack具体处理monad的方法.一般来说,这只是 monad转换为更大的monad的情况,这是通过lift.lift(再次,简化)的类型是:

IO String -> ServerPart String
Run Code Online (Sandbox Code Playgroud)

所以,我们可以将正确的monad liftmyFunc 1 :: IO String价值,然后>>=像往常一样组成:

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
Run Code Online (Sandbox Code Playgroud)

就如此容易.我再次使用相同的lambda抽象技巧,但你也可以使用do-notation:

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
Run Code Online (Sandbox Code Playgroud)

 

PS回到大型和小型罐子的故事:你可以投入IOServerPart正是因为ServerPart它也是一个IO单子 - 它是一个MonadIO的实例.这意味着,任何你能做到IO,你也可以做ServerPart,而且,除了一般的lift,有一个专门的liftIO,你可以用我到处使用的功能lift.您很可能会遇到许多其他monad,MonadIO因为它是在大型应用程序中构造代码的便捷方式.

在你的特殊情况下,我会坚持不懈的require方式,因为我认为这是设计师的happstack意思.我不是特别了解happstack,所以我可能错了.

 

而已.快乐的黑客!