Another elementary question about using GHC: finding the package containing a module when using existing code. This question has been asked several times here, but none of the answers seem to really address this basic issue.
I expected that the answer would be to use hackage, but the search facility is peculiar or perhaps under-documented.
Example: I have
import System.Random
So I visit hackage and try to search for System.Random with the search packages facility.
I get back mersenne-random-pure64
, randfile
and normaldistribution
. But not the random
package which is what I am expecting from past experience.
Searching with just Random, however, does return the random package among many others. Which may suggest that "." is some sort of meta-character. But what, and why is there no documentation?
Somewhere, I saw that the search was in a package's metadata without defining the term metadata. I downloaded the random package tarball and looked at the contents and "System.Random"
was in the description field of random.cabal
among other places, so why can't Hackage see it?
Or is there some other way to identify a package or packages containing a particular module?
Here's an awful hack you can use:
cabal list | grep '^\*' | cut -d' ' -f2 | xargs cabal info 2>/dev/null \
| grep '^\(\*\| *System\.Random$\)' | grep -B1 ' *System\.Random$' \
| grep '^\*'
Explanation:
cabal list
: show some basic info about every package cabal knows aboutgrep '^\*'
: keep only lines with a package name on themcut -d' ' -f2
: keep only the package name from each line; we now have a long list of packagesxargs cabal info 2>/dev/null
: produce detailed information about each package, including their module lists; because cabal
is invoked many times in the pipeline, any warnings or errors sent to stderr will get repeated many times, so we redirect those away every time except the first cabal list
with 2>/dev/null
grep '^\(\*\| *System\.Random$\)'
: keep only lines that have a package name (^\*
) or a module name that matches the pattern we like (^ *<pattern>$
)grep -B1 '^ *System\.Random$'
: -B1
says to keep one line before each match, which should be the package name that exposes the module that matchedgrep '^\*'
: keep only the lines with package names (tossing the module names and some other diagnostic output from the previous grep
)Of course, if cabal could do the same thing itself, that would be much cleaner and probably much faster as well.
On my system, the pipeline above prints only the random
package, which seems right to me. A more exciting search for Data.List
shows that this can produce many results if there are such, listing base
, base-noprelude
, bizzlelude
, bizzlelude-js
, fay-base
, haskell2010
, haskell2020
, liquid-base
, rerebase
, and safe-coupling
. More packages than I expected export that module!
Compared to using hoogle, this uses a more complete initial package list (all of Hackage rather than all of Stackage) and gives you flexibility over whether you are searching for an exact module name (as above) or the prefix of a module hierarchy (drop the $
from the module patterns). However it is more fragile, possibly producing false positives if your module pattern matches some other part of the package information than the exported modules list, and is tightly coupled with details of cabal
's current output format.