
On 27/06/2009 02:07, Henning Thielemann wrote:
Ian Lynagh schrieb:
Option 1 --------
In order to solve the version number issue, we could simply state that "base follows the PvP, but only for shared module hierarchies". However, it would be impossible for packages which /do/ need GHC.* modules to give accurately versioned dependencies, and it wouldn't solve the other issue at all.
Option 2 --------
Another possible solution would be to rename the base package to base-internals, and to make base only re-export the "public" modules. This would require either renaming each module M to Internal.M, or for every implementation to support something like GHC's PackageImports extension.
Option 3 --------
The other alternative is to try to split base into two parts: The shared "public" modules, and the internal GHC.* modules. Then GHC would have ghc-base, hugs would have hugs-base, etc, and there would be a common base package built on top of the appropriate impl-base.
This sounds most sensible to me. I would also like to see System.IO.Unsafe in a separate package. This would simplify running of untrusted code.
I think the point that hasn't been made clearly so far is this: it's simply not possible to get a clean split between the compiler-specific portions and the portable portions of the base package. Imagine trying to do this. Firstly, you have to establish a clear API boundary between the portable code and the compiler-specific implementations of various primitives. For instance, the definition of the Prelude in the Haskell 98 report refers to things like "primPlusInt". Now, move the implementations of the compiler-specific primitives into their own package, leaving the portable code behind. In theory this sounds fine, but in practice the compiler-specific code wants to depend on portable bits. A basic example is list operations like 'map'; almost certainly you'll want this in the compiler-specific package. There are many more; for example the FFI is needed pretty early on, but most of the FFI libraries are portable. When you start separating dependencies it all gets pretty tedious, and what's more you're never sure whether the dependencies might change in the future, requiring yet more code to move across the boundary in one direction or another. And you'll invariably end up with a bunch of portable code on the wrong side of the boundary. There's no good way to do it. I argue that separating the implementations like this (a) is a time sink and (b) makes the code harder, not easier, to work with. What's really important is that we can expose a portable API. The implementation can stay all in one package where it is convenient, letting us interleave portable and non-portable code in the dependency graph arbitrarily. Obviously we should continue to make every attempt to clearly separate portable from non-portable code using the module system, within the base package. So, unless it isn't clear, I claim option (2) is the way forward. Now, there are a couple of problems with it: - firstly, compilers other than GHC don't support re-exporting modules from packages - Haddock doesn't support it properly either, unless Isaac Dupree's SoC project comes to fruition in time for the release. This would be a show-stopper. Cheers, Simon