如何使用“IO字符串”作为Happstack中的HTTP响应? [英] How to use "IO String" as an HTTP response in Happstack?

查看:141
本文介绍了如何使用“IO字符串”作为Happstack中的HTTP响应?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用HDBC从数据库检索数据,然后尝试使用Happstack将这些数据发送给Web客户端。

  myFunc :: Integer  - > IO字符串
myFunc = ...从数据库中获取此处...

handlers :: ServerPart响应
处理程序=
decodeBody(defaultBodyPolicy/ tmp / 0 1000 1000)
msum [
dirgetData$ ok $ toResponse $ myFunc $ toInteger 1
]

mainFunc = simpleHTTP nullConf处理程序

当我构建上面的代码时,我得到这个错误:


没有使用
`toResponse'引发的(ToMessage(IO String))实例




<我尝试将 IO字符串转换为 / code>到字符串(例如使用 liftIO )。

  • 我试图在这里找到任何类似的问题。

  • 我试图在Happstack Crash Course中找到一个类似的例子。 $ b
  • 我搜索了所有不同组合的所有相关关键字。

  • 预先致谢。


    你必须设计你的处理程序,这个事实是从数据库中获取是一个手术行动,可能不会给你所期望的。 (例如,数据库可能会崩溃。)这就是为什么它的结果作为 IO 的原因,这是 monad 的特例。


    monad是一个瓶颈非常狭窄的瓶子,甚至很窄,一旦你把东西放在那里,你就不能把它取出来。 (除非碰巧也是一个 comonad ,但这是另一回事,而 IO 也不是 ServerPart 。)所以,你永远不会将 IO字符串转换为字符串。不是你不能,而是你的程序会变得不正确。

    你的情况有点棘手,因为你有两个单子在那玩: IO ServerPart 。幸运的是, ServerPart 建立在 IO 上,它是 greater ,并且可以感觉吸收 IO :我们可以将一些 IO 放入 ServerPart ,它仍然是一个 ServerPart ,所以我们可以将它赋给 simpleHTTP >。在 happstack 中,这个转换可以通过 require 函数,但还有一个更通用的解决方案, em> monad变形金刚 lift

    b $ b

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

      IO(也许a) - > (a  - > ServerPart r) - > ServerPart r 

    - 因此,它需要 IO 带有一些参数的jar,并使它适用于位于 ServerPart jar中的函数。我们只需要调整一下类型并创建一个lambda抽象:

      myFunc :: Integer  - > IO(也许字符串)
    myFunc _ =返回。只要$美丽的东西永远是一种快乐。

    handlers :: ServerPart Response
    handlers = require(myFunc 1)$ \x - >
    do decodeBody(defaultBodyPolicy/ tmp /0 1000 1000)
    msum [
    dirgetData$ ok $ toResponse x
    ]

    mainFunc = simpleHTTP nullConf处理程序

    正如你所看到的,我们必须做2次修改:


    • 调整 myFunc ,以便它返回可能,如 require 所要求的。这是一个更好的设计,因为 myFunc 现在可能以两种方式失败:


      • a Maybe ,它可能会返回 Nothing ,这意味着 404 或类似的。这是很常见的情况。

      • 作为 IO ,可能会出错,这意味着数据库崩溃了。现在是时候提醒DevOps团队了。


    • 调整处理程序因此 myFunc 是它们的外部。可以更具体地说:从处理程序 中抽象 myFunc 。这就是为什么这种语法被称为lambda抽象的原因。

    • >

      require 是专门处理 happstack 中的monads的方法。一般来说,这仅仅是 将monads 转换为大的情况,这通过 lift 即可。 lift (再次简化)的类型是:

        IO字符串 - > ServerPart字符串

      所以,我们只需 lift 像往常一样将 myFunc 1 :: IO String 值添加到正确的monad中,然后与>> = 组合:

        myFunc :: Integer  - > IO字符串
      myFunc _ =返回$它的可爱性增加,..

      handlers :: ServerPart Response
      handlers = lift(myFunc 1)>> = \ x - >
      do decodeBody(defaultBodyPolicy/ tmp /0 1000 1000)
      msum [
      dirgetData$ ok $ toResponse x
      ]

      mainFunc = simpleHTTP nullConf处理程序

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

        myFunc :: Integer  - > IO字符串
      myFunc _ =返回$...它永远不会变成虚无。

      handlers :: ServerPart Response
      handlers = do
      x< - lift(myFunc 1)
      decodeBody(defaultBodyPolicy/ tmp /0 1000 1000)
      msum [
      dirgetData$ ok $ toResponse x
      ]

      mainFunc = simpleHTTP nullConf处理程序

       



      PS 回到大罐和小罐的故事:可以正确地将 IO 放入 ServerPart 中,因为 ServerPart 也是 an IO monad - 它是 MonadIO class 。这意味着您可以在 IO 中执行任何操作,您还可以在 ServerPart 中执行任何操作,除了一般 lift ,有一个专门的 liftIO 函数,您可以在任何地方使用 lift 。您可能会遇到许多其他monad,它们是 MonadIO 的实例,因为它是在大型应用程序中构建代码的一种便捷方式。



      在你的特定情况下,我坚持使用 require 方式,因为我认为这是 happstack 意味着要完成。虽然我对 happstack 没有特别的了解,所以我可能会错。



       



      就是这样。快乐黑客!

      I'm retrieving data from a database using HDBC, then trying to send this data to a web client using Happstack.

      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
      

      When I build the above code I get this error:

      No instance for (ToMessage (IO String)) arising from a use of `toResponse'

      What did I try ?

      1. I tried to convert the IO String to String (using liftIO for example).
      2. I tried to find any similar questions here.
      3. I tried to find a similar example in the Happstack Crash Course.
      4. I googled all related keywords in all different combinations.

      Thanks in advance.

      解决方案

      You have to design your handlers around the fact that fetching from a database is a magical action that may not give you what you expect. (For example, your database may crash.) This is why its result is served as an IO, which is a particular case of a monad.

      A monad is a jar with a very narrow neck, so narrow even that, once you put something in there, you cannot unput it. (Unless it happens to also be a comonad, but that's a whole another story and not the case with IO nor with ServerPart.) So, you would never convert an IO String to a String. Not that you can't, but your program would become incorrect.

      Your case is kind of tricky as you have two monads at play there: IO and ServerPart. Fortunately, ServerPart builds upon IO, it is " larger " and can, in a sense, absorb IO: we can put some IO into a ServerPart and it will be a ServerPart still, so we may then give it to simpleHTTP. In happstack, this conversion may be done via require function, but there is a more general solution as well, involving monad transformers and lift.

       

      Let's take a look at the solution with require first. Its type (simplified to our case) is:

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

      — So, it takes an IO jar with some argument and makes it suitable for a function that lives in the ServerPart jar. We just have to adjust types a bit and create one lambda abstraction:

      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
      

      As you see, we have to make 2 modifications:

      • Adjust myFunc so that it returns Maybe, as necessitated by require. This is a better design because myFunc may now fail in two ways:

        • As a Maybe, it may return Nothing, which means 404 or the like. This is rather common a situation.
        • As an IO, it may error out, which means the database crashed. Now is the time to alert the DevOps team.
      • Adjust handlers so that myFunc is external to them. One may say more specifically: abstract myFunc from handlers. This is why this syntax is called a lambda abstraction.

       

      require is the way to deal with monads in happstack specifically. Generally though, this is just a case of transforming monads into larger ones, which is done via lift. The type of lift (again, simplified), is:

      IO String -> ServerPart String
      

      So, we can just lift the myFunc 1 :: IO String value to the right monad and then compose with >>=, as usual:

      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
      

      As simple as that. I used the same lambda abstraction trick again, but you may as well use 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
      

       

      P.S. Returning to the story of large and small jars: you can put IO into ServerPart precisely because ServerPart is also an IO monad — it is an instance of the MonadIO class. That means that anything you can do in IO you can also do in ServerPart, and, besides general lift, there is a specialized liftIO function that you can use everywhere I used lift. You are likely to meet many other monads out there that are instances of MonadIO as it's a handy way of structuring code in large applications.

      In your particular case, I would stick with the require way nevertheless because I think it's how the designers of happstack meant it to be done. I'm not particularly knowledgeable about happstack though, so I may be wrong.

       

      That's it. Happy hacking!

      这篇关于如何使用“IO字符串”作为Happstack中的HTTP响应?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆