TemplateHaskell class 名称与新名称冲突
TemplateHaskell class name with conflict newName
我有一个 TemplateHaskell 函数创建一个 class 名称:
test :: Q [Dec]
test = do
clsname <- newName "A"
a <- newName "a"
return [
ClassD [] clsname [PlainTV a] [][]
]
classname 是用 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 中都是相同的。这是一个错误还是我理解错了?
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
所做的 所做的是创建一个无法被 "deeper" 绑定捕获的名称,但是它通过将附加信息附加到创建的 Name
对象来实现这一点,而不是通过破坏实际名称。如果这样的 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 |]
等同于以下使用 newName
的 do-block:
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
创建的 "one" 名称可用于创建在 main
函数中引用的顶级绑定,使用其简单、朴素的名称: one
。 (如果你在这里创建了一个额外的拼接副本,你会得到与你的 类 相同的 "multiple declarations" 错误。)
我有一个 TemplateHaskell 函数创建一个 class 名称:
test :: Q [Dec]
test = do
clsname <- newName "A"
a <- newName "a"
return [
ClassD [] clsname [PlainTV a] [][]
]
classname 是用 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 中都是相同的。这是一个错误还是我理解错了?
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
所做的 所做的是创建一个无法被 "deeper" 绑定捕获的名称,但是它通过将附加信息附加到创建的 Name
对象来实现这一点,而不是通过破坏实际名称。如果这样的 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 |]
等同于以下使用 newName
的 do-block:
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
创建的 "one" 名称可用于创建在 main
函数中引用的顶级绑定,使用其简单、朴素的名称: one
。 (如果你在这里创建了一个额外的拼接副本,你会得到与你的 类 相同的 "multiple declarations" 错误。)