Opened 3 years ago

Closed 2 years ago

#4491 closed bug (fixed)

dataToQa uses only unqualified names when converting values to their TH representation.

Reported by: gmainland Owned by:
Priority: normal Milestone: 7.4.1
Component: Template Haskell Version: 6.12.3
Keywords: Cc:
Operating System: Unknown/Multiple Architecture: Unknown/Multiple
Type of failure: None/Unknown Difficulty:
Test Case: Blocked By:
Blocking: Related Tickets:


The dataToQa function in Language.Haskell.TH.Quote always use unqualified names when converting a value to its TH representation, even if the names are not in scope in the TH splice where they are called.

I have attached a patch that makes things a bit better, but there are still a number of outstanding issues.

The problem is that the Data type class allows one to find the name of the module in which a data constructor is declared, but it does not allow one to find the name of the package in which the constructor is declared. On the other hand, TH lets you either create a qualified name that is resolved using the namespace in effect at the point of a TH splice, or create a fully resolved name if you know the package in which it is declared. The patch I have attached changes dataToQa to create all names as qualified names, but the resulting TH only compiles without error if the data constructors that are used are imported in such a way that they can be qualified with the name of the module in which they are declared. Examples that assume the new definition of dataToQa follow.

Assume the file A.hs exists with the contents:

{-# LANGUAGE DeriveDataTypeable #-}

module A where

import Data.Generics

data Foo = Foo Int
  deriving (Show, Data, Typeable)

Now this program will run and print Foo 1:

{-# LANGUAGE TemplateHaskell #-}

import Language.Haskell.TH

import A

main :: IO ()
main = print $(dataToExpQ (const Nothing) (Foo 1))

So will this program:

{-# LANGUAGE TemplateHaskell #-}

import Language.Haskell.TH

import qualified A

main :: IO ()
main = print $(dataToExpQ (const Nothing) (A.Foo 1))

But this program will not compile:

{-# LANGUAGE TemplateHaskell #-}

import Language.Haskell.TH

import A as B

main :: IO ()
main = print $(dataToExpQ (const Nothing) (Foo 1))

Instead it gives the following error:

Not in scope: data constructor `A.Foo'
In the first argument of `print', namely
    `$(dataToExpQ (const Nothing) (Foo 1))'
In the expression: print ($(dataToExpQ (const Nothing) (Foo 1)))
In the definition of `main':
    main = print ($(dataToExpQ (const Nothing) (Foo 1)))

This is expected, since dataToQa creates TH qualified names but uses the resolved module name, A, as the qualifier.

The current (unpatched) version of dataToQa creates all TH names using mkName, i.e., it creates all names unqualified. I think always using qualified names instead is preferable. It may break existing code, but it gives the programmer better control (that is, some control!) over namespace pollution.

There are two better solutions.

The first would be to change the Data type class so that it exposes the package name as well as the module name in which data constructors are declared. This seems GHC-specific though and a bit of a hack.

I think the ideal solution is to add a smart TH name constructor that allows one to create a resolved name by specifying the resolved module name without requiring that the package name be specified. Of course if different packages define the same data constructor and they both happen to be in scope where the splice occurs, then there will be a static error, but I suspect this would only happen in a module that uses PackageImports, and even then it would be rare. This might require adding a member to the Q monad that allows "resolving" names or something, which I'm sure is non-trivial. I would be willing to work on a patch if given a few hints on how to approach the problem and assuming there's a willingness to adopt such a patch.

Attachments (1)

Language-Haskell-TH-Quote.diff (1.6 KB) - added by gmainland 3 years ago.
patch to Language.Haskell.TH.Quote

Download all attachments as: .zip

Change History (8)

Changed 3 years ago by gmainland

patch to Language.Haskell.TH.Quote

comment:1 Changed 3 years ago by simonpj

Interesting. What is a "resolved module name"? And how would you get hold of the resolved module name for the constructor without changing Data?

I'd certainly entertain a patch. As you say, changing Data would be a fairly big deal, so if you can figure out a way to solve the problem without doing so, that'd be much easier. I'd totally forgotten about dataToQa and friends, and had to go back to your paper to figure out what they do. (Even then I'm shaky. Could you add some more explicit Haddock comments?) So I doubt they are used a lot, and changes there would be easier.


comment:2 Changed 3 years ago by gmainland

Sorry for the muddled terminology. When I wrote "resolved module name" I meant the moduleName of the Module component of the NameSort of a Name (whew...). An "unresolved module name" would be the ModuleName component of a qualified RdrName. Perhaps "renamed module name" and "qualifier" are better terms? What is the correct terminology?

Given an x that is an instance of Data, (showConstr . toConstr) x is the (unqualified) name of the data constructor used to construct x and (tyconModule . dataTypeName . dataTypeOf) x is the name of the module in which the corresponding type constructor is defined. Both are (newtype'd) strings. I would like to use these to get Template Haskell to create an original name, not a qualified name. The module defining the constructor may not be in scope, or it could be in scope but aliased due to an "as" import, so creating a qualified name is not the right thing to do.

Two things that might get us where I want to be:

  1. Add a constructor to Language.Haskell.TH.Syntax.NameFlavour, NameO ModName, that represents an original name.
  1. In GHC, have a way to convert a ModuleName to a Module, i.e., find the package that defines the ModuleName.

Then when converting TH, GHC could apply (2) to (1) and use the Orig data constructor to create a RdrName.

Wired-in names would still have to be handled separately (the attached patch does this more-or-less correctly, I think).

comment:3 Changed 3 years ago by igloo

  • Milestone set to 7.2.1

comment:4 Changed 3 years ago by simonpj

Hang on. The NameFlavour type already has a NameG constructor that lets you construct an original name. You supply the module name and package and away you go. It may not be exported properly, but isn't that what you need?

comment:5 Changed 3 years ago by gmainland

Yes, that along with (2) from above is what I need---I meant (1) and (2) to be mutually exclusive, sorry. The difficulty is getting the package name from just a module name. I have a small patch to HEAD that adds a qModPackage member of type ModName -> m PkgName to the Quasi monad and an implementation in TcSplice.lhs. I think that solves my problem, but I haven't fully tested the patch.

comment:6 Changed 3 years ago by nicolas.frisby

Has anyone proposed something along the lines of

Data.Data.toConstrTH :: a -> Language.Haskell.TH.Name

? I've simulated this with SYB3 (and corresponding copy-tweak-and-paste of dataToQa) in a project and it worked pretty nicely. Coupling Data.Data and TH like so does seem a bit heavy handed…

comment:7 Changed 2 years ago by gmainland

  • Resolution set to fixed
  • Status changed from new to closed
Note: See TracTickets for help on using tickets.