Opened 4 years ago

Last modified 15 months ago

#10391 patch feature request

Ability to get export list of TH reified module

Reported by: dmcclean Owned by: mgsloan
Priority: low Milestone:
Component: Template Haskell Version:
Keywords: Cc:
Operating System: Unknown/Multiple Architecture: Unknown/Multiple
Type of failure: None/Unknown Test Case: T10391
Blocked By: Blocking:
Related Tickets: #10842 Differential Rev(s): Phab:D4925
Wiki Page:

Description

After using reifyModule, I'd like to be able to inspect the list of exports from that module.

At first I thought that this could perhaps be done by changing from

data ModuleInfo =
  -- | Contains the import list of the module.
  ModuleInfo [Module]

to

data ModuleInfo =
  -- | Contains the import list and export list of the module. 
  ModuleInfo [Module] [Name]

but I now wonder if this is the best plan, because thisModule >>= reifyModule might be expected to function as a time machine, allowing us to see declarations that may not have been made yet (in the case where this module's export list is implicit).

Can anyone propose a type that would allow us to perform this inspection for modules that have already been baked, but not for "this" module?

An intended use case would be in our dimensional types library. We have several modules that define a whole bunch of units. It's useful for the unit name parser to have access to a dictionary with all of those units in it, but maintaining the definition of such a dictionary is redundant. If this feature existed, a module in the parser could import the modules where units are defined, examine the export lists, reify the names in the export lists, determine which represent unit definitions, and emit a declaration of a dictionary that contains them all.

(This is in some ways similar to #9699, though perhaps more difficult?)

Change History (8)

comment:1 Changed 4 years ago by dmcclean

Summary: Ability to get contents of TH reified moduleAbility to get export list of TH reified module

comment:2 Changed 4 years ago by goldfire

I'm not too bothered about the types here: I think it's reasonable just to have a TH-runtime error if someone tries to use a time machine.

But what would be a better answer here is an API specification that also addresses #1475, which covers full support for import/export lists. I'd settle for just exports, as imports can be separated quite easily. Unfortunately, I don't have the time to dive into this now, but would be happy to support someone else who did.

comment:3 Changed 4 years ago by dmcclean

I'm not sure how strong the connection to #1475 is, because that ticket is about writing new exports in splices, whereas this one is about reading existing splices.

I'd like to take a swing at this one, and I've just traced through enough of the code to have a good plan of attack, but I barely even know where to start to tackle #1475, so if you feel strongly that there's something to be gained by attacking both at once then I may not be the man for the job. (Which isn't to say that I won't try, just that I might need considerable help getting up to speed.)

Setting aside #1475, on this one the meat of it happens [here](https://github.com/ghc/ghc/blob/4efa421327cf127ebefde59b2eece693e37dc3c6/compiler/typecheck/TcSplice.hs#L1525-L1533):

reifyThisModule = do
  usages <- fmap (map modToTHMod . moduleEnvKeys . imp_mods) getImports
  return $ TH.ModuleInfo usages

reifyFromIface reifMod = do
  iface <- loadInterfaceForModule (ptext (sLit "reifying module from TH for") <+> ppr reifMod) reifMod
  let usages = [modToTHMod m | usage <- mi_usages iface,
                               Just m <- [usageToModule (modulePackageKey reifMod) usage] ]
  return $ TH.ModuleInfo usages

My plan would be to extend the reifyFromIface bit to bind the exported names from the AvailInfos in the mi_exports field of iface, flattening them into a list of names. (I'm not sure quite how to get from Name.Name to Language.TH.Syntax.Name though...) Then I would add the list of names to the TH.ModuleInfo.

I would extend the reifyThisModule bit to TH.ModuleInfo usages (error "Accessing the list of names exported by the current module is not supported."), or something to that effect. I assume there are style conventions on how to write such errors, I could use a pointer on where to look for that.

comment:4 Changed 4 years ago by goldfire

Yes -- I see better how these are decoupled. (I hadn't looked at the API before responding.) Yes, it's reasonable to tackle this separately from #1475.

I also didn't realize that you're hooking into existing functions. (I was unaware you could get imports via TH.) It might be worth squawking on ghc-devs (or perhaps libraries@…) to see if anyone has strong feelings about the API choice. Maybe it's better to deliver imports and exports separately? Maybe have a Maybe [Name] for the exports to account for the thisModule case? For some reason, I'm OK with a call to a function reifyThisModule'sExports to throw an error, but I dislike embedding a call to error in an otherwise-proper data structure.

Sorry for totally changing my mind on all of these issues, but that's what happens sometimes. :)

The implementation plan, from a 30,000 ft view, looks about right. I think reifyName is the name conversion function you want.

comment:5 Changed 4 years ago by thomie

comment:6 Changed 15 months ago by mgsloan

Owner: set to mgsloan

I might make progress on this, so assigning this to myself for now.

comment:7 Changed 15 months ago by mgsloan

Differential Rev(s): Phab:D4925

I've implemented this feature in https://phabricator.haskell.org/D4925 ! I was pleasantly surprised to see that this was fairly straightforward to implement, though there was a fair bit to consider design-wise. Wish I'd known how small the change would be when I was itching for this years ago!

comment:8 Changed 15 months ago by mgsloan

Status: newpatch
Test Case: T10391
Note: See TracTickets for help on using tickets.