Квазицитата с аргументами

Я хочу написать цитату на Haskell. Аргумент имени должен быть передан в функцию gen для создания объявления.

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

Однако, похоже, я не могу использовать такую ​​цитату

[quote "Hello"| from x to y |]

Поскольку Haskell не позволяет объявлениям кавычек и цитатам находиться в одном и том же файле, что раздражает, что я могу сделать, чтобы передать аргумент извне в цитату?


person Song Zhang    schedule 08.12.2014    source источник


Ответы (1)


У вас есть два варианта:

  1. Переключиться на использование сплайсов $(...),
  2. Закодируйте свой параметр в квазикавычки во входной строке.

С синтаксисом splice ваш пример будет выглядеть так:

quote :: String -> String -> Q [Dec]
quote name jsonStr = gen name (getValue jsonStr)

и вызов его выглядит так: $(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_"

Первый символ входной строки интерпретируется как используемый символ скобки. Фактически мы передали параметр 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
person ErikR    schedule 08.12.2014