2013-05-02 4 views
6

У меня есть следующий код в модуле:Template Haskell: GHC ограничение стадии и как преодолеть

{-# LANGUAGE TemplateHaskell #-} 

module Alpha where 

import Language.Haskell.TH 
import Data.List 

data Alpha = Alpha { name :: String, value :: Int } deriving (Show) 
findName n = find ((== n) . name) 

findx obj = sequence [valD pat bod []] 
    where 
     nam = name obj 
     pat = varP (mkName $ "find" ++ nam) 
     bod = normalB [| findName nam |] 

А потом я следующий в главном файле:

{-# LANGUAGE TemplateHaskell #-} 

import Alpha 

one = Alpha "One" 1 
two = Alpha "Two" 2 
three = Alpha "Three" 3 
xs = [one, two , three] 

findOne = findName "One" 
findTwo = findName "Two" 

$(findx three) -- This Fails 
$(findx (Alpha "Four" 4)) -- This Works 

main = putStrLn "Done" 

Я бы как $(findx three) для создания findThree = findName "Three" для меня. Но вместо этого я получаю эту ошибку:

GHC stage restriction: `three' 
    is used in a top-level splice or annotation, 
    and must be imported, not defined locally 
In the first argument of `findx', namely `three' 
In the expression: findx three 

Как это преодолеть? Я бы предпочел не определять one, two и т. Д. В отдельном файле.

Второй вопрос: почему $(findx (Alpha "Four" 4)) работает без проблем?

+1

Если вы просто пытаетесь сослаться на имя в том же модуле, что и определено, вы можете сделать это с кавычками/квазицитированием каким-то образом? Если вы пытаетесь на самом деле _execute_ что-то в сращивании, забудьте об этом. Компилятор принципиально не может этого сделать. – MathematicalOrchid

+1

Хорошо. В чем разница между '$ (findx three)' и '$ (findx (Alpha" Four "4))'? Первое действительно приводит к ошибке, но последнее работает. – me2

+1

'three' определяется в том же модуле, а' Alpha' - нет. – yiding

ответ

6

Я не очень по Template Haskell сам, но на основе моего ограниченного понимания проблемы в том, что three в каком-то смысле «все еще определяется», когда GHC пытается скомпилировать $(findx three), в то время как все составные части из $(findx (Alpha "Four" 4)) являются уже полностью определены.

Основная проблема заключается в том, что все определения в одном модуле влияют на значение друг друга. Это связано с типом вывода, а также с взаимной рекурсией. Определение x = [] может означать множество разных вещей, в зависимости от контекста; он может быть привязан x к списку Int, или к списку IO(), или что-нибудь еще. GHC, возможно, придется обработать весь модуль, чтобы выяснить, что именно это делает означает (или что это на самом деле ошибка).

Код, который Template Haskell испускает в модуль, который компилируется, должен быть рассмотрен этим анализом. Таким образом, код шаблона Haskell должен запускаться до GHC выяснил, что означают определения в модуле, и поэтому логически вы не можете использовать ни один из них.

Вещи, которые были импортированы из других модулей OTOH, были полностью проверены, когда GHC скомпилировал , что модуль. Нет больше информации, которую нужно узнать о них, скомпилировав этот модуль. Таким образом, они могут быть доступны и использованы до компиляции кода в этом модуле.

Другой способ подумать об этом: возможно, three на самом деле не должен быть типа Alpha. Возможно, это была опечатка, а конструктор должен был быть Alphz. Обычно GHC узнает об этих типах ошибок, компилируя весь другой код в модуле, который использует three, чтобы узнать, не вступает ли это в несогласованность или нет. Но что, если , что код использует или используется вещами, которые испускаются только $(findx three)? Мы даже не знаем, какой код будет, пока мы его не запустим, но мы не можем решить вопрос о том, правильно ли набирается three, пока мы его не запустим.

Возможно, это будет , чтобы снять это ограничение в некоторых случаях (я не знаю, было бы легко или практично). Возможно, мы могли бы заставить GHC считать, что что-то «определено раньше», если оно импортировано, или если оно использует только другие вещи, которые «определены раньше» (и, возможно, имеет явную подпись типа).Возможно, он мог бы попытаться скомпилировать модуль без запуска кода TH, и если ему удастся полностью выполнить typecheck three, прежде чем он столкнется с какими-либо ошибками, он может передать это в код TH, а затем перекомпилировать все. Недостатком (помимо работы) было бы намного сложнее указать, какие точные ограничения касаются того, что вы можете передать шаблону Haskell.

+0

Я думаю, что ограничения на то, что вы могли бы передать Template Haskell, на самом деле не были бы намного сложнее, но кодирование может быть очень сложным. –