2016-12-13 40 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來創建綁定,綁定使用提供的原始名稱,而不是損壞的版本。

看到這一點,首先注意到,mkName創建Name有更多的結構比它可顯示的表示顯示:

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。 (如果您在此處創建了拼接的額外副本,則會得到與您的類相同的「多個聲明」錯誤。)