2

我正在学习编写自己的方案教程,一个代码块让我想知道绑定和赋值之间的区别:

 parseAtom = do first <- letter <|> symbol
            rest <- many (letter <|> digit <|> symbol)
            let atom = first:rest
            return $ case atom of 
                       "#t" -> Bool True
                       "#f" -> Bool False
                       _    -> Atom atom

为什么let atom =而不是atom <-?因此,我尝试了:

parseAtom = do first <- letter <|> symbol
           rest <- many (letter <|> digit <|> symbol)
           atom <- first : rest
           return $ case atom of
                "#t" -> Bool True
                "#f" -> Bool False
                _ -> Atom atom

我得到编译错误:

    Couldn't match expected type `[Char]'
       against inferred type `Text.Parsec.Prim.ParsecT
                                String () Data.Functor.Identity.Identity Char'
In a stmt of a 'do' expression: atom <- first : rest

我无法准确理解这意味着什么,这可能是由于对domonad 的不精确理解。(我已经阅读了 Learn You a Haskell以及其他各种 monad/do 教程,并且其他 SO 问题指出缩进通常会导致这里出现问题,但我认为我的缩进是正确的)

4

2 回答 2

5

您在解析器单子中,因此右侧<-需要是解析器表达式。然而first : rest,它只是一个列表(特别是一个字符串),而不是解析器表达式。

它的作用是v <- someParser将给定的解析器应用于输入,然后将匹配的文本存储在v. 字符串不是解析器,它不能应用于输入,并且不会有匹配的文本存储在v. 因此,您所能做的就是将字符串存储在 中v,您可以通过编写let v = someString.

于 2013-06-09T16:43:20.193 回答
5

您正在处理两种语法糖结构:do-notationdo -notation 中的let块。如果我们简单地对您正确的函数实现进行脱糖处理,事情就会变得清晰起来。

原来的

parseAtom = do 
  first <- letter <|> symbol
  rest <- many (letter <|> digit <|> symbol)
  let atom = first:rest
  return $ case atom of 
    "#t" -> Bool True
    "#f" -> Bool False
    _    -> Atom atom

让块脱糖

parseAtom = do 
  first <- letter <|> symbol
  rest <- many (letter <|> digit <|> symbol)
  let 
    atom = first : rest
    in do
      return $ case atom of 
        "#t" -> Bool True
        "#f" -> Bool False
        _    -> Atom atom

完全脱糖

parseAtom =
  (letter <|> symbol) >>= \first ->
  many (letter <|> digit <|> symbol) >>= \rest ->
  let 
    atom = first : rest
    in return $ case atom of 
      "#t" -> Bool True
      "#f" -> Bool False
      _    -> Atom atom

还应该注意的是,do-notation中的简单let-expression可以按照您的预期替换为bind-expression - 您需要做的只是放入. 所以可以翻译成。returnlet atom = first:restatom <- return $ first : rest

于 2013-06-09T17:38:54.480 回答