Version 9 (modified by malcolm.wallace@…, 9 years ago) (diff)

flesh out a concrete proposal for a minimal prelude

Shrink the Prelude

People sometimes complain that the Prelude steals lots of good names from the user. Think of 'map' or (+). Yes, most of the time we want these names to have a standard interpretation, but occasionally it is useful to be able to redefine them to mean something else. For instance, there are several proposals to change the prelude's numeric class hierarchy into something more flexible involving rings, groups, and so on.

But it is tedious if the user of such a proposed replacement for the Prelude must explicitly hide the standard prelude in every importing module.

Thus it might be useful to trim the Prelude down to the bare minimum possible. Most users of e.g. list functions would then need to import Data.List, users of numeric functions would need to import Data.Numeric, and so on. Of course, some users (e.g. university teachers) might want to collect a bunch of utility libraries into a single import resembling the current Prelude. But the point is that they could choose the features they want to expose to students, and hide those they want to avoid as well. For instance, there are certainly some teachers who would like to be able to ignore the class overloading system altogether at the beginning, then perhaps introduce the concept later on, once the basics have been covered.

Examples of proposals for restructuring the Prelude

  • revise the Numeric class hierarchy, e.g. collapse classes RealFrac, Fractional, and Floating.
  • make String into a class rather than a type, with instances for [Char], PackedString, etc

Concrete proposals for deciding what to remove

  1. Remove entities that clash with other common libraries, which would otherwise require import Prelude hiding. For example, a function called catch is also exported by Control.Exception, take/drop are also exported by Data.Sequence. A list of these can easily be constructed.
  2. Remove entities that are used in fewer than N% of modules (measure as much code as we can get our hands on, choose some appropriate N).
  3. Decide what to keep.
  4. Remove the implicit import of the Prelude in its entirety. Where a syntactic desugaring rule currently uses an entity from the Prelude, the new interpretation is that it uses whatever binding of that entity is in scope - if there is no such entity in scope, it is an error. For compatibility, the current Prelude can continue to be made available as an explicit import, so that the only change required to fix existing code is import Prelude. Indeed, an implementation may choose to provide a compile-time option like -fimplicit-prelude, the inversion of ghc's -fno-implicit-prelude, to avoid the need to change old code at all.

For the record I (Simon M.) support doing at the very least (1), and I'm interested in measuring code along the lines of (2) so that we can find out what things are hardly ever used. (3) is rather less conservative. (4) is Malcolm's idea.


At the moment this is an idea for a proposal, rather than a proposal. It needs to be made concrete before we can act on it.

On the other hand, any firm proposal in this area would cost a significant amount of work. Some indication of general positivity or negativity towards the idea would help us know whether it is worth expending effort on devising some definite proposals.

Data points

Language support

The following names are used by the language definition:

Bool (True, False), Char, String, Integer, Rational, Ratio, IO,
Eq ((==)), Ord ((>=)),
Enum (enumFrom, enumFromThen, enumFromTo, enumFromThenTo),
Num (fromInteger, negate, (-)), Fractional (fromRational),
Monad ((>>=), (>>), fail),
undefined, error, (%), concatMap

Defaulting uses Double and all classes defined in standard libraries.

Derived instances use the classes Eq, Ord, Enum, Bounded, Show and Read, which refer to Ordering, Int, ShowS and ReadS.

The ForeignFunctionInterface uses

Char, Int, Double, Float, Bool,
Int8, Int16, Int32, Int64, Word8, Word16, Word32, Word64,
Ptr, FunPtr, StablePtr


The following Prelude functions are re-exported by other Haskell 98 modules:

map, (++), concat, filter, head, last, tail, init, null, length, (!!), foldl, foldl1, scanl, scanl1, foldr, foldr1, scanr, scanr1, iterate, repeat, replicate, cycle, take, drop, splitAt, takeWhile, dropWhile, span, break, lines, words, unlines, unwords, reverse, and, or, any, all, elem, notElem, lookup, sum, product, maximum, minimum, concatMap, zip, zip3, zipWith, zipWith3, unzip, unzip3
ioError, userError, catch, interact, putChar, putStr, putStrLn, print, getChar, getLine, getContents, readFile, writeFile, appendFile, readIO, readLn
mapM, mapM_, sequence, sequence_, (=<<)

Concrete proposal for a minimal Prelude

Here is a concrete proposal for a truly minimal Prelude. Let the Prelude itself contain only entities that relate purely to functions - no other datatypes.

module Prelude
    ( (->)
    , (.)
    , ($)
    , ($!)
    , flip
    , id
    , const
    , error
    , undefined
    , seq
    , asTypeOf

Everything else that is currently in the Haskell'98 Prelude is re-distributed across a variety of small modules. Where a syntactic desugaring rule currently uses an entity from the Prelude, the new interpretation is that it uses whatever binding of that entity is in scope - if there is no such entity in scope, it is an error. For compatibility, we define a wrapper module called Prelude.Standard which re-exports the original Haskell'98 Prelude:

module Prelude.Standard
    ( module Prelude
    , module Prelude.Num
    , module Prelude.Comparison
    , module Prelude.Monad
    , module Prelude.List
    , module Prelude.Maybe
    , module Prelude.Either
    , module Prelude.Tuple
    , module Prelude.IO
    , module Prelude.Text

And here are the individual fragments:

module Prelude.Num
    ( data Natural(..)
    , data Int(..)
    , data Int8(..)
    , data Int16(..)
    , data Int32(..)
    , data Int64(..)
    , data Word8(..)
    , data Word16(..)
    , data Word32(..)
    , data Word64(..)
    , data Integer(..)
    , data Float(..)
    , data Double(..)
    , type Rational
    , class Integral(..)
    , class Num(..)
    , class Fractional(..)
    , class Real(..)
    , class RealFrac(..)
    , class Floating(..)
    , class RealFloat(..)
    , gcd, lcm
    , fromIntegral, realToFrac
    , numericEnumFrom, numericEnumFromTo, numericEnumFromThen
    , numericEnumFromThenTo
    , (^), (^^), (%)
    , even, odd, subtract

module Prelude.Comparison
    ( data Bool(..)
    , data Ordering(..)
    , class Eq(..)
    , class Ord(..)
    , class Enum(..)
    , class Bounded(..)
    , otherwise
    , (&&), (||), not, until

module Prelude.Monad
    ( class Functor(..)
    , class Monad(..)
    , mapM, mapM_, sequence, sequence_, (=<<)

module Prelude.List
    ( data [](..)
    , all, and, any, (++), break, concat, concatMap, cycle, drop, dropWhile
    , elem, filter, foldl, foldl1, foldr, foldr1, head, (!!), init, iterate
    , last, length, lines, lookup, map, maximum, minimum, notElem, null
    , or, product, repeat, replicate, reverse, scanl, scanl1, scanr, scanr1
    , span, splitAt, sum, tail, take, takeWhile, unlines, unwords, words

module Prelude.Maybe
    ( data Maybe(..)
    , maybe

module Prelude.Either
    ( data Either(..)
    , either

module Prelude.Tuple
    ( data ()(..)
    , data (,)(..)
    , data (,,)(..)
    , data (,,,)(..)
    , data (,,,,)(..)
    , data (,,,,,)(..)
    , data (,,,,,,)(..)
    , data (,,,,,,,)(..)
    , data (,,,,,,,,)(..)
    , data (,,,,,,,,,)(..)
    , data (,,,,,,,,,,)(..)
    , data (,,,,,,,,,,,)(..)
    , data (,,,,,,,,,,,,)(..)
    , data (,,,,,,,,,,,,,)(..)
    , data (,,,,,,,,,,,,,,)(..)
    , fst, snd
    , unzip, unzip3, zip, zip3, zipWith, zipWith3
    , curry, uncurry

module Prelude.IO
    ( data IO
    , data IOError(..)
    , data FilePath
    , ioError, userError, catch
    , print
    , putChar, putStr, putStrLn
    , getChar, getLine, getContents, interact
    , readFile, writeFile, appendFile, readIO, readLn

module Prelude.Text
    ( data Char(..)
    , type String
    , class Read(..)
    , class Show(..)
    , type ReadS
    , type ShowS
    , read, reads, readParen, lex
    , shows, showString, showParen, showChar