如何在 Haskell 中与 Data.Text 进行模式匹配?

时间:2021-02-11 19:34:50

标签: parsing haskell

我目前正在用 Haskell 编写解析器。我有以下代码。

{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}

module Main where

import Data.Text

newtype Parser a = Parser { runParser :: Text -> Either Text (Text, a) }

char1 :: Char -> Parser Char
char1 c = Parser $ \case
    (x:xs) | x == c -> Right (xs, x)
    _               -> Left "Unexpected character"

编译失败,出现这两个错误。

test.hs:12:6: error:
    • Couldn't match expected type ‘Text’ with actual type ‘[Char]’
    • In the pattern: x : xs
      In a case alternative: (x : xs) | x == c -> Right (xs, x)
      In the second argument of ‘($)’, namely
        ‘\case
           (x : xs) | x == c -> Right (xs, x)
           _ -> Left "Unexpected character"’
   |
12 |     (x:xs) | x == c -> Right (xs, x)
   |      ^^^^

test.hs:12:24: error:
    • Couldn't match type ‘[Char]’ with ‘Text’
      Expected type: Either Text (Text, Char)
        Actual type: Either Text ([Char], Char)
    • In the expression: Right (xs, x)
      In a case alternative: (x : xs) | x == c -> Right (xs, x)
      In the second argument of ‘($)’, namely
        ‘\case
           (x : xs) | x == c -> Right (xs, x)
           _ -> Left "Unexpected character"’
   |
12 |     (x:xs) | x == c -> Right (xs, x)
   |                        ^^^^^^^^^^^^^

我可以通过将 Text 数据类型替换为 String 来修复错误,但我更愿意使用 Text 数据类型。

有没有一种方法可以与 Data.Text 类型进行模式匹配,而无需先将其显式转换为字符串?也许有一个 GHC 扩展可以让我这样做?

提前致谢。

2 个答案:

答案 0 :(得分:6)

对@DanielWagner 的回答进行了改进,您可以结合视图模式和模式同义词来执行此操作。您需要一个新的构造函数来代替 :,但它可能看起来像:

{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE ViewPatterns #-}

import Data.Text

pattern x :> xs <- (uncons -> Just (x, xs))
pattern Empty <- (uncons -> Nothing)

findInText :: (Char -> Bool) -> Text -> Maybe Char
findInText _ Empty = Nothing
findInText p (x :> xs) | p x = Just x
                       | otherwise = findInText p xs

这里的想法是模式 x :> xs 是模式 uncons -> Just (x, xs) 的同义词,后者是一种视图模式,通过将 uncons 应用于被审查者并将结果与Just (x, xs) 为父模式填充 xxs

根据评论,可能有人担心这种用法是否会多次调用 uncons。优化完全关闭 (-O0) 后,生成的核心确实有多个 uncons 调用:

-- unoptimized -O0
findInText
  = \ ds ds1 ->
      case uncons ds1 of {
        Nothing -> Nothing;
        Just ipv ->
          case uncons ds1 of {
            Nothing -> ...

优化(-O-O2)后,所有内容都被内联,并且由于 Unicode 处理正在进行,生成的核心非常复杂。但是,如果您还定义了:

findInText' :: (Char -> Bool) -> Text -> Maybe Char
findInText' p txt = case uncons txt of
  Nothing -> Nothing
  Just (x, xs) | p x -> Just x
               | otherwise -> findInText' p xs

事实证明 GHC 将 findInText' 编译为:

findInText' = findInText

所以至少在这种情况下,GHC 不会因为视图模式而做任何额外的工作。

答案 1 :(得分:4)

您可以匹配对 uncons 的呼叫。

case uncons text of
    Just (x, xs) -> ...
    Nothing -> ...

查看模式可让您在模式内而不是在被审查者内执行此操作,但要求您对每个模式说一次 uncons

case text of
    (uncons -> Just (x, xs)) -> ...
    (uncons -> Nothing) -> ...
相关问题