Оценка функции во время компиляции с шаблоном Haskell - PullRequest
9 голосов
/ 12 февраля 2012

Я пишу простой класс HashString, который представляет собой просто строку и ее хэш:

data HashString = HashString Int    -- ^ hash
                             T.Text -- ^ string!

Теперь я пытаюсь сгенерировать их во время компиляции с чем-то вроде:

$(hString "hello, world") :: HashString

Я хочу, чтобы хеш и упаковка текста происходили во время компиляции.Как мне это сделать?

Вот что я пробовал до сих пор, но я не уверен, что это правильно, и я не уверен, что он делает все во время компиляции:

hString :: String -> Q Exp
hString s = [| HashString (hash $ T.pack s) (T.pack s) |]

1 Ответ

14 голосов
/ 12 февраля 2012

То, как вы написали свой код, во время компиляции оценки не произойдет. Когда вы цитируете выражение Haskell с помощью [| ... |], цитируемый код / ​​AST вставляется туда, где вы применяете его без какой-либо оценки, поэтому пишите:

$(hString "hello, world")

точно так же, как писать:

let s = "hello, world" in HashString (hash $ T.pack s) (T.pack s)

Но подумайте об этом так: вы используете [| ... |], чтобы заключить выражение в кавычки позже, и генерируете код во время компиляции с $(...). Таким образом, если вы включите некоторый код $(foo) в выражение в кавычках bla = [| bar $(foo) |], выполнение $(bla) сгенерирует код bar $(foo), который, в свою очередь, оценит foo во время компиляции. Кроме того, чтобы взять значение, которое вы генерируете во время компиляции, и сгенерировать выражение из него, вы используете функцию lift. Итак, что вы хотите сделать, это:

import Data.String (fromString)
import Language.Haskell.TH.Syntax

hString s = [| HashString $(lift . hash . T.pack $ s) (fromString s) |]

Это оценивает хеш-функцию во время компиляции, поскольку внутреннее соединение разрешается после того, как внешнее соединение было разрешено. Кстати, использование fromString из Data.String является общим способом построения некоторого OverloadedString типа данных из String.

Кроме того, вы должны рассмотреть вопрос о создании квази-квотера для вашего HashString интерфейса. Использование квазиквотеров более естественно, чем вызов функций сплайсинга вручную (и вы уже использовали их; безымянный [| ... |] кавычка цитирует выражения Haskell).

Вы бы создали квазиквотер так:

import Language.Haskell.TH.Quote

hstr =
  QuasiQuoter
  { quoteExp = hString -- Convenient: You already have this function
  , quotePat = undefined
  , quoteType = undefined
  , quoteDec = undefined
  }

Это позволит вам написать HashString с этим синтаксисом:

{-# LANGUAGE QuasiQuotes #-}
myHashString = [hstr|hello, world|]
...