4

这是几个问题合而为一:

  1. do符号表示,每一行都必须返回相同的类型吗?例如,我可以在一个do块中写一行返回一个IO单子,另一行返回一个整数吗?(我的理解是,基于脱糖>>>>=似乎如何工作,答案是否定的。)

  2. 如果不是,那么编译器如何确定这些行必须全部返回什么类型?在我看到的所有示例中,作者都​​认为我们只是在使用IOmonads。但是你怎么知道,对于给定的do块,每行必须返回什么?

  3. 再次假设#1 的答案是否定的:你如何使用不会在do块内返回正确类型的 monad 的函数?例如,考虑这个 websockets 代码:

    application :: MVar ServerState -> WS.Request -> WS.WebSockets WS.Hybi00 ()
    application state rq = do
      WS.acceptRequest rq
      msg <- WS.receiveData :: WS.WebSockets WS.Hybi00 Text
      return ()
    

    假设我想打印msg. 我将如何以不与do块类型冲突的方式进行处理?

4

2 回答 2

9
  1. 在 do-block 中,每一行可以返回不同的类型,但它们必须在同一个 monad 中
    • 一行可以返回一个IO String,一个可以返回一个,IO Integer但它们都必须是IO
  2. 与 Haskell 的其余部分相同。类型推断。就像在 Haskell 的其他部分中一样,它并不总是有效,当它不起作用时,您也必须进行注释。
  3. 有两种方法可以做到这一点
    • let,记住在 GHCi 中你必须使用 let 来声明局部变量,你可以在一个do块中做同样的事情。let someMonad = doSomething
      • 注意,没有in
    • 单子变形金刚!这是一个很大的话题,需要在简介中解释,但基本上它们是具有特殊功能的单子,lift可以将另一个单子“提升”到变压器中。变压器通常以 T 结尾,例如 StateT。几乎你使用的每个 monad 都有一个等效的转换器。
于 2013-05-15T04:40:35.373 回答
4

要回答最后一个问题的最后一部分,

假设我想打印msg. 我将如何以不与do块类型冲突的方式进行处理?

正如jozefg 在他的回答中所说,monad 转换器通常是你需要的。但是,在这种情况下,WebSockets pmonad 不是转换器。但是,它是一个实例,MonadIO它是 monad 堆栈的一个类,它具有IO“位于底部”,因此可以让您IO在其中运行任意操作。

该类MonadIO提供liftIO具有类型的函数

liftIO :: MonadIO m => IO a -> m a

在您的情况下,这变为IO a -> WebSockets Hybi00 a,因此您可以使用它将print msg操作从转换IO ()WebSockets Hybi00 (),然后您可以在do块中使用它:

application :: MVar ServerState -> WS.Request -> WS.WebSockets WS.Hybi00 ()
application state rq = do
  WS.acceptRequest rq
  msg <- WS.receiveData :: WS.WebSockets WS.Hybi00 Text
  liftIO $ print msg
于 2013-05-15T11:20:51.430 回答