Merge in Data.ByteString head. Fixes ByteString+cbits in hugs
[haskell-directory.git] / Data / Generics / Basics.hs
index 9be4b8a..84e5838 100644 (file)
@@ -6,86 +6,99 @@
 -- 
 -- Maintainer  :  libraries@haskell.org
 -- Stability   :  experimental
--- Portability :  non-portable
+-- Portability :  non-portable (local universal quantification)
 --
--- \"Scrap your boilerplate\" --- Generic programming in Haskell 
--- See <http://www.cs.vu.nl/boilerplate/>. The present module provides
--- the Data class with its primitives for generic programming.
+-- \"Scrap your boilerplate\" --- Generic programming in Haskell.
+-- See <http://www.cs.vu.nl/boilerplate/>. This module provides
+-- the 'Data' class with its primitives for generic programming.
 --
 -----------------------------------------------------------------------------
 
 module Data.Generics.Basics ( 
 
-       -- Module Data.Typeable re-exported for convenience
+       -- * Module Data.Typeable re-exported for convenience
        module Data.Typeable,
 
        -- * The Data class for processing constructor applications
        Data( 
                gfoldl,         -- :: ... -> a -> c a
+               gunfold,        -- :: ... -> Constr -> c a
                toConstr,       -- :: a -> Constr
-               fromConstr,     -- :: Constr -> a
                dataTypeOf,     -- :: a -> DataType
-               cast0to1,       -- mediate types and unary type constructors
-               cast0to2        -- mediate types and binary type constructors
+               dataCast1,      -- mediate types and unary type constructors
+               dataCast2,      -- mediate types and binary type constructors
+               -- Generic maps defined in terms of gfoldl 
+               gmapT,
+               gmapQ, 
+               gmapQl,
+               gmapQr,
+               gmapQi,
+               gmapM,
+               gmapMp,
+               gmapMo
             ),
 
-       -- * Datatype representations (incl. constructors)
+       -- * Datatype representations
+       DataType,       -- abstract, instance of: Show
+       -- ** Constructors
+       mkDataType,     -- :: String   -> [Constr] -> DataType
+       mkIntType,      -- :: String -> DataType
+       mkFloatType,    -- :: String -> DataType
+       mkStringType,   -- :: String -> DataType
+       mkNorepType,    -- :: String -> DataType
+       -- ** Observers
+       dataTypeName,   -- :: DataType -> String
+       DataRep(..),    -- instance of: Eq, Show
+       dataTypeRep,    -- :: DataType -> DataRep
+       -- ** Convenience functions
+       repConstr,      -- :: DataType -> ConstrRep -> Constr
+       isAlgType,      -- :: DataType -> Bool
+       dataTypeConstrs,-- :: DataType -> [Constr]
+       indexConstr,    -- :: DataType -> ConIndex -> Constr
+       maxConstrIndex, -- :: DataType -> ConIndex
+       isNorepType,    -- :: DataType -> Bool
+
+       -- * Data constructor representations
        Constr,         -- abstract, instance of: Eq, Show
-       PrimRep(..),    -- instance of: Eq, Show
        ConIndex,       -- alias for Int, start at 1
        Fixity(..),     -- instance of: Eq, Show
-       DataType,       -- abstract, instance of: Show
-       PrimCons(..),   -- instance of: Eq, Show
-
-       -- * Constructing datatype representations
-       mkDataConstr,   -- :: ConIndex -> String -> Fixity -> Constr
-        mkPrimConstr,   -- :: PrimRep -> Constr
-       mkDataType,     -- :: [Constr] -> DataType
-       mkPrimType,     -- :: Typeable a => PrimCons -> a -> DataType
-
-       -- * Observing datatype representations
-       dataTyCon,      -- :: DataType -> String
-       dataTyMod,      -- :: DataType -> String
-       isPrimType,     -- :: DataType -> Bool
-       dataCons,       -- :: DataType -> [Constr]
-       primCons,       -- :: DataType -> PrimCons
-       constrPrimRep,  -- :: Constr -> PrimRep
-       conString,      -- :: Constr -> String
-       conFixity,      -- :: Constr -> Fixity
-       conIndex,       -- :: Constr -> ConIndex
-       stringCon,      -- :: DataType -> String -> Maybe Constr
-       indexCon,       -- :: DataType -> ConIndex -> Constr
-       maxConIndex,    -- :: DataType -> ConIndex
-
-        -- * Generic maps defined in terms of gfoldl 
-       gmapT,
-        gmapQ, 
-        gmapQl,
-        gmapQr,
-        gmapQi,
-        gmapM,
-        gmapMp,
-        gmapMo,
+       -- ** Constructors
+       mkConstr,       -- :: DataType -> String -> Fixity -> Constr
+       mkIntConstr,    -- :: DataType -> Integer -> Constr
+       mkFloatConstr,  -- :: DataType -> Double  -> Constr
+       mkStringConstr, -- :: DataType -> String  -> Constr
+       -- ** Observers
+       constrType,     -- :: Constr   -> DataType
+       ConstrRep(..),  -- instance of: Eq, Show
+       constrRep,      -- :: Constr   -> ConstrRep
+       constrFields,   -- :: Constr   -> [String]
+       constrFixity,   -- :: Constr   -> Fixity
+       -- ** Convenience function: algebraic data types
+       constrIndex,    -- :: Constr   -> ConIndex
+       -- ** From strings to constructors and vice versa: all data types
+       showConstr,     -- :: Constr   -> String
+       readConstr,     -- :: DataType -> String -> Maybe Constr
+
+       -- * Convenience functions: take type constructors apart
+       tyconUQname,    -- :: String -> String
+       tyconModule,    -- :: String -> String
+
+       -- * Generic operations defined in terms of 'gunfold'
+        fromConstr,    -- :: Constr -> a
+        fromConstrB,   -- :: ... -> Constr -> a
+       fromConstrM     -- :: Monad m => ... -> Constr -> m a
 
   ) where
 
 
 ------------------------------------------------------------------------------
 
-#ifdef __HADDOCK__
-import Prelude
-#endif
+import Prelude -- necessary to get dependencies right
 
 import Data.Typeable
 import Data.Maybe
 import Control.Monad
-import Data.Int              -- So we can give Data instance for Int8, ...
-import Data.Word             -- So we can give Data instance for Word8, ...
 
-import GHC.Real( Ratio(..) ) -- So we can give Data instance for Ratio
--- import GHC.Ptr           -- So we can give Data instance for Ptr
--- import GHC.Stable        -- So we can give Data instance for StablePtr
-#include "Typeable.h"
 
 
 ------------------------------------------------------------------------------
@@ -94,67 +107,118 @@ import GHC.Real( Ratio(..) ) -- So we can give Data instance for Ratio
 --
 ------------------------------------------------------------------------------
 
-{- 
-
-The Data class comprehends a fundamental primitive "gfoldl" for
+{- |
+The 'Data' class comprehends a fundamental primitive 'gfoldl' for
 folding over constructor applications, say terms. This primitive can
-be instantiated in several ways to map over the immediate subterms of
-a term; see the "gmap" combinators later in this module. Indeed, a
-generic programmer does not necessarily need to use the ingenious
-gfoldl primitive but rather the intuitive "gmap" combinators. The
-"gfoldl" primitive is completed by means to query top-level
-constructors, to turn constructor representations into proper terms,
-and to list all possible datatype constructors. This completion
-allows us to serve generic programming scenarios like read, show,
-equality, term generation.
+be instantiated in several ways to map over the immediate subterms
+of a term; see the @gmap@ combinators later in this class.  Indeed, a
+generic programmer does not necessarily need to use the ingenious gfoldl
+primitive but rather the intuitive @gmap@ combinators.  The 'gfoldl'
+primitive is completed by means to query top-level constructors, to
+turn constructor representations into proper terms, and to list all
+possible datatype constructors.  This completion allows us to serve
+generic programming scenarios like read, show, equality, term generation.
+
+The combinators 'gmapT', 'gmapQ', 'gmapM', etc are all provided with
+default definitions in terms of 'gfoldl', leaving open the opportunity
+to provide datatype-specific definitions.
+(The inclusion of the @gmap@ combinators as members of class 'Data'
+allows the programmer or the compiler to derive specialised, and maybe
+more efficient code per datatype.  /Note/: 'gfoldl' is more higher-order
+than the @gmap@ combinators.  This is subject to ongoing benchmarking
+experiments.  It might turn out that the @gmap@ combinators will be
+moved out of the class 'Data'.)
+
+Conceptually, the definition of the @gmap@ combinators in terms of the
+primitive 'gfoldl' requires the identification of the 'gfoldl' function
+arguments.  Technically, we also need to identify the type constructor
+@c@ for the construction of the result type from the folded term type.
+
+In the definition of @gmapQ@/x/ combinators, we use phantom type
+constructors for the @c@ in the type of 'gfoldl' because the result type
+of a query does not involve the (polymorphic) type of the term argument.
+In the definition of 'gmapQl' we simply use the plain constant type
+constructor because 'gfoldl' is left-associative anyway and so it is
+readily suited to fold a left-associative binary operation over the
+immediate subterms.  In the definition of gmapQr, extra effort is
+needed. We use a higher-order accumulation trick to mediate between
+left-associative constructor application vs. right-associative binary
+operation (e.g., @(:)@).  When the query is meant to compute a value
+of type @r@, then the result type withing generic folding is @r -> r@.
+So the result of folding is a function to which we finally pass the
+right unit.
+
+With the @-fglasgow-exts@ option, GHC can generate instances of the
+'Data' class automatically.  For example, given the declaration
+
+> data T a b = C1 a b | C2 deriving (Typeable, Data)
+
+GHC will generate an instance that is equivalent to
+
+> instance (Data a, Data b) => Data (T a b) where
+>     gfoldl k z (C1 a b) = z C1 `k` a `k` b
+>     gfoldl k z C2       = z C2
+>
+>     gunfold k z c = case constrIndex c of
+>                         1 -> k (k (z C1))
+>                         2 -> z C2
+>
+>     toConstr (C1 _ _) = con_C1
+>     toConstr C2       = con_C2
+>
+>     dataTypeOf _ = ty_T
+>
+> con_C1 = mkConstr ty_T "C1" [] Prefix
+> con_C2 = mkConstr ty_T "C2" [] Prefix
+> ty_T   = mkDataType "Module.T" [con_C1, con_C2]
+
+This is suitable for datatypes that are exported transparently.
 
 -}
 
 class Typeable a => Data a where
 
-{-
-
-Folding constructor applications ("gfoldl")
-
-The combinator takes two arguments "f" and "z" to fold over a term
-"x".  The result type is defined in terms of "x" but variability is
-achieved by means of type constructor "c" for the construction of the
-actual result type. The purpose of the argument "z" is to define how
-the empty constructor application is folded. So "z" is like the
-neutral / start element for list folding. The purpose of the argument
-"f" is to define how the nonempty constructor application is
-folded. That is, "f" takes the folded "tail" of the constructor
-application and its head, i.e., an immediate subterm, and combines
-them in some way. See the Data instances in this file for an
-illustration of "gfoldl". Conclusion: the type of gfoldl is a
-headache, but operationally it is simple generalisation of a list
-fold.
-
--}
-
-  -- | Left-associative fold operation for constructor applications
+  -- | Left-associative fold operation for constructor applications.
+  --
+  -- The type of 'gfoldl' is a headache, but operationally it is a simple
+  -- generalisation of a list fold.
+  --
+  -- The default definition for 'gfoldl' is @'const' 'id'@, which is
+  -- suitable for abstract datatypes with no substructures.
   gfoldl  :: (forall a b. Data a => c (a -> b) -> a -> c b)
+               -- ^ defines how nonempty constructor applications are
+               -- folded.  It takes the folded tail of the constructor
+               -- application and its head, i.e., an immediate subterm,
+               -- and combines them in some way.
           -> (forall g. g -> c g)
-          -> a -> c a
+               -- ^ defines how the empty constructor application is
+               -- folded, like the neutral \/ start element for list
+               -- folding.
+          -> a
+               -- ^ structure to be folded.
+         -> c a
+               -- ^ result, with a type defined in terms of @a@, but
+               -- variability is achieved by means of type constructor
+               -- @c@ for the construction of the actual result type.
+
+  -- See the 'Data' instances in this file for an illustration of 'gfoldl'.
 
-  -- Default definition for gfoldl
-  -- which copes immediately with basic datatypes
-  --
   gfoldl _ z = z
 
+  -- | Unfolding constructor applications
+  gunfold :: (forall b r. Data b => c (b -> r) -> c r)
+          -> (forall r. r -> c r)
+          -> Constr
+          -> c a
+
   -- | Obtaining the constructor from a given datum.
   -- For proper terms, this is meant to be the top-level constructor.
   -- Primitive datatypes are here viewed as potentially infinite sets of
   -- values (i.e., constructors).
-  --
   toConstr   :: a -> Constr
 
 
-  -- | Building a term from a constructor
-  fromConstr   :: Constr -> a
-
-
-  -- | Provide access to list of all constructors
+  -- | The outer type constructor of the type
   dataTypeOf  :: a -> DataType
 
 
@@ -165,17 +229,27 @@ fold.
 --
 ------------------------------------------------------------------------------
 
-  -- | Mediate types and unary type constructors
-  cast0to1 :: Typeable1 t
-           => (forall a. Data a => c (t a))
-           -> Maybe (c a)
-  cast0to1 _ = Nothing
-
-  -- | Mediate types and binary type constructors
-  cast0to2 :: Typeable2 t
-           => (forall a b. (Data a, Data b) => c (t a b))
-           -> Maybe (c a)
-  cast0to2 _ = Nothing
+  -- | Mediate types and unary type constructors.
+  -- In 'Data' instances of the form @T a@, 'dataCast1' should be defined
+  -- as 'gcast1'.
+  --
+  -- The default definition is @'const' 'Nothing'@, which is appropriate
+  -- for non-unary type constructors.
+  dataCast1 :: Typeable1 t
+            => (forall a. Data a => c (t a))
+            -> Maybe (c a)
+  dataCast1 _ = Nothing
+
+  -- | Mediate types and binary type constructors.
+  -- In 'Data' instances of the form @T a b@, 'dataCast2' should be
+  -- defined as 'gcast2'.
+  --
+  -- The default definition is @'const' 'Nothing'@, which is appropriate
+  -- for non-binary type constructors.
+  dataCast2 :: Typeable2 t
+            => (forall a b. (Data a, Data b) => c (t a b))
+            -> Maybe (c a)
+  dataCast2 _ = Nothing
 
 
 
@@ -185,28 +259,12 @@ fold.
 --
 ------------------------------------------------------------------------------
 
-{-
-
-The combinators gmapT, gmapQ, gmapM, ... can all be defined in terms
-of gfoldl. We provide corresponding default definitions leaving open
-the opportunity to provide datatype-specific definitions.
-
-(The inclusion of the gmap combinators as members of class Data allows
-the programmer or the compiler to derive specialised, and maybe more
-efficient code per datatype. Note: gfoldl is more higher-order than
-the gmap combinators. This is subject to ongoing benchmarking
-experiments. It might turn out that the gmap combinators will be moved
-out of the class Data.)
-
-Conceptually, the definition of the gmap combinators in terms of the
-primitive gfoldl requires the identification of the gfoldl function
-arguments. Technically, we also need to identify the type constructor
-"c" for the construction of the result type from the folded term type.
-
--}
-
 
   -- | A generic transformation that maps over the immediate subterms
+  --
+  -- The default definition instantiates the type constructor @c@ in the
+  -- type of 'gfoldl' to an identity datatype constructor, using the
+  -- isomorphism pair as injection and projection.
   gmapT :: (forall b. Data b => b -> b) -> a -> a
 
   -- Use an identity datatype constructor ID (see below)
@@ -225,24 +283,6 @@ arguments. Technically, we also need to identify the type constructor
       k c x = CONST $ (unCONST c) `o` f x 
       z _   = CONST r
 
-{-
-
-In the definition of gmapQ? combinators, we use phantom type
-constructors for the "c" in the type of "gfoldl" because the result
-type of a query does not involve the (polymorphic) type of the term
-argument. In the definition of gmapQl we simply use the plain constant
-type constructor because gfoldl is left-associative anyway and so it
-is readily suited to fold a left-associative binary operation over the
-immediate subterms. In the definition of gmapQr, extra effort is
-needed. We use a higher-order accumulation trick to mediate between
-left-associative constructor application vs. right-associative binary
-operation (e.g., (:)). When the query is meant to compute a value of
-type r, then the result type withing generic folding is r -> r. So the
-result of folding is a function to which we finally pass the right
-unit.
-
--}
-
   -- | A generic query with a right-associative binary operator
   gmapQr :: (r' -> r -> r) -> r -> (forall a. Data a => a -> r') -> a -> r
   gmapQr o r f x = unQr (gfoldl k (const (Qr id)) x) r
@@ -264,6 +304,10 @@ unit.
 
 
   -- | A generic monadic transformation that maps over the immediate subterms
+  --
+  -- The default definition instantiates the type constructor @c@ in
+  -- the type of 'gfoldl' to the monad datatype constructor, defining
+  -- injection and projection using 'return' and '>>='.
   gmapM   :: Monad m => (forall a. Data a => a -> m a) -> a -> m a
 
   -- Use immediately the monad datatype constructor 
@@ -346,731 +390,373 @@ newtype Mp m x = Mp { unMp :: m (x, Bool) }
 
 ------------------------------------------------------------------------------
 --
---     Datatype and constructor representations
+--     Generic unfolding
 --
 ------------------------------------------------------------------------------
 
--- | Representation of datatypes.
---   A package of constructor representations with names of type and module.
---   The list of constructors could be an array, a balanced tree, or others.
---
-data DataType = DataType
-        { tycon    :: String,
-           tymod    :: String,
-          datacons :: DataCons
-         }
-
-              deriving Show
-
-
--- | Datatype constructors
-data DataCons = DataCons [Constr]
-              | PrimCons PrimCons
-
-              deriving Show
-
-
--- | Primitive constructors
-data PrimCons = PrimStringCons
-              | PrimIntCons
-              | PrimFloatCons
-
-              deriving (Eq, Show)
-
 
--- | Representation of constructors
-data Constr =
-             -- The prime case for algebraic datatypes
-             DataConstr ConIndex String Fixity
-
-             -- Provision for primitive types
-           | PrimConstr PrimRep
+-- | Build a term skeleton
+fromConstr :: Data a => Constr -> a
+fromConstr = fromConstrB undefined
 
-             -- Provision for function types
-           | FunConstr
-
-              deriving Show
 
-
--- | Primitive types
-data PrimRep 
-       = PrimStringRep String 
-        | PrimIntRep    Integer 
-       | PrimFloatRep  Double
-
-              deriving (Eq, Show)
-
-
--- | Select primitive representation
-constrPrimRep :: Constr -> PrimRep
-constrPrimRep (PrimConstr x) = x
-constrPrimRep _              = error "constrPrimRep"
-
-
--- 
--- Equality of datatype constructors via index.
--- Use designated equalities for primitive types.
--- 
-instance Eq Constr where
-  (DataConstr i1 _ _) == (DataConstr i2 _ _) = i1 == i2
-  (PrimConstr x)      == (PrimConstr y)      = x  == y
-  _                   == _                   = False
-
-
--- | Unique index for datatype constructors.
---   Textual order is respected. Starts at 1.
---
-type ConIndex = Int
+-- | Build a term and use a generic function for subterms
+fromConstrB :: Data a
+            => (forall a. Data a => a)
+            -> Constr
+            -> a
+fromConstrB f = unID . gunfold k z
+ where
+  k c = ID (unID c f)
+  z = ID
 
 
--- | Fixity of constructors
-data Fixity = Prefix
-            | Infix    -- Later: add associativity and precedence
-           deriving (Eq,Show)
+-- | Monadic variation on 'fromConstrB'
+fromConstrM :: (Monad m, Data a)
+            => (forall a. Data a => m a)
+            -> Constr
+            -> m a
+fromConstrM f = gunfold k z 
+ where
+  k c = do { c' <- c; b <- f; return (c' b) }
+  z = return
 
 
 
 ------------------------------------------------------------------------------
 --
---     Constructing representations
+--     Datatype and constructor representations
 --
 ------------------------------------------------------------------------------
 
 
--- | Make a datatype constructor
-mkDataConstr   :: ConIndex -> String -> Fixity -> Constr
---     ToDo: consider adding arity?
-mkDataConstr = DataConstr
-
-
--- | Make a constructor for primitive types
-mkPrimConstr :: PrimRep -> Constr
-mkPrimConstr = PrimConstr
-
-
--- | Make a package of constructor representations
-mkDataType :: Typeable a => [Constr] -> a -> DataType
-mkDataType cs x = DataType { tycon    = typeTyCon x
-                           , tymod    = typeMod x
-                           , datacons = DataCons cs }
-
-
--- | Make a datatype representation for a primitive type
-mkPrimType :: Typeable a => PrimCons -> a -> DataType
-mkPrimType pc x = DataType { tycon    = typeTyCon x
-                           , tymod    = typeMod x
-                           , datacons = PrimCons pc }
-
-
-------------------------------------------------------------------------------
 --
---     Observing representations
+-- | Representation of datatypes.
+-- A package of constructor representations with names of type and module.
 --
-------------------------------------------------------------------------------
-
-
--- | Gets the type constructor
-dataTyCon :: DataType -> String
-dataTyCon = tycon
-
-
--- | Gets the module
-dataTyMod :: DataType -> String
-dataTyMod = tymod
-
-
--- | Tests for primitive types
-isPrimType :: DataType -> Bool
-isPrimType dt = case datacons dt of
-                     (DataCons _) -> False
-                     _            -> True
-
-
--- | Gets datatype constructors in increasing order of indicies;
-dataCons :: DataType -> [Constr] 
-dataCons dt = case datacons dt of
-               (DataCons cs) -> cs
-               _             -> error "dataCons"
-
-
--- | Gets datatype constructors in increasing order of indicies;
-primCons :: DataType -> PrimCons
-primCons dt = case datacons dt of
-               (PrimCons pc) -> pc
-               _             -> error "primCons"
-
-
--- | Turn a constructor into a string
-conString :: Constr -> String
-conString (DataConstr _ str _) = str
-conString (PrimConstr (PrimStringRep x)) = x
-conString (PrimConstr (PrimIntRep x))    = show x
-conString (PrimConstr (PrimFloatRep x))  = show x
-conString FunConstr = "->"
-
-
--- | Determine fixity of a constructor;
---   undefined for primitive types.
-conFixity :: Constr -> Fixity
-conFixity (DataConstr _ _ fix) = fix
-conFixity _                    = undefined
-
+data DataType = DataType
+                       { tycon   :: String
+                       , datarep :: DataRep
+                       }
 
--- | Determine index of a constructor.
---   Undefined for primitive types.
-conIndex   :: Constr -> ConIndex
-conIndex (DataConstr idx _ _) = idx
-conIndex _                    = undefined
+              deriving Show
 
 
--- | Lookup a constructor via a string
-stringCon :: DataType -> String -> Maybe Constr
-stringCon dt str | not (isPrimType dt)
- = worker (dataCons dt)
-  where
-    worker []     = Nothing
-    worker (c:cs) =
-      case c of
-        (DataConstr _ str' _) -> if str == str'
-                                   then Just c
-                                   else worker cs
-
-       -- other forms of Constr not valid here
-        _ -> error "stringCon"
+-- | Representation of constructors
+data Constr = Constr
+                       { conrep    :: ConstrRep
+                       , constring :: String
+                       , confields :: [String] -- for AlgRep only
+                       , confixity :: Fixity   -- for AlgRep only
+                       , datatype  :: DataType
+                       }
 
-stringCon dt str | primCons dt == PrimStringCons =
-  Just $ mkPrimConstr (PrimStringRep str)
+instance Show Constr where
+ show = constring
 
-stringCon dt str | primCons dt == PrimIntCons =
-  Just $ mkPrimConstr (PrimIntRep (read str))
 
-stringCon dt str | primCons dt == PrimFloatCons =
-  Just $ mkPrimConstr (PrimFloatRep (read str))
+-- | Equality of constructors
+instance Eq Constr where
+  c == c' = constrRep c == constrRep c'
 
-stringCon _ _ = error "stringCon"
 
+-- | Public representation of datatypes
+data DataRep = AlgRep [Constr]
+             | IntRep
+            | FloatRep
+            | StringRep
+             | NoRep
 
--- | Lookup a constructor by its index;
----  not defined for primitive types.
-indexCon :: DataType -> ConIndex -> Constr
-indexCon dt idx = (dataCons dt) !! (idx-1)
+           deriving (Eq,Show)
+-- The list of constructors could be an array, a balanced tree, or others.
 
 
--- | Return maximum index;
----  not defined for primitive types.
-maxConIndex :: DataType -> ConIndex
-maxConIndex dt = length (dataCons dt)
+-- | Public representation of constructors
+data ConstrRep = AlgConstr    ConIndex
+               | IntConstr    Integer
+              | FloatConstr  Double
+              | StringConstr String
 
+              deriving (Eq,Show)
 
--- | Determine type constructor for a typeable
-typeTyCon :: Typeable a => a -> String
-typeTyCon = select         -- Drop module prefix
-          . typeString     -- Determine full string for type
- where
-  -- Drop *.*.*... before name
-  select :: String -> String
-  select x = let x' = dropWhile (not . (==) '.') x
-              in if x' == [] then x else select (tail x')
 
+-- | Unique index for datatype constructors,
+-- counting from 1 in the order they are given in the program text.
+type ConIndex = Int
 
--- | Determine module of a typeable
-typeMod :: Typeable a => a -> String
-typeMod = select         -- Take module prefix
-        . typeString     -- Determine full string for type
- where
-  -- Take *.*.*... before name
-  select :: String -> String
-  select x = let (a,b) = break ((==) '.') x
-              in if b == ""
-                  then b 
-                  else a++select' (tail b)
-    where
-     select' x = let x' = select x
-                  in if x' == "" then "" else ('.':x')
 
+-- | Fixity of constructors
+data Fixity = Prefix
+            | Infix    -- Later: add associativity and precedence
 
--- | Determine full string for type
-typeString :: Typeable a => a -> String
-typeString = tyconString   -- Turn into string
-           . typerepTyCon  -- Extract type constructor
-           . typeOf        -- Query type of term
+           deriving (Eq,Show)
 
 
 ------------------------------------------------------------------------------
 --
---     Instances of the Data class for Prelude types
---     We define top-level definitions for representations.
+--     Observers for datatype representations
 --
 ------------------------------------------------------------------------------
 
 
-falseConstr    = mkDataConstr 1 "False" Prefix
-trueConstr     = mkDataConstr 2 "True"  Prefix
-boolDataType x = mkDataType [falseConstr,trueConstr] x
-
-instance Data Bool where
-  toConstr False = falseConstr
-  toConstr True  = trueConstr
-  fromConstr c = case conIndex c of
-                   1 -> False
-                   2 -> True
-                   _ -> error "fromConstr"
-  dataTypeOf = boolDataType
-
-
-------------------------------------------------------------------------------
-
-
-instance Data Char where
-  toConstr x = mkPrimConstr (PrimStringRep [x])
-  fromConstr (PrimConstr (PrimStringRep [x])) = x
-  fromConstr _ = error "fromConstr"
-  dataTypeOf = mkPrimType PrimStringCons
-
-
-------------------------------------------------------------------------------
-
-
-instance Data Float where
-  toConstr x = mkPrimConstr (PrimFloatRep (realToFrac x))
-  fromConstr (PrimConstr (PrimFloatRep x)) = realToFrac x
-  fromConstr _ = error "fromConstr"
-  dataTypeOf = mkPrimType PrimFloatCons
-
-
-------------------------------------------------------------------------------
-
-
-instance Data Double where
-  toConstr x = mkPrimConstr (PrimFloatRep x)
-  fromConstr (PrimConstr (PrimFloatRep x)) = x
-  fromConstr _ = error "fromConstr"
-  dataTypeOf = mkPrimType PrimFloatCons
-
-
-------------------------------------------------------------------------------
+-- | Gets the type constructor including the module
+dataTypeName :: DataType -> String
+dataTypeName = tycon
 
 
-instance Data Int where
-  toConstr x = mkPrimConstr (PrimIntRep (fromIntegral x))
-  fromConstr (PrimConstr (PrimIntRep x)) = fromIntegral x
-  fromConstr _ = error "fromConstr"
-  dataTypeOf = mkPrimType PrimIntCons
 
+-- | Gets the public presentation of a datatype
+dataTypeRep :: DataType -> DataRep
+dataTypeRep = datarep
 
-------------------------------------------------------------------------------
 
+-- | Gets the datatype of a constructor
+constrType :: Constr -> DataType
+constrType = datatype
 
-instance Data Integer where
-  toConstr x = mkPrimConstr (PrimIntRep x)
-  fromConstr (PrimConstr (PrimIntRep x)) = x
-  fromConstr _ = error "fromConstr"
-  dataTypeOf = mkPrimType PrimIntCons
 
+-- | Gets the public presentation of constructors
+constrRep :: Constr -> ConstrRep
+constrRep = conrep
 
-------------------------------------------------------------------------------
 
+-- | Look up a constructor by its representation
+repConstr :: DataType -> ConstrRep -> Constr
+repConstr dt cr =
+      case (dataTypeRep dt, cr) of
+       (AlgRep cs, AlgConstr i)      -> cs !! (i-1)
+       (IntRep,    IntConstr i)      -> mkIntConstr dt i
+       (FloatRep,  FloatConstr f)    -> mkFloatConstr dt f
+       (StringRep, StringConstr str) -> mkStringConstr dt str
+       _ -> error "repConstr"
 
-instance Data Int8 where
-  toConstr x = mkPrimConstr (PrimIntRep (fromIntegral x))
-  fromConstr (PrimConstr (PrimIntRep x)) = fromIntegral x
-  fromConstr _ = error "fromConstr"
-  dataTypeOf = mkPrimType PrimIntCons
 
 
 ------------------------------------------------------------------------------
-
-
-instance Data Int16 where
-  toConstr x = mkPrimConstr (PrimIntRep (fromIntegral x))
-  fromConstr (PrimConstr (PrimIntRep x)) = fromIntegral x
-  fromConstr _ = error "fromConstr"
-  dataTypeOf = mkPrimType PrimIntCons
-
-
-------------------------------------------------------------------------------
-
-
-instance Data Int32 where
-  toConstr x = mkPrimConstr (PrimIntRep (fromIntegral x))
-  fromConstr (PrimConstr (PrimIntRep x)) = fromIntegral x
-  fromConstr _ = error "fromConstr"
-  dataTypeOf = mkPrimType PrimIntCons
-
-
+--
+--     Representations of algebraic data types
+--
 ------------------------------------------------------------------------------
 
 
-instance Data Int64 where
-  toConstr x = mkPrimConstr (PrimIntRep (fromIntegral x))
-  fromConstr (PrimConstr (PrimIntRep x)) = fromIntegral x
-  fromConstr _ = error "fromConstr"
-  dataTypeOf = mkPrimType PrimIntCons
+-- | Constructs an algebraic datatype
+mkDataType :: String -> [Constr] -> DataType
+mkDataType str cs = DataType
+                       { tycon   = str
+                       , datarep = AlgRep cs
+                       }
 
 
-------------------------------------------------------------------------------
-
+-- | Constructs a constructor
+mkConstr :: DataType -> String -> [String] -> Fixity -> Constr
+mkConstr dt str fields fix =
+       Constr
+               { conrep    = AlgConstr idx
+               , constring = str
+               , confields = fields
+               , confixity = fix
+               , datatype  = dt 
+               }
+  where
+    idx = head [ i | (c,i) <- dataTypeConstrs dt `zip` [1..],
+                     showConstr c == str ]
 
-instance Data Word8 where
-  toConstr x = mkPrimConstr (PrimIntRep (fromIntegral x))
-  fromConstr (PrimConstr (PrimIntRep x)) = fromIntegral x
-  fromConstr _ = error "fromConstr"
-  dataTypeOf = mkPrimType PrimIntCons
 
+-- | Gets the constructors of an algebraic datatype
+dataTypeConstrs :: DataType -> [Constr]
+dataTypeConstrs dt = case datarep dt of 
+                       (AlgRep cons) -> cons
+                       _ -> error "dataTypeConstrs"
 
-instance Data Word where
-  toConstr x = mkPrimConstr (PrimIntRep (fromIntegral x))
-  fromConstr (PrimConstr (PrimIntRep x)) = fromIntegral x
-  fromConstr _ = error "fromConstr"
-  dataTypeOf = mkPrimType PrimIntCons
 
+-- | Gets the field labels of a constructor
+constrFields :: Constr -> [String]
+constrFields = confields
 
-------------------------------------------------------------------------------
 
+-- | Gets the fixity of a constructor
+constrFixity :: Constr -> Fixity
+constrFixity = confixity
 
-instance Data Word16 where
-  toConstr x = mkPrimConstr (PrimIntRep (fromIntegral x))
-  fromConstr (PrimConstr (PrimIntRep x)) = fromIntegral x
-  fromConstr _ = error "fromConstr"
-  dataTypeOf = mkPrimType PrimIntCons
 
 
 ------------------------------------------------------------------------------
-
-
-instance Data Word32 where
-  toConstr x = mkPrimConstr (PrimIntRep (fromIntegral x))
-  fromConstr (PrimConstr (PrimIntRep x)) = fromIntegral x
-  fromConstr _ = error "fromConstr"
-  dataTypeOf = mkPrimType PrimIntCons
-
-
+--
+--     From strings to constr's and vice versa: all data types
+--     
 ------------------------------------------------------------------------------
 
 
-instance Data Word64 where
-  toConstr x = mkPrimConstr (PrimIntRep (fromIntegral x))
-  fromConstr (PrimConstr (PrimIntRep x)) = fromIntegral x
-  fromConstr _ = error "fromConstr"
-  dataTypeOf = mkPrimType PrimIntCons
-
-
-------------------------------------------------------------------------------
+-- | Gets the string for a constructor
+showConstr :: Constr -> String
+showConstr = constring
 
 
-ratioConstr    = mkDataConstr 1 ":%" Infix
-ratioDataType x = mkDataType [ratioConstr] x
+-- | Lookup a constructor via a string
+readConstr :: DataType -> String -> Maybe Constr
+readConstr dt str =
+      case dataTypeRep dt of
+       AlgRep cons -> idx cons
+       IntRep      -> mkReadCon (\i -> (mkPrimCon dt str (IntConstr i)))
+       FloatRep    -> mkReadCon (\f -> (mkPrimCon dt str (FloatConstr f)))
+       StringRep   -> Just (mkStringConstr dt str)
+        NoRep       -> Nothing
+  where
 
-instance (Data a, Integral a) => Data (Ratio a) where
-  toConstr _ = ratioConstr
-  fromConstr c | conIndex c == 1 = undefined :% undefined
-  fromConstr _ = error "fromConstr"
-  dataTypeOf = ratioDataType
+    -- Read a value and build a constructor
+    mkReadCon :: Read t => (t -> Constr) -> Maybe Constr
+    mkReadCon f = case (reads str) of
+                   [(t,"")] -> Just (f t)
+                   _ -> Nothing
 
+    -- Traverse list of algebraic datatype constructors
+    idx :: [Constr] -> Maybe Constr
+    idx cons = let fit = filter ((==) str . showConstr) cons 
+                in if fit == []
+                     then Nothing
+                     else Just (head fit)
 
 
 ------------------------------------------------------------------------------
-
-
-
-nilConstr      = mkDataConstr 1 "[]"  Prefix
-consConstr     = mkDataConstr 2 "(:)" Infix
-listDataType x = mkDataType [nilConstr,consConstr] x
-
-instance Data a => Data [a] where
-  gfoldl f z []     = z []
-  gfoldl f z (x:xs) = z (:) `f` x `f` xs
-  toConstr []    = nilConstr
-  toConstr (_:_) = consConstr
-  fromConstr c = case conIndex c of
-                   1 -> []
-                   2 -> undefined:undefined
-                   _ -> error "fromConstr"
-  dataTypeOf = listDataType
-  cast0to1   = cast1
-
 --
--- The gmaps are given as an illustration.
--- This shows that the gmaps for lists are different from list maps.
+--     Convenience funtions: algebraic data types
 --
-  gmapT  f   []     = []
-  gmapT  f   (x:xs) = (f x:f xs)
-  gmapQ  f   []     = []
-  gmapQ  f   (x:xs) = [f x,f xs]
-  gmapM  f   []     = return []
-  gmapM  f   (x:xs) = f x >>= \x' -> f xs >>= \xs' -> return (x':xs')
-
-
 ------------------------------------------------------------------------------
 
 
-nothingConstr   = mkDataConstr 1 "Nothing" Prefix
-justConstr      = mkDataConstr 2 "Just"    Prefix
-maybeDataType x = mkDataType [nothingConstr,justConstr] x
-
-instance Data a => Data (Maybe a) where
-  gfoldl f z Nothing  = z Nothing
-  gfoldl f z (Just x) = z Just `f` x
-  toConstr Nothing  = nothingConstr
-  toConstr (Just _) = justConstr
-  fromConstr c = case conIndex c of
-                   1 -> Nothing
-                   2 -> Just undefined
-                   _ -> error "fromConstr"
-  dataTypeOf = maybeDataType
-  cast0to1   = cast1
-
-
-------------------------------------------------------------------------------
+-- | Test for an algebraic type
+isAlgType :: DataType -> Bool
+isAlgType dt = case datarep dt of
+                (AlgRep _) -> True
+                _ -> False 
 
 
-ltConstr           = mkDataConstr 1 "LT" Prefix
-eqConstr           = mkDataConstr 2 "EQ" Prefix
-gtConstr           = mkDataConstr 3 "GT" Prefix
-orderingDataType x = mkDataType [ltConstr,eqConstr,gtConstr] x
+-- | Gets the constructor for an index (algebraic datatypes only)
+indexConstr :: DataType -> ConIndex -> Constr
+indexConstr dt idx = case datarep dt of
+                       (AlgRep cs) -> cs !! (idx-1)
+                       _           -> error "indexConstr"
 
-instance Data Ordering where
-  gfoldl f z LT  = z LT
-  gfoldl f z EQ  = z EQ
-  gfoldl f z GT  = z GT
-  toConstr LT  = ltConstr
-  toConstr EQ  = eqConstr
-  toConstr GT  = gtConstr
-  fromConstr c = case conIndex c of
-                   1 -> LT
-                   2 -> EQ
-                   3 -> GT
-                   _ -> error "fromConstr"
-  dataTypeOf = orderingDataType
 
-
-------------------------------------------------------------------------------
+-- | Gets the index of a constructor (algebraic datatypes only)
+constrIndex :: Constr -> ConIndex
+constrIndex con = case constrRep con of
+                    (AlgConstr idx) -> idx
+                   _ -> error "constrIndex"
 
 
-leftConstr       = mkDataConstr 1 "Left"  Prefix
-rightConstr      = mkDataConstr 2 "Right" Prefix
-eitherDataType x = mkDataType [leftConstr,rightConstr] x
+-- | Gets the maximum constructor index of an algebraic datatype
+maxConstrIndex :: DataType -> ConIndex
+maxConstrIndex dt = case dataTypeRep dt of
+                       AlgRep cs -> length cs
+                       _            -> error "maxConstrIndex"
 
-instance (Data a, Data b) => Data (Either a b) where
-  gfoldl f z (Left a)   = z Left  `f` a
-  gfoldl f z (Right a)  = z Right `f` a
-  toConstr (Left _)  = leftConstr
-  toConstr (Right _) = rightConstr
-  fromConstr c = case conIndex c of
-                   1 -> Left undefined
-                   2 -> Right undefined
-                   _ -> error "fromConstr"
-  dataTypeOf = eitherDataType
-  cast0to2   = cast2
 
 
 ------------------------------------------------------------------------------
-
-
 --
--- A last resort for functions
+--     Representation of primitive types
 --
-instance (Data a, Data b) => Data (a -> b) where
-  toConstr _   = FunConstr
-  fromConstr _ = error "fromConstr"
-  dataTypeOf   = error "dataTypeOf"
-  cast0to2     = cast2
-
-
 ------------------------------------------------------------------------------
 
 
-tuple0Constr     = mkDataConstr 1 "()" Prefix
-tuple0DataType x = mkDataType [tuple0Constr] x
+-- | Constructs the 'Int' type
+mkIntType :: String -> DataType
+mkIntType = mkPrimType IntRep
 
-instance Data () where
-  toConstr _ = tuple0Constr
-  fromConstr c | conIndex c == 1 = ()  
-  fromConstr _ = error "fromConstr"
-  dataTypeOf = tuple0DataType
 
+-- | Constructs the 'Float' type
+mkFloatType :: String -> DataType
+mkFloatType = mkPrimType FloatRep
 
-------------------------------------------------------------------------------
 
+-- | Constructs the 'String' type
+mkStringType :: String -> DataType
+mkStringType = mkPrimType StringRep
 
-tuple2Constr     = mkDataConstr 1 "(,)" Infix
-tuple2DataType x = mkDataType [tuple2Constr] x
 
-instance (Data a, Data b) => Data (a,b) where
-  gfoldl f z (a,b) = z (,) `f` a `f` b
-  toConstr _ = tuple2Constr
-  fromConstr c = case conIndex c of
-                   1 -> (undefined,undefined)
-                   _ -> error "fromConstr"
-  dataTypeOf = tuple2DataType
-  cast0to2   = cast2
+-- | Helper for 'mkIntType', 'mkFloatType', 'mkStringType'
+mkPrimType :: DataRep -> String -> DataType
+mkPrimType dr str = DataType
+                       { tycon   = str
+                       , datarep = dr
+                       }
 
 
-------------------------------------------------------------------------------
+-- Makes a constructor for primitive types
+mkPrimCon :: DataType -> String -> ConstrRep -> Constr
+mkPrimCon dt str cr = Constr 
+                       { datatype  = dt
+                       , conrep    = cr
+                       , constring = str
+                       , confields = error "constrFields"
+                       , confixity = error "constrFixity"
+                       }
 
 
-tuple3Constr     = mkDataConstr 1 "(,,)" Infix
-tuple3DataType x = mkDataType [tuple3Constr] x
+mkIntConstr :: DataType -> Integer -> Constr
+mkIntConstr dt i = case datarep dt of
+                 IntRep -> mkPrimCon dt (show i) (IntConstr i)
+                 _ -> error "mkIntConstr"
 
-instance (Data a, Data b, Data c) => Data (a,b,c) where
-  gfoldl f z (a,b,c) = z (,,) `f` a `f` b `f` c
-  toConstr _ = tuple3Constr
-  fromConstr c = case conIndex c of
-                   1 -> (undefined,undefined,undefined)
-                   _ -> error "fromConstr"
-  dataTypeOf = tuple3DataType
 
+mkFloatConstr :: DataType -> Double -> Constr
+mkFloatConstr dt f = case datarep dt of
+                   FloatRep -> mkPrimCon dt (show f) (FloatConstr f)
+                   _ -> error "mkFloatConstr"
 
-------------------------------------------------------------------------------
 
-
-tuple4Constr     = mkDataConstr 1 "(,,,)" Infix
-tuple4DataType x = mkDataType [tuple4Constr] x
-
-instance (Data a, Data b, Data c, Data d)
-         => Data (a,b,c,d) where
-  gfoldl f z (a,b,c,d) = z (,,,) `f` a `f` b `f` c `f` d
-  toConstr _ = tuple4Constr
-  fromConstr c = case conIndex c of
-                   1 -> (undefined,undefined,undefined,undefined)
-                   _ -> error "fromConstr"
-  dataTypeOf = tuple4DataType
-
-
-------------------------------------------------------------------------------
-
-
-tuple5Constr     = mkDataConstr 1 "(,,,,)" Infix
-tuple5DataType x = mkDataType [tuple5Constr] x
-
-instance (Data a, Data b, Data c, Data d, Data e)
-         => Data (a,b,c,d,e) where
-  gfoldl f z (a,b,c,d,e) = z (,,,,) `f` a `f` b `f` c `f` d `f` e
-  toConstr _ = tuple5Constr
-  fromConstr c = case conIndex c of
-                   1 -> (undefined,undefined,undefined,undefined,undefined)
-                   _ -> error "fromConstr"
-  dataTypeOf = tuple5DataType
-
-
-------------------------------------------------------------------------------
-
-
-tuple6Constr     = mkDataConstr 1 "(,,,,,)" Infix
-tuple6DataType x = mkDataType [tuple6Constr] x
-
-instance (Data a, Data b, Data c, Data d, Data e, Data f)
-         => Data (a,b,c,d,e,f) where
-  gfoldl f z (a,b,c,d,e,f') = z (,,,,,) `f` a `f` b `f` c `f` d `f` e `f` f'
-  toConstr _ = tuple6Constr
-  fromConstr c =
-    case conIndex c of
-           1 -> (undefined,undefined,undefined,undefined,undefined,undefined)
-           _ -> error "fromConstr"
-  dataTypeOf = tuple6DataType
-
-
-------------------------------------------------------------------------------
-
-
-tuple7Constr     = mkDataConstr 1 "(,,,,,,)" Infix
-tuple7DataType x = mkDataType [tuple7Constr] x
-
-instance (Data a, Data b, Data c, Data d, Data e, Data f, Data g)
-         => Data (a,b,c,d,e,f,g) where
-  gfoldl f z (a,b,c,d,e,f',g) =
-    z (,,,,,,) `f` a `f` b `f` c `f` d `f` e `f` f' `f` g
-  toConstr _ = tuple7Constr
-  fromConstr c = case conIndex c of
-   1 -> (undefined,undefined,undefined,undefined,undefined,undefined,undefined)
-   _ -> error "fromConstr"
-  dataTypeOf = tuple7DataType
-
-
-------------------------------------------------------------------------------
-
-
-instance Data TypeRep where
-  toConstr _   = error "toConstr"
-  fromConstr _ = error "fromConstr"
-  dataTypeOf   = error "dataTypeOf"
-
-
-------------------------------------------------------------------------------
-
-
-instance Data TyCon where
-  toConstr _   = error "toConstr"
-  fromConstr _ = error "fromConstr"
-  dataTypeOf   = error "dataTypeOf"
+mkStringConstr :: DataType -> String -> Constr
+mkStringConstr dt str = case datarep dt of
+                      StringRep -> mkPrimCon dt str (StringConstr str)
+                      _ -> error "mkStringConstr"
 
 
 ------------------------------------------------------------------------------
-
-
-INSTANCE_TYPEABLE0(DataType,dataTypeTc,"DataType")
-
-instance Data DataType where
-  toConstr _   = error "toConstr"
-  fromConstr _ = error "fromConstr"
-  dataTypeOf   = error "dataTypeOf"
-
-
-------------------------------------------------------------------------------
-
-
-INSTANCE_TYPEABLE0(DataCons,dataConsTc,"DataCons")
-
-instance Data DataCons where
-  toConstr _   = error "toConstr"
-  fromConstr _ = error "fromConstr"
-  dataTypeOf   = error "dataTypeOf"
-
-
+--
+--     Non-representations for non-presentable types
+--
 ------------------------------------------------------------------------------
 
 
-INSTANCE_TYPEABLE0(PrimCons,primConsTc,"PrimCons")
-
-instance Data PrimCons where
-  toConstr _   = error "toConstr"
-  fromConstr _ = error "fromConstr"
-  dataTypeOf   = error "dataTypeOf"
-
-
-------------------------------------------------------------------------------
+-- | Constructs a non-representation for a non-presentable type
+mkNorepType :: String -> DataType
+mkNorepType str = DataType
+                       { tycon   = str
+                       , datarep = NoRep
+                       }
 
 
-INSTANCE_TYPEABLE0(Constr,constrTc,"Constr")
+-- | Test for a non-representable type
+isNorepType :: DataType -> Bool
+isNorepType dt = case datarep dt of
+                  NoRep -> True
+                  _ -> False 
 
-instance Data Constr where
-  toConstr _   = error "toConstr"
-  fromConstr _ = error "fromConstr"
-  dataTypeOf   = error "dataTypeOf"
 
 
 ------------------------------------------------------------------------------
-
-
-INSTANCE_TYPEABLE0(PrimRep,primRepTc,"PrimRep")
-
-instance Data PrimRep where
-  toConstr _   = error "toConstr"
-  fromConstr _ = error "fromConstr"
-  dataTypeOf   = error "dataTypeOf"
-
-
+--
+--     Convenience for qualified type constructors
+--
 ------------------------------------------------------------------------------
 
 
-INSTANCE_TYPEABLE0(Fixity,fixityTc,"Fixity")
-
-instance Data Fixity where
-  toConstr _   = error "toConstr"
-  fromConstr _ = error "fromConstr"
-  dataTypeOf   = error "dataTypeOf"
-
-
-------------------------------------------------------------------------------
+-- | Gets the unqualified type constructor:
+-- drop *.*.*... before name
+--
+tyconUQname :: String -> String
+tyconUQname x = let x' = dropWhile (not . (==) '.') x
+                 in if x' == [] then x else tyconUQname (tail x')
+
+
+-- | Gets the module of a type constructor:
+-- take *.*.*... before name
+tyconModule :: String -> String
+tyconModule x = let (a,b) = break ((==) '.') x
+                 in if b == ""
+                      then b 
+                      else a ++ tyconModule' (tail b)
+  where
+    tyconModule' x = let x' = tyconModule x
+                      in if x' == "" then "" else ('.':x')