带参数的QuasiQuote

Son*_*ang 8 haskell template-haskell

我想在Haskell中写一个引文.name参数需要传递给gen函数以生成声明.

quote ::  String -> QuasiQuoter
quote name = QuasiQuoter {
       quoteExp = undefined,
       quotePat = undefined,
       quoteType = undefined,
       quoteDec = \jsonStr -> gen name (getValue str)
     }
Run Code Online (Sandbox Code Playgroud)

但是,似乎我不能像这样使用报价

[quote "Hello"| from x to y |]
Run Code Online (Sandbox Code Playgroud)

由于Haskell不允许引用声明和引用在同一个令人烦恼的文件中,我该怎样做才能将参数从外部传递到引用中?

Eri*_*ikR 6

您有两种选择:

  1. 切换到使用拼接$(...),
  2. 将参数编码到输入字符串中的准引号.

使用拼接语法,您的示例如下所示:

quote :: String -> String -> Q [Dec]
quote name jsonStr = gen name (getValue jsonStr)
Run Code Online (Sandbox Code Playgroud)

并调用它看起来像: $(quote "Hello" "from x to y")

为了演示选项2,这里有一个简单的引号,它包含一个带字符的文字字符串:

import Language.Haskell.TH (litE, stringL)
import Language.Haskell.TH.Quote

surround :: QuasiQuoter
surround = QuasiQuoter
  { quoteExp  = litE . stringL . (\(c:s) -> [c] ++ s ++ [c])
  , quotePat  = undefined
  , quoteType = undefined
  , quoteDec  = undefined
  }

-- in another file:
main = print [surround|_some text|] -- prints "_some text_"
Run Code Online (Sandbox Code Playgroud)

输入字符串的第一个字符被解释为要使用的括号字符.实际上我们已经将Char参数传递给了类型的函数Char -> QuasiQuoter.

对于更复杂的参数或多个参数,您必须创建自己的语法和解析器来解码它们.

更新:这是一个稍微复杂的示例,其中调用[foo| var xyz|]将其var视为变量的名称和xyz文字字符串:

-- [foo| var xyz|]   is translated to:   var ++ "xyz"

foo :: QuasiQuoter
foo = QuasiQuoter
  { quoteExp  = go
  , quotePat  = undefined
  , quoteType = undefined
  , quoteDec  = undefined
  }
  where go str = infixE (Just $ varE (mkName var))
                        (varE $ mkName "++")
                        (Just $ litE (stringL arg1))
          where (var:arg1:_) = words str
Run Code Online (Sandbox Code Playgroud)