请原谅我的无知但是,是否有办法从仅返回Handler ()
的方法中调用返回IO ()
的方法
例如,考虑这两种方法
onReceiveMessage :: IO ()
onReceiveMessage = do
_ <- putStrLn "Received Message"
_ <- doSomething
return ()
doSomething :: Handler ()
doSomething = return ()
这似乎没有编译。我尝试用几种不同的方式编译它,但都是静脉。我相信它一定是可能的,但只是不知道如何。
任何想法?
扩展前面的例子,假设我有另一个函数,它接受前一个函数的值并返回IO()。这也行不通。
onReceiveMessage :: IO ()
onReceiveMessage =
doSomething >>= doSomethingElse
doSomething :: Handler MessageStatus
doSomething = return MessageStatus.Success
doSomethingElse :: MessageStatus -> IO ()
doSomethingElse _ = return ()
这似乎也不起作用。要使用liftIO功能,可以从Handler调用IO动作。以下功能编译并正常工作。它从返回Handler MessageStatus的函数调用IO()动作。这是使用liftIO功能实现的。
doSomething' :: Handler MessageStatus
doSomething' = (liftIO $ putStrLn "hello world") >> return MessageStatus.Success
我们是否有类似于从IO调用Handler操作的东西?
提供更多背景信息并解释我是如何解决问题的。
我试图在Yesod应用程序中使用amqp包来侦听RabbitMQ。
(Message, Envelope) -> IO ()
。 runDB
来连接我的代码。Handler
中,因此无法从消息回调中调用它。我最终做的是
App
内构建Foundation
(Application.hs
)对象并将其传递给我的代码时,抓住它。总的来说,我觉得我可能有过复杂的事情,但目前我不知道有什么更好的办法。如果有人有更好的想法,我很乐意听到它。
答案 0 :(得分:1)
首先,你的缩进看了。 Haskell中的白色空间非常重要。但更重要的是,单一行动中的每一个陈述都必须在同一个单子中:
onReceiveMessage :: IO ()
onReceiveMessage = do
putStrLn "Received Message" :: IO ()
doSomething :: Handler () -- Illegal! Must be type IO ()
return () :: IO ()
doSomething :: Handler ()
doSomething = return ()
所以不,你不能从IO
动作返回处理程序。
更新
Handler
类型实际上是更复杂类型的别名,您可以将其视为“包装”IO monad。它允许你做的是将一个IO动作“提升”到Handler
monad中,但这只是一种方式。要将Handler
操作转换为IO
操作,库通常会提供类似runHandler :: Handler [-> other args] -> IO ()
的功能。我对Yesod并不是特别熟悉,但是在许多库中,模式类似。此函数用于将整个Handler
操作转换为IO
操作,通常保留用于运行服务器本身。
复杂的答案是Handler
类型是所谓的 monad转换器。起初他们学习起来相当棘手,但你可以把它想象成一个包含另一个monad的monad。 Monads无法在自己之外执行操作,因此IO
无法执行Handler
操作,但由于Handler
内包含IO
,因此可以执行IO
操作“提升”了一个级别。 monad变换器真正有用的是它们可以无限分层,它本质上可以让你将不同monad的行为组合在一起。例如,假设您有Maybe
操作,但您还需要State
功能,Writer
功能和IO
功能。使用monad变换器,如果有点复杂,这就成为可能。但是,这些组合的顺序通常很重要,因为操作可以解除,但不能降低。
答案 1 :(得分:0)
在最近的版本中,您可以使用Yesod.Core.Handler.handlerToIO
返回舒适的Handler堆栈。
但我建议将消息传递和处理分离:
getOneMsg :: Channel -> Text -> Handler (Message, Envelope)
getOneMsg chan queue = liftIO $ do
mbox <- newEmptyMVar
tag <- consumeMsgs chan queue NoAck (putMVar mbox)
reply <- takeMVar mbox
cancelConsumer chan tag
return reply
myHandler = do
... regular handler code ...
(msg, env) <- getOneMsg chan queue
... regular handler code ...