2016-12-13 6 views
1

У меня есть функция TemplateHaskell создавая имя класса:TemplateHaskell имя класса с NEWNAME конфликта

test :: Q [Dec] 
test = do 
    clsname <- newName "A" 
    a <- newName "a" 
    return [ 
    ClassD [] clsname [PlainTV a] [][] 
    ] 

Имя класса генерируется с newName, так должно быть конфликтными (причина я создаю экземпляры непосредственно в TH и не нужно, чтобы это было видно).

test 
test 

Schema.hs:27:1: error: 
    Multiple declarations of ‘A’ 
    Declared at: Schema.hs:26:1 
       Schema.hs:27:1 

Однако тестирования с Debug.Trace, название A действительно что-то вроде A_1627476119. Это то же самое как в GHC 7.10.3, так и в GHC8. Является ли это ошибкой или я понимаю это неправильно?

ответ

0

newName не работает так, как вы думаете. Он не создает случайный неиспользуемый символ, используя предоставленную строку только как префикс, и, насколько я могу судить, для шаблона Haskell нет стандартной функции. Однако вы можете получить эффект, эквивалентный с:

gensym :: String -> Q Name 
gensym pfx = mkName . show <$> newName pfx 

, который должен работать для анонимных классов:

test :: Q [Dec] 
test = do 
    clsname <- gensym "A" -- use gensym here 
    a <- newName "a"  -- this is fine, though 
    return [ 
    ClassD [] clsname [PlainTV a] [][] 
    ] 

Если вы заинтересованы в более объяснения, какие newNameделает сделать, это создать имя, которое невозможно захватить с помощью «более глубоких» привязок, но оно делает это путем добавления дополнительной информации к созданному объекту Name, а не путем изменения фактического имени. Если для создания привязки используется такой Name, привязка использует исходное имя , а не измененную версию.

Чтобы убедиться в этом, заметим сначала, что Name созданный mkName имеет больше структуры, чем для печати представления предполагает:

GHCi> :m Language.Haskell.TH Language.Haskell.TH.Syntax 
GHCi> nm <- runQ (newName "foo") 
GHCi> nm 
foo_16 
GHCi> let Name occname nmtype = nm 
GHCi> occname 
OccName "foo" 
GHCi> nmtype 
NameU 16 
GHCi> 

Во-вторых, обратите внимание, что цитата:

[d| one = 1 |] 

эквивалентно нижеследующий do-block с использованием newName:

do nm <- newName "one" 
    decl <- valD (varP nm) (normalB (litE (integerL 1))) [] 
    return [decl] 

так что вы можете написать следующее:

{-# LANGUAGE TemplateHaskell #-} 

import Language.Haskell.TH 

$(do nm <- newName "one" 
    decl <- valD (varP nm) (normalB (litE (integerL 1))) [] 
    return [decl]) 

main = print one 

, иллюстрирующим, что «один» имя, созданное newName может быть использовано для создания верхнего уровня связывания, на который ссылаются в функции main используя свое обычное неприкрашенное, имя : one. (Если вы создали дополнительную копию сращивания здесь, вы получите те же «множественные декларации», которые вы получили с вашими классами.)