72

I want a TemplateHaskell function variablesInScope :: Q [Name] that returns a list of the Name's of all the variables in scope. TemplateHaskell obviously has this information available in order to implement functions like reify :: Name -> Q Info and lookupValueName :: String -> Q (Maybe Name).

Does the function I want exist somewhere and I just overlooked it? Or can it be easily built somehow?

Mike Izbicki
  • 6,286
  • 1
  • 23
  • 53
  • 2
    I'm fairly certain you can't do this with just TH, but you can use `haskell-src-meta` to parse a Haskell module as a TH AST. – user2407038 Oct 16 '14 at 01:27
  • 1
    Would that require using the IO features of the `Q` monad to load the module, and then send that to `haskell-src-meta`? yikes. Also, this can't disambiguate which name to use in the particular scope the splice is at. – Mike Izbicki Oct 16 '14 at 03:52
  • 1
    Yes, you need to use IO to actually read the file. I'm not sure I understand your second statement - why would you need to disambiguate if you are getting *all* names? You could open a ticket for a feature request, I suspect that the underlying mechanism which powers TH has all in-scope names available anyways. – user2407038 Oct 16 '14 at 14:11
  • 1
    I've created the tickect ([#9699](https://ghc.haskell.org/trac/ghc/ticket/9699#ticket)). What I mean by the second point is that a function might declare a variable called `print`, and then the `print` from `System.IO` would no longer be in scope. So the result of `variablesInScope` has a pretty complicated dependence on where in the code the splice occurs. – Mike Izbicki Oct 16 '14 at 16:37

1 Answers1

2

Unfortunately, you cannot do this with TH alone. Try the haskell-src-meta to parse the Haskell module as a TH AST.

It will require the IO features of the Q monad to load the module though.

Please reference https://ghc.haskell.org/trac/ghc/ticket/9699#ticket to see the current rough spec

(1) Extend ​ModuleInfo (obtained from ​reifyModule) to ModuleInfo [Module] [Name], where [Module] is still the import list and [Name] contains the module's list of exported names.

(2) Add thisModule :: Q Module producing the current ​Module.

(3) Add topLevelNames :: Q [Name] producing a list of top-level names (both exported and non-exported) bound in the current module that would be visible to reify.

(4) Add nestedNames :: Q [Name] (in need of a better name) producing a list of the non-top-level (nested) names visible to reify in this context.

(5) Add parentNames :: Q [Name] (also in need of a better name) producing a list of the names immediately associated with the current splicing context, if available. For example, foo, bar :: $(typeSplice) would see [foo, bar], foo = $(exprSplice) would see [foo], and $(topLevelDecSplice) would see [].

(6) Optional Add isTopLevel :: Name -> Q Bool to detect whether a name is bound at the top level (of the current module?). Something like this could alternately be accomplished by searching through topLevelNames.

Community
  • 1
  • 1
Kirk Strobeck
  • 17,984
  • 20
  • 75
  • 114