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" 错误。)