【问题标题】:Using a declaration quoter in a where statement在 where 语句中使用声明引用器
【发布时间】:2021-08-26 07:31:33
【问题描述】:

我正在实现一个基于使用标准 haskell 函数/组合器来构建数据库查询的 DSL。从实现 POV 中,我决定像这样在查询中表示变量:

newtype Variable = Var { fromVar :: Text }

然而,这迫使用户经常写Var "something",所以我决定 编写一个自动执行此操作的 quasiquoter。

这是 DSL 的一个示例:

{-# LANGUAGE OverloadedStrings #-}
maxQuery :: Query MAX
maxQuery = match
         ( sch `isa` "school"
         $ forWhich "ranking" `labelMatches` ran $ε)
         `get` [ran]
         `max` [ran]
    where 
        [sch,ran] = map Var ["sch","ran"] 

我希望它是什么:

maxQuery :: Query MAX
maxQuery = match
         ( sch `isa` "school"
         $ forWhich "ranking" `labelMatches` ran $ε)
         `get` [ran]
         `max` [ran]
    where [defVars| sch ran |] 

或类似的东西。

我写的准引用者在这里:

{-# LANGUAGE TemplateHaskell #-}
module TypeDBTH where 
import Language.Haskell.TH.Syntax
import Language.Haskell.TH.Quote
import Data.List.Split
import Data.Text (pack)

mkVars :: [String] -> Dec
mkVars vars = ValD 
                (ListP (map (VarP . mkName) vars))
                (NormalB (ListE (map (\v -> AppE (ConE $ mkName "Var")
                                          $ AppE (VarE $ mkName "pack")
                                                 (LitE $ StringL v))
                                     vars)))
                []

defVars :: QuasiQuoter
defVars = QuasiQuoter { quoteDec = quoteVars }
                 --, quoteExp = expQuoteVars }

quoteVars :: String -> Q [Dec]
quoteVars = return . return . mkVars . filter (/= "") . splitOn " "

expQuoteVars :: String -> Q Exp
expQuoteVars s = return $ LetE [(mkVars . filter (/= "") . splitOn " " $ s)] (LitE $ StringL "x")

最初我只写了quoteVars。为了在 ghci 中进行测试,我添加了 expQuoteVars。 但是,现在删除后一个并尝试编写

...
    where [defVars| sch ran |] 

给我留下两个错误:

lib/TypeDBQuery.hs:806:1: error:
    parse error (possibly incorrect indentation or mismatched brackets)

因为where [quasiquoter] 后面什么都没有

lib/TypeDBQuery.hs:807:5: error:
    • Exception when trying to run compile-time code:
        lib/TypeDBTH.hs:18:11-46: Missing field in record construction quoteExp

      Code: Language.Haskell.TH.Quote.quoteExp defVars " sch ran "
    • In the quasi-quotation: [defVars| sch ran |]
    |
807 | x = [defVars| sch ran |] 
    |     ^^^^^^^^^^^^^^^^^^^^

我如何将准引用器用于quoteDec 而不是quoteExp? 这可能吗?

如果这样更容易的话,我也愿意像这样使用它:

maxQuery :: Query MAX
maxQuery = let [defVars | sch ran |] in
                $ match
                ( sch `isa` "school"
                $ forWhich "ranking" `labelMatches` ran $ε)
                `get` [ran]
                `max` [ran]

我查看了 wiki.haskell.org 和 TH 模块的“教程”和信息站点,但不知道如何执行此操作... https://wiki.haskell.org/Template_Haskell#What_to_do_when_you_can.27t_splice_that_there https://wiki.haskell.org/Quasiquotation https://wiki.haskell.org/A_practical_Template_Haskell_Tutorial

【问题讨论】:

    标签: haskell template-haskell quasiquotes


    【解决方案1】:

    不幸的是,您只能在顶级声明中使用声明准引号。来自the documentation

    一个准引号可以代替

    • 表达式
    • 一种模式
    • A 型
    • 顶级声明

    您可以考虑使用OverloadedStrings,而不是使用 TH:

    instance IsString Variable where
      fromString str = Var (pack str)
    
    maxQuery :: Query MAX
    maxQuery = match
             ( "sch" `isa` "school"
             $ forWhich "ranking" `labelMatches` "ran" $ε)
             `get` ["ran"]
             `max` ["ran"]
    

    【讨论】:

    • 这很可悲;所以在这种情况下,唯一的方法是为所有变量构造顶级声明......
    • 我考虑过这种方法,但我将重载字符串用于其他事情,不幸的是,这似乎与 Var 发生冲突
    • 这不能用在let 模式和表达式中吗?喜欢let [v|vars|] = [x|vars] in rest_of_function
    • 是的,你可以拆分模式和表达式,但是你必须写两次变量。
    • 也许仅仅使用普通的 Haskell 并没有太大的不同:[sch, ran] = vars ["sch", "ran"] 使用一些合适的 vars 函数。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多