在 where 语句中使用声明引号
Using a declaration quoter in a where statement
我正在实施基于使用标准 haskell functions/combinators 构建数据库查询的 DSL。从一个实现 POV 我决定像这样在查询中表示变量:
newtype Variable = Var { fromVar :: Text }
然而,这迫使用户经常写 Var "something"
,所以我决定
编写一个自动执行此操作的准引号。
这是 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
很遗憾,您只能在顶级声明中使用声明类引号。来自 the documentation:
A quasiquote may appear in place of
- An expression
- A pattern
- A type
- A top-level declaration
您可以考虑使用 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"]
我正在实施基于使用标准 haskell functions/combinators 构建数据库查询的 DSL。从一个实现 POV 我决定像这样在查询中表示变量:
newtype Variable = Var { fromVar :: Text }
然而,这迫使用户经常写 Var "something"
,所以我决定
编写一个自动执行此操作的准引号。
这是 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
很遗憾,您只能在顶级声明中使用声明类引号。来自 the documentation:
A quasiquote may appear in place of
- An expression
- A pattern
- A type
- A top-level declaration
您可以考虑使用 OverloadedStrings
:
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"]