-1

我正在尝试使用绑定转换IO [String]为;但是,我需要在语句下使用块来执行此操作,但 Haskell 一直抱怨缩进。这是代码:[String]<-dowhere

decompEventBlocks :: IO [String] -> IO [[String]]
decompEventBlocks words
 | words' /= [] = block : (decompEventBlocks . drop $ (length block) words')
 | otherwise = []
  where 
   do
    words' <- words
    let block = (takeWhile (/="END") words')

这是什么原因?我们如何在语句中使用do块?where而且,有没有机会在守卫面前发表一些声明?

4

4 回答 4

5

您不能IO 字符串转换为字符串。

但是,您可以将 IO String 的内容绑定到“变量”,但这仍会导致整个计算嵌入到 IO 中。

foo = do
   x <- baz -- here baz is the IO String
   let x' = doStuff x
   return x' -- embeds the String inside IO, as otherwise the computation would result in IO ()

回答你的问题

foo x = baz x -- x here is your 'IO String'
  where
    baz x = do
      x' <- x
      return $ doStuff x'
于 2018-06-11T18:58:13.107 回答
4

请记住:do-blocks 是一元符号的语法糖。这意味着以下适用:

do {a; b} = a >> b
dp {a <- b; c} = b >>= \a -> c

换句话说,当使用do-notation 时,您实际上是在产生值。这就是为什么您不能只在语句do的顶层使用 -block 的原因。where

解决这个问题的方法是将函数放入do-block 中:

decompEventBlocks :: IO [String] -> IO [[String]]
decompEventBlocks words = do
    -- We unwrap the IO [String], but we keep it in the do-block,
    -- because it must be kept in a monadic context!
    words' <- words 
    let block = (takeWhile (/="END") words')
    -- This is equivalent to the guards you had in your function.
    -- NB return :: Monad m => a -> m a, to keep it in a monadic context!
    if not $ null words'
        then do 
          -- Since the recursion is monadic, we must bind it too:
          rest <- decompEventBlocks $ return $ drop (length block) words'
          return $ block : rest
        else return []

要了解 monads、do-notation、>>=>>,我强烈建议阅读LYAH 章节以获得更好的理解,然后再尝试更多的 monadic 代码。

于 2018-06-11T19:02:45.243 回答
0

作为 AJFarmar 回答的一个稍微不同的角度:你可以在 a 中拥有的唯一东西where是声明。do块不是声明,它们是表达式。即,这与您尝试编写where 2+5. 如果要block在 a中声明where,则必须

where
  // can have other declarations, even mutually recursive
  block = ...
于 2018-06-11T23:17:12.957 回答
0

Do表示法用于编写一般形式的表达式

ex :: Monad m => m t
let ex = do 
          {  x <- foo         -- foo        :: Monad m => m a,   x :: a
          ;  y <- bar x       -- bar  x     :: Monad m => m b,   y :: b
          ;  z <- baz x y     -- baz  x y   :: Monad m => m c,   z :: c
          ;  quux x y z       -- quux x y z :: Monad m => m t
          }

请注意,所有的ms 都是相同的,并且a, b, c, ... 可以不同,尽管t最后一个do子表达式的类型和整个do表达式的类型是相同的。

do表示法变量被认为是由<-构造“绑定”的。它们在引入时进入范围(在 的左侧<-),并保留在所有后续do子表达式的范围内。

一个可用于任何 monad 的内置 monadic 表达式是return :: Monad m => a -> m a. 因此x <- return v 绑定 xv,以便x在后续子表达式中可用,并且将具有 的值v

所有do变量都被限制在该do块中,不能在它之外使用。每个变量的作用域是同do一块中的所有代码,位于变量绑定的下方/之后。

这也意味着<-'s 是一个非递归绑定,因为变量不能在其右侧和左侧一样:在这种情况下,它将是两个具有相同名称的不同变量,并且变量位于必须在高于该点的某个地方确立权利。

这里有一些通用模式:

do { _ <- p ; _ <- q ; r }    ===   do { p ; q ; r }
do { x <- p ; return x }      ===   do { p }          ===   p
do { x <- return v ; foo x }  ===   do { foo v }      ===   foo v
do { p ; q ; r }              ===   do { p ; do { q ; r } }
                              ===   do { do { p ; q } ; r }
do { x <- p ;                 ===   do { x <- p ;
     y <- q x ;                          z <- do { y <- q x ;
     return (foo x y) }                            return (foo x y) } ;
                                         return z }

所有的Monad m => m a表达式都是这样,表达式,因此特别是可以是一个if - then - else表达式,其结果分支和替代分支都是相同的单子类型(这通常会让初学者感到困惑):

    do { x <- p ;
         y <- if (pred x) then (foo x) else (bar x) ;
         return (baz x y) }

更新: monad 的要点之一是将效果与纯计算完全分离。一旦进入单子,你就不能“出去”。一元计算可以使用纯计算,反之则不行。

于 2018-06-12T10:37:28.783 回答