[英]How to use “IO String” as an HTTP response in Happstack?
I'm retrieving data from a database using HDBC, then trying to send this data to a web client using Happstack. 我正在使用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
When I build the above code I get this error: 构建以上代码时,出现此错误:
No instance for (ToMessage (IO String)) arising from a use of `toResponse' 没有因使用`toResponse'而产生(ToMessage(IO String))的实例
What did I try ? 我尝试了什么?
IO String
to String
(using liftIO
for example). 我试图将IO String
转换为String
(例如,使用liftIO
)。 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. 您必须根据以下事实来设计handlers
:从数据库中获取数据是一种神奇的动作 ,可能无法满足您的期望。 (For example, your database may crash.) This is why its result is served as an IO
, which is a particular case of a monad . (例如,您的数据库可能崩溃。)这就是为什么其结果用作IO
,这是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
. (除非它碰巧也是一个comonad
,但这又是另外一回事了, IO
和ServerPart
并非如此。)因此,您永远不会将IO String
转换为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
. 您的案例有些棘手,因为那里有两个monad: IO
和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
. 幸运的是, ServerPart
建立在IO
之上,从某种意义上讲,它是“ 更大的 ”并且可以吸收 IO
:我们可以将一些IO
放入ServerPart
而它将仍然是ServerPart
,因此可以将其提供给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
. 在happstack
,可以通过require
函数完成此转换,但是还有一个更通用的解决方案,涉及monad转换器和lift
。
Let's take a look at the solution with require
first. 让我们看一下先使用require
的解决方案 。 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. —因此,它需要一个带有某些参数的IO
jar,并使它适合于ServerPart
在ServerPart
jar中的函数。 We just have to adjust types a bit and create one lambda abstraction : 我们只需要稍微调整一下类型并创建一个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
As you see, we have to make 2 modifications: 如您所见,我们必须进行2处修改:
Adjust myFunc
so that it returns Maybe
, as necessitated by require
. 调整myFunc
,以便它返回Maybe
,作为必要的require
。 This is a better design because myFunc
may now fail in two ways: 这是一个更好的设计,因为myFunc
现在可能会以两种方式失败:
Maybe
, it may return Nothing
, which means 404
or the like. 作为Maybe
,它可能会返回Nothing
,这意味着404
之类。 This is rather common a situation. 这是相当普遍的情况。 IO
, it may error out, which means the database crashed. 作为IO
,它可能会出错,这意味着数据库崩溃了。 Now is the time to alert the DevOps team. 现在是时候提醒DevOps团队了。 Adjust handlers
so that myFunc
is external to them. 调整handlers
使myFunc
在它们外部。 One may say more specifically: abstract myFunc
from handlers
. 可能更具体地说: 从handlers
提取myFunc
。 This is why this syntax is called a lambda abstraction . 这就是为什么此语法称为lambda 抽象的原因 。
require
is the way to deal with monads in happstack
specifically. require
是专门处理happstack
monad的happstack
。 Generally though, this is just a case of transforming monads into larger ones, which is done via lift
. 通常,这只是将 monad转换为更大的情况,这是通过lift
。 The type of lift
(again, simplified), is: lift
的类型(再次简化)是:
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: 所以,我们就可以lift
了myFunc 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
As simple as that. 就如此容易。 I used the same lambda abstraction trick again, but you may as well use do-notation : 我再次使用了相同的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
PS 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 . PS回到大罐子和小罐子的故事:您可以将IO
放入ServerPart
正是因为ServerPart
也是IO
monad —它是MonadIO
类的实例。 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
. 这意味着,任何你能在做IO
,你也可以做ServerPart
,并且,除了一般的lift
,有一个专门的liftIO
,你可以用我到处使用的功能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. 您可能会遇到许多其他MonadIO
,它们是MonadIO
实例,因为它是在大型应用程序中结构化代码的便捷方法。
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. 在您的特定情况下,我还是会坚持使用require
方法,因为我认为这是happstack
的设计者认为happstack
这一点的方式。 I'm not particularly knowledgeable about happstack
though, so I may be wrong. 我对happstack
知识不是特别了解,所以我可能是错的。
That's it. 而已。 Happy hacking! 骇客入侵!
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.