如何在Haskell中编写模式的准Quoter



我使用准语录在编译时创建我的智能构造数据类型。这看起来像:

import qualified Data.Text as T
import Language.Haskell.TH.Quote (QuasiQuoter(..))
import Language.Haskell.TH (Q, Exp, Pat(..), Lit(..))
import Language.Haskell.TH.Syntax (Lift(..))
import qualified Language.Haskell.TH.Syntax as TH
import Instances.TH.Lift () -- th-lift-instances package
newtype NonEmptyText = NonEmptyText Text
textIsWhitespace :: Text -> Bool
textIsWhitespace = T.all (== ' ')
mkNonEmptyText :: Text -> Maybe NonEmptyText
mkNonEmptyText t = if textIsWhitespace t then Nothing else (Just (NonEmptyText t))
compileNonEmptyText :: QuasiQuoter
compileNonEmptyText = QuasiQuoter
  { quoteExp = compileNonEmptyText'
  , quotePat = error "NonEmptyText is not supported as a pattern"
  , quoteDec = error "NonEmptyText is not supported at top-level"
  , quoteType = error "NonEmptyText is not supported as a type"
  }
  where
    compileNonEmptyText' :: String -> Q Exp
    compileNonEmptyText' s = case mkNonEmptyText (pack s) of
      Nothing -> fail $ "Invalid NonEmptyText: " ++ s
      Just txt -> [| txt |]

(如果需要的话,我可以提供一个独立的工作示例 - 我只是将此示例从较大的代码库中删除)

本质上,通过仅为我的newtypes得出Lift,我可以将数据类型放在表达式quasi quoter [| txt |]中实现 quoteExp

,但是我在quotePat方面遇到了麻烦。如果我这样做,例如:

Just txt -> [p| txt |]

然后,我警告说,第一个TXT未使用,第二个TXT是第一个。我很确定该模式只是创建一个新名称txt,而不是像Quasi Quoter表达式一样在scope txt中拼接,因为当我这样做时:

f :: NonEmptyText -> Bool
f [compileNonEmptyText|test|] = True
f _ = False

一切都与第一个语句匹配。

好吧,我认为我已经知道了。从基本字符串s开始,我可以将其包装在StringLLitP中以获取字面字符串,因为TextIsString实例将成为Text。从那里,我需要使用ConP应用NonEmptyText构造函数:

compileNonEmptyTextPattern' :: String -> Q TH.Pat
compileNonEmptyTextPattern' s = case mkNonEmptyText (pack s) of
  Nothing -> fail $ "Invalid NonEmptyText: " ++ s
  Just (NonEmptyText txt) -> pure $ ConP 'NonEmptyText [(LitP (StringL (T.unpack txt)))]

不幸的是,这比表达式版本更详细!我想知道是否有Q Pat的类型类型,例如LiftQ Exp

相关内容

  • 没有找到相关文章

最新更新