base library and GHC 6.12

Simon Marlow marlowsd at gmail.com
Mon Jun 29 08:22:21 EDT 2009


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


More information about the Libraries mailing list