"undefined value, reference not allowed" 解决方法

"undefined value, reference not allowed" workaround

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

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

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

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

问题是:

如果您手动对 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 最好地解决它,我可以想到几个选项,但 none 特别优雅。也许是这样的:

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 -> 可能会更容易,因此您可以延迟递归引用直到 Unit 提供了值,类似于:

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

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