“未定义的值,不允许引用”解决方法

时间:2016-05-02 13:56:58

标签: purescript

我正在寻找有关编译器错误消息The value of xyz is undefined here, so reference is not allowed.的一些说明以及标记。我没有设法足够概括这个例子,我能给出的只是具体的例子,我偶然发现了这种行为。对不起。

使用purescript-parsing,我想编写一个接受嵌套多行注释的解析器。为简化示例,每条评论都以(开头,以)结尾,可以包含a或其他评论。一些示例:(a)((a))已被接受,()(afoo被拒绝。

以下代码导致行The value of comment is undefined here, so reference is not allowed.上的错误content <- string "a" <|> comment

comment :: Parser String String
comment = do
  open <- string "("
  content <- commentContent
  close <- string ")"
  return $ open ++ content ++ close

commentContent :: Parser String String
commentContent = do
  content <- string "a" <|> comment
  return content

我可以通过在content <- string "a" <|> comment上方插入一行来消除错误,据我所知,它根本不会改变生成的解析器:

commentContent :: Parser String String
commentContent = do
  optional (fail "")
  content <- string "a" <|> comment
  return content

问题是:

  • 这里发生了什么?为什么额外的行有帮助?
  • 什么是非hacky方式来编译代码?

1 个答案:

答案 0 :(得分:4)

如果您手动去除do

,第二种情况的工作原因会变得更加明显
commentContent :: Parser String String
commentContent =
  optional (fail "") >>= \_ ->
    string "a" <|> comment >>= \content ->
      return content

以这种方式定义时,comment引用位于lambda内部,因此在commentContent的定义期间不会对其进行求值。

对于非hacky解决方案,它会涉及fix我想象的一些用法。 fix允许您定义递归解析器,如:

myParser = fix \p -> do
   ... parser definition ....

其中p是对myParser的引用,您可以在其中使用。至于你有相互递归解析器的情况,我不确定如何用fix最好地解决它,我可以想到几个选项,但没有一个特别优雅。也许是这样的:

parens :: Parser String String -> Parser String String
parens p = do
  open <- string "("
  content <- p
  close <- string ")"
  return $ open ++ content ++ close

comment :: Parser String String
comment = parens commentContent

commentContent :: Parser String String
commentContent = fix \p -> do
  content <- string "a" <|> parens p
  return content

使用类似于奇怪的do情况的技巧并在其中一个解析器前插入Unit ->可能更容易,因此您可以延迟递归引用直到{{1}提供了值,如:

Unit