ondra
ondra

Reputation: 9331

TemplateHaskell class name with conflict newName

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

Answers (1)

K. A. Buhr
K. A. Buhr

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

Related Questions