Get a list of what is exported by a module

Viewed 394

Similar question (Is there a way to see the list of functions in a module, in GHCI?), though not the result that I seek.

Is there a way to get a list of what is exported by a module?

Of course in GHCi you can import it then type Some.Module., hit tab for auto-completion and it will show what I seek. But I want to capture that stuff. Roughly speaking, String -> [String].

Purpose? Suppose that I have a source file with a naked import Some.Module. Question: What belongs to Some.Module in that file? A simple way would be to output the list of what the module exports, feed that to grep and return the contenders, without the need to load that source file in GHCi (might be complicated or not possible). And everything becomes a lot clearer.

If there's a smarter approach to that, I'm listening. I heard of solutions involving GOA and lambdabot. No idea if applicable or how to make use of this.

2 Answers

As @HTNW mentioned in a comment, if you can run ghc on your actual file, you can use -ddump-minimal-imports. Otherwise, if you want to actually get the list of exports from another module, assuming that you're using GHC, the easiest way to do this is probably to look at the .hi interface files. ghc has some built-in support for printing human-readable representations of interface files once you know the path to one, but as the wiki page notes "This textual format is not particularly designed for machine parsing". You can also access the information you might want via the GHC API. A small example of doing something like that follows.

We start with a bunch of random imports for doing IO & from the GHC api:

import Control.Monad.IO.Class
import System.IO
import System.Environment

import GHC
import GHC.Paths (libdir)
import DynFlags
import Outputable
import Name
import Pretty (Mode(..))

With that bureaucracy out of the way, main starts by firing up the GHC Monad:

main :: IO ()
main = defaultErrorHandler defaultFatalMessager defaultFlushOut $ do
  runGhc (Just libdir) $ do

We're not actually generating any code so we can set hscTarget = HscNothing during the DynFlags setup boilerplate:

    dflags <- getSessionDynFlags
    let dflags' = dflags { hscTarget = HscNothing }
    setSessionDynFlags dflags'

With that out of the way we can find the module we want from the package database (using the first command-line argument as the name):

    mn <- head <$> (liftIO $ getArgs)
    m <- lookupModule (mkModuleName mn) Nothing

We can use getModuleInfo to get a module info structure:

    mmi <- getModuleInfo m
    case mmi of
      Nothing -> liftIO $ putStrLn "Could not find module interface"

If we did find the interface, everything we need for this is in the modInfoExports. If we needed more, we could also get the actual ModIface:

      Just mi -> mapM_ (printExport dflags') (modInfoExports mi)

Actually printing out an exported is a bit tedious, as it requires working with Names; a simple example printExport might just use the pretty-printing functions, but these are more intended for printing human-readable output than machine-readable:

printExport :: DynFlags -> Name -> Ghc ()
printExport dflags n =
  liftIO $ printSDocLn PageMode dflags stdout (defaultUserStyle dflags)
         $ pprNameUnqualified n

A particularly simple way for interactive use is :browse. Load up a ghci that has access to the appropriate package, then

> :browse Some.Module
class Some.Module.Foo a where
  Some.Module.foo :: a -> a
  {-# MINIMAL foo #-}
Some.Module.bar :: Int

All the qualification can get a bit much, especially if there are many functions that operate on types defined in the same module. To reduce the clutter, you can bring Some.Module into scope first:

> :m + Some.Module
> :browse Some.Module
class Foo a where
  foo :: a -> a
  {-# MINIMAL foo #-}
bar :: Int
Related