Reputation: 9331
I have a TemplateHaskell function creating a class name:
test :: Q [Dec]
test = do
clsname <- newName "A"
a <- newName "a"
return [
ClassD [] clsname [PlainTV a] [][]
]
The classname is generated with newName
, so should be conflict free (the reason is I create the instances directly in TH and don't need it to be visible).
test
test
Schema.hs:27:1: error:
Multiple declarations of ‘A’
Declared at: Schema.hs:26:1
Schema.hs:27:1
However testing it with Debug.Trace, the name of A
is indeed something like A_1627476119
. This is the same in both GHC 7.10.3 and GHC8. Is this a bug or do I understand it wrong?
Upvotes: 2
Views: 131
Reputation: 50819
newName
doesn't work the way you're imagining. It doesn't create a random unused symbol using the supplied string merely as a prefix, and -- as far as I can tell -- Template Haskell doesn't have a standard function to do that. However you can get the equivalent effect with:
gensym :: String -> Q Name
gensym pfx = mkName . show <$> newName pfx
which should work for your anonymous classes:
test :: Q [Dec]
test = do
clsname <- gensym "A" -- use gensym here
a <- newName "a" -- this is fine, though
return [
ClassD [] clsname [PlainTV a] [][]
]
If you're interested in the longer explanation, what newName
does do is create a name that cannot be captured by "deeper" bindings, but it does this by attaching additional information to the created Name
object, not by mangling the actual name. If such a Name
is used to create a binding, the binding uses the original supplied name, not a mangled version.
To see this, note first that the Name
created by mkName
has more structure than its printable representation suggests:
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>
Second, note that the quotation:
[d| one = 1 |]
is equivalent to the following do-block using newName
:
do nm <- newName "one"
decl <- valD (varP nm) (normalB (litE (integerL 1))) []
return [decl]
so you can write the following:
{-# LANGUAGE TemplateHaskell #-}
import Language.Haskell.TH
$(do nm <- newName "one"
decl <- valD (varP nm) (normalB (litE (integerL 1))) []
return [decl])
main = print one
illustrating that the "one" name created by newName
can be used to create a top-level binding that is referenced in the main
function using its plain, unadorned, name: one
. (If you created an extra copy of the splice here, you'd get the same "multiple declarations" error you got with your classes.)
Upvotes: 2