如何使用“IO字符串”作为Happstack中的HTTP响应? [英] How to use "IO String" as an HTTP response in Happstack?
问题描述
我使用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))实例
预先致谢。 monad是一个瓶颈非常狭窄的瓶子,甚至很窄,一旦你把东西放在那里,你就不能把它取出来。 (除非碰巧也是一个 你的情况有点棘手,因为你有两个单子在那玩:
<我尝试将 IO字符串转换为 / code>到
也不是字符串
(例如使用 liftIO
)。
你必须设计你的处理程序
,这个事实是从数据库中获取是一个手术行动,可能不会给你所期望的。 (例如,数据库可能会崩溃。)这就是为什么它的结果作为 IO
的原因,这是 monad 的特例。
comonad
,但这是另一回事,而 IO字符串
转换为字符串
。不是你不能,而是你的程序会变得不正确。
IO
和 ServerPart
。幸运的是, ServerPart
建立在 IO
上,它是 greater ,并且可以感觉吸收 IO
:我们可以将一些 IO
放入 ServerPart
,它仍然是一个 ServerPart
,所以我们可以将它赋给 simpleHTTP
>。在 happstack
中,这个转换可以通过 require
函数,但还有一个更通用的解决方案, em> monad变形金刚和 lift
。
让我们先看看解决方案,其中 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团队了。
- a
-
调整
处理程序
因此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 ?
- I tried to convert the
IO String
toString
(usingliftIO
for example). - I tried to find any similar questions here.
- I tried to find a similar example in the Happstack Crash Course.
- 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 returnsMaybe
, as necessitated byrequire
. This is a better design becausemyFunc
may now fail in two ways:- As a
Maybe
, it may returnNothing
, which means404
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.
- As a
Adjust
handlers
so thatmyFunc
is external to them. One may say more specifically: abstractmyFunc
fromhandlers
. 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屋!