wiki:Commentary/Packages/GhcPackagesProposal

Version 8 (modified by simonpj, 8 years ago) (diff)

--

This page summarises our current proposal for packages in GHC.

The problem

A vexed question in the current design of Haskell is the issue of whether a single program can contain two modules with the same name. Currently that is absolutely ruled out, and as a result packages are fundamentally non-modular: every package must use a distinct space in the global namespace.

There are two quite separate issues, addressed in the following two sections. But before we start, note that we take for granted the following

  • Each package has a globally-unique name, organised by some social process

This assumption is deeply built into Cabal, and lots of things would need to change if it wasn't met.

Question 1: Can two different packages contain a module with the same module name?

We now think that's unreasonable to answer 'no', because that means that EVERY module in EVERY package written by ANYONE must have different module names. That's like saying that every function must have different local variables, and is a serious loss of modularity. We suspect that this is something about which we can all agree.

The only sensible way to fix it is to relax the language design so that

  • A module name must be unique within its package (only)

That means that module A.B.C could exist *both* in package P1 and in P2. And both packages could be linked into the same program. Suppose for the moment that A.B.C is not exposed by both P1 and P2. Then you would say simply:

  ghc --make Main -o app

The authors of packages P1 and P2 didn't need to know about each other, and don't need to choose globally unique module names.

The fundamental thing GHC needs to do is to include the package name into the names of entities the package defines. That means that when compiling a module M you must say what package it is part of:

  ghc -c -package-name P1 C.hs

Then C.o will contain symbols like "P1.A.B.C.f" etc. In effect, the "original name" of a function f in module M of package P is <P,M,f>.

But what if two packages expose the same module A.B.C? That takes us to Question 2.

Question 2. When you say "import A.B.C", from what package does A.B.C come?

Here GHC already has a fairly elaborate scheme (perhaps too elaborate).

  • For a start, you may or may not have a package installed.
  • Even if you do, the package may or may not be exposed by default (reasoning: you may want old versions of package X to be installed, but not in scope by default).
  • Then, you can use the -hide-package flag to hide an otherwise-exposed package, and the -package flag to expose an otherwise-hidden package.

By manipulating these flags, you can expose package P1 when compiling module M (say), and expose P2 when compiling module N. Then M and N could both import module A.B.C, which would come from P1 and P2 respectively. But:

  • What if you wanted to import A.B.C from P1 and A.B.C from P2 into the same module?
  • Compiling different modules with different flags in a way that affects the semantics (rather than, say, the optimisation level) seems undesirable.
  • To support --make in this situation we'd need to allow -package flags in the per-module OPTIONS pragmas, which isn't currently supported. (ghc --make already gathers those options together for the link step.)

The obvious solution is to allow the programmer to specify the source package in the import line, something like this:

  import A.B.C from "base" ( map, filter )

That would presumably get the most recent installed incarnation of the base package. If you want a particular version of the package, we could allow

  import A.B.C from "base-3.4" ( map, filter )

The exact syntax is unimportant. The important thing is that the programmer can specify the package in the source text.

Alternative: the Packages space

Perhaps every (exposed) module from every (installed) package should always be available via an import like

   import Packages.Gtk-1_3_4.Widget.Button

That is, the module is name by a fully-qualified name involving its package name (already globally unique).

(Tiresome side note: to make the package id look like a module name we may have to capitalise it, and change dots to underscores. And that could conceivably make two package names collide.)

Alterative: grafting

Some kind of 'grafting' or 'mounting' scheme could be added, to allow late binding of where in the module tree the is brought into scope. One might say

	ghc -c Foo.hs -package gtk-2.3=Graphics.GTK

to mount the gtk-2.3 package at Graphics.GTK in the module name space. Outside the package one would need to import Graphics.GTK.M, but within the package one just imports M. That way the entire package can be mounted elsewhere in the namespace, if desired, without needing to change or recompile the package at all.

This would allow a single module to import modules from two different packages that happened to use the same name. It's not strictly a necessary feaure. If you want to

  • import module A from package P, and
  • import module A from package Q into a single module M of a program,

you can always do this:

  • make a new module AP, that imports A and re-exports it all;
  • compile AP with package P visible and Q hidden
  • ditto for AQ
  • make M say "import AP; import AQ".

The exact details of the mounting scheme, and whether it is done at build time, at install time, or at compilation time, or all of the above, are open to debate. We don't have a very fixed view.