Search code examples
haskellhackage

Why does Hackage link a module to a different source file?


I am trying to understand the structure of the base package as displayed by Hackage. Following https://en.wikibooks.org/wiki/Haskell/Modules a module name must reflect the path to its source; to quote the above:

The name of the file is the name of the module plus the .hs file extension. Any dots '.' in the module name are changed for directories.

Taking Data.List as an example, this lead me to believe that there should be a source file under a path ../Data/List.hs that then contains

module Data.List where ...

(modulo export list).

On the other hand, if I browse the Data.List module on Hackage and follow the '# Source' links, I am directed to source files for various different modules. Among those are: GHC.Base, Data.OldList, Data.Foldable, GHC.List.

On the first hand again, my local installation of the base package clearly contains a ../Data/List.hi interface file.

My question is therefore: How can the discrepancy be explained? What is really on display on Hackage?

Thanks in advance for any insight on the matter!


Solution

  • Names imported from modules can be re-exported. When that happens, haddock helpfully links you to the original source of the name, rather than to the import line in the re-exporting module; it follows the indirection for you. This is what's happening in your case. So taking (++) (the first function in the haddocks for Data.List) as an example, the code is structured like this:

     -- GHC/Base.hs
     module GHC.Base where
     (++) = ...
    
     -- Data/OldList.hs
     module Data.OldList ( (++) {- re-exports GHC.Base's (++) -}, ... ) where
     import GHC.Base -- brings (++) into scope
    
     -- Data/List.hs
     module Data.List ( (++), ... ) where
     import Data.OldList hiding ( ... {- does not mention (++) -} )
    

    So you can see haddock has actually followed two links: (++) was imported from Data.OldList, and even there it was a re-export.

    The compiler carefully tracks the original module that defined a name, too; if you import all of Data.List, Data.OldList, and GHC.Base, you will find that you can still use (++) even though it seems at first blush as though it might be ambiguous which of (Data.List.++), (Data.OldList.++), or (GHC.Base.++) you meant. Since all three actual eventually resolve to (GHC.Base.++), there is no ambiguity.