当数据构造函数不在范围内时,如何对抽象数据类型进行模式匹配?

时间:2019-05-05 05:08:38

标签: haskell parsec

我正在使用Parsec组合器编写一个解析器库,并且我想对一些解析器进行单元测试。所以我有一个简单的解析器:

dash :: GenParser Char st Char
dash = char '-'

我想为此编写一些测试。积极的测试非常容易:

spec :: Spec
spec = do
  describe "dash" $ do
    it "parses a dash" $
      parse dash "N/A" "-" `shouldBe` (Right '-')

我也想写一个否定的测试。解析器不匹配时,它将返回Left中的ParseError。我想编写一个测试来验证ParseError包含的确切消息。所以我真正想做的是像

spec :: Spec
spec = do
  describe "dash" $ do
    it "doesn't parse an underscore" $
      parse dash "N/A" "_" `shouldSatisfy` (hasErrorMessage "not a dash")

hasErrorMessage (Left (ParseError _ msgs)) expected = msg == expected
hasErrorMessage _ expected = False

但是我无法编写这种代码,因为ParseError的数据构造函数不是从Text.Parsec.Error导出的。

在没有适用于该类型的数据构造函数的类型上,是否可以使用模式匹配?

我知道我可以写hasErrorMessage之类的

hasErrorMessage :: String -> (Either ParseError a) -> Bool
hasErrorMessage expected (Left pe) = elem expected $ fmap messageString (errorMessages pe)

但是我也想了解这个细微差别。

1 个答案:

答案 0 :(得分:3)

尽管未导出数据构造函数,但用于访问其参数的函数却已导出。您可以将它们与view patterns结合使用以获取所需的内容。对于您而言,模式(errorMessages -> msgs)可以完美地代表(ParseError _ msgs),但有两个警告:

  1. 您需要{-# LANGUAGE ViewPatterns #-}才能使用此功能。
  2. errorMessages对消息进行排序,而数据构造函数上的模式匹配则不会。

您甚至可以将此技术与pattern synonyms结合使用,以创建伪造的数据构造函数,因此可以使用与其他语法完全相同的语法:

{-# LANGUAGE PatternSynonyms, ViewPatterns #-}
pattern ParseError pos msgs <- ((,) <$> errorPos <*> errorMessages -> (pos, msgs)) where
        ParseError pos msgs = foldr addErrorMessage (newErrorUnknown pos) msgs