The Generic H SKELL User's Guide: The Generic H Skell Team Dæv Clarke Ralf Hinze Johan Jeuring Andres L Oh Jan de Wit
The Generic H SKELL User's Guide: The Generic H Skell Team Dæv Clarke Ralf Hinze Johan Jeuring Andres L Oh Jan de Wit
Utrecht University
P.O. Box 80.089
3508 TB Utrecht
The Netherlands
http://www.generic-haskell.org
A
The Generic H SKELL User’s Guide
A
The Generic H SKELL Team
Dæv Clarke
Ralf Hinze
Johan Jeuring
Andres Löh
Jan de Wit
November 1, 2001
A
1 What is Generic H SKELL? 4
1.1 Generic Programming . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
A
1.2 Generic H SKELL overview . . . . . . . . . . . . . . . . . . . . . . . . . 4
2 Installation 5
2.1 System requirements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2.2 Installing the binary distribution . . . . . . . . . . . . . . . . . . . . . . . 5
2.3 Building from source . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
2.4 Running gh . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
2.5 Compiling and running the generated code . . . . . . . . . . . . . . . . . 6
A
3 Generic H SKELL: The Language 7
3.1 Special Parentheses . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
3.2 Kind-indexed types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
3.3 Type-indexed values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
3.4 Generic application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
3.5 Generic abstraction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
3.6 Type-indexed types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
3.7 Generated function naming . . . . . . . . . . . . . . . . . . . . . . . . . . 11
3.8 Haskell compatibility . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
4 Library 12
4.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
4.2 Module Bounds . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
4.3 Module Collect . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
4.4 Module Compare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
4.5 Module Eq . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
4.6 Module Map . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
4.7 Module MapM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
4.8 Module ReadShow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
4.9 Module Reduce . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
4.10 Module Table . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
4.11 Module ZipWith . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
5 Future Work 16
2
6 Meta-information 17
6.1 Contact . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
6.2 Caveats . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
6.3 Known bugs and limitations . . . . . . . . . . . . . . . . . . . . . . . . . . 17
6.4 Change Log . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
6.5 Acknowledgements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
6.6 Copyright information . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
3
A
1 What is Generic H SKELL?
A
1.2 Generic H SKELL overview
A
Generic H SKELL extends Haskell with a number of features.
• type-indexed values are defined as a value indexed over the various Haskell type
constructors (unit, primitive types, sums, products, and user-defined type con-
structors). The resulting type-indexed value can be specialised to any type.
• kind-indexed types are types indexed over kinds, defined by giving a case for both
∗ and κ → κ0 . Instances are obtained by applying the kind-indexed type to a kind.
• generic definitions can be used by applying them to a type or kind. This is called
generic application. The result is a type or value, depending on which sort of
generic definition is applied.
• generic abstraction allows generic definitions to take a type parameter. Such a
definition can apply only to types of a single kind.
• type-indexed types are types which are indexed over the type constructors. These
can be used to give types to more involved generic values. The resulting type-
indexed types can be specialised to any type.
4
2 Installation
./configure --prefix=install_path
make install
This will result in the binary gh being installed in the directory ${prefix}/bin.
5
2.3 Building from source
Building from source requires exactly the same command sequence as installing the
binary distribution. The difference of course is the amount of work which subsequently
occurs.
2.4 Running gh
A
The Generic H SKELL compiler is called gh. There are essentially two modes for
running it.
If you call gh without supplying a file to process, you will be asked for a file name.
Specify an input file relative to your working directory, and the compiler will process
the file and generate an output file with the same basename as the input file, but the
extension .hs.
Alternatively, input files can be specified on the command line.
A number of command line options are also available:
The first level of verbosity (no -v flag) produces only error messages. The second level
(-v) in addition provides some diagnostic information and warnings. The third level
(-vv) in addition provides debugging information.
6
A
3 Generic H SKELL: The Language
A
The Generic H SKELL compiler implements a number of extensions to Haskell. These
are described briefly here. Further information can be found by consulting the literature
[2, 1, for example] and the examples included in the distribution (in examples/*.ghs).
A case is defined for both kind ∗ and for higher kinds k → l . To a certain degree
the k → l case is predetermined, depending on the number of arguments [2]. This is
exemplified by the k → l case in the following example.
Example The kind-indexed type for the generic map function is defined as:
type Map{[∗]} t1 t2 = t1 → t2
type Map{[k → l ]} t1 t2 = forall u v . Map{[k ]} u v → Map{[l ]} (t1 u) (t2 v )
Note that both cases have the same number of arguments and an equal number of
variables introduced by the forall.
7
3.3 Type-indexed values
A
Generic H SKELL introduces a new top-level declaration for type-indexed values. A
type-indexed value is defined using the following syntax.
hVarid i {| t :: k |} :: htypei
hVarid i {| hstypei |} . . . = . . .
where
hstypei ::= Unit | :+: | :*: | Fun | Con hvar i | Label hvar i | htyconi
Firstly, we must declare the type of the type-indexed value. This is generally an ex-
pression involving a kind- or type-indexed type. Then we provide its definition which is
indexed over the constructors (hstypei).
Corresponding to each hstypei is a regular Haskell data or type declaration. It is impor-
tant to know these so that the appropriate pattern can be employed in the appropriate
case of a generic definition.
These are defined in the file GHPrelude.hs which is automatically imported by the
generated code. Consult GHPrelude.hs for details of ConDescr and LabelDescr and for
other auxiliary functions.
Naturally, these identifiers should not be used in the remainder of a program in a way
that clashes with their use in generic definitions, following the usual scoping rules of
Haskell.
Example The type-indexed value for the generic map function is defined as:
map{|t :: k |} :: Map{[k ]} t t
map{|Unit|} Unit = Unit
map{|:+:|} mA mB (Inl a) = Inl (mA a)
map{|:+:|} mA mB (Inr b) = Inr (mB b)
map{|:*:|} mA mB (a:*:b) = mA a:*:mB b
map{|Con c|} m (Con d b) = Con d (m b)
8
map{|Label l |} m (Label l b) = Label l (m b)
map{|(→)|} = error "cannot map over function type"
map{|Int|} i = i
map{|Char |} c = c
Generics defined over htyconi Type-indexed values (and later types) can be defined
over (possibly user-defined) type constructors. This covers the case for Int and Char ,
as illustrated above. Additional cases such as the following are also possible (though in
this case superfluous):
Notice the call map{|List|} m as on the right-hand side of this definition. This is required
since List is a recursive type.
haexpi ::= . . .
| hVarid i{|htypei|}
hgtyconi ::= . . .
| hConid i{[hkind i]}
9
3.5 Generic abstraction
A type variable (of fixed kind) can be abstracted generically from an expression using a
kind of generic abstraction. Declarations take the following form:
Here t is a type variable of the given kind, where hkind i ranges over grounded kinds
(i.e., those without kind variables).
An example is the so-called categorical strength:
New constructors (hconi) must be introduced for each case of such a definition — each
case will be compiled into a newtype declaration.
A type-indexed type can be specialised to a type by supplying its type argument.
hgtyconi ::= . . .
| hConid i{|htypei|}
FMap can be used anywhere a type can by supplying FMap with its type parameter,
for example in the following:
10
The constructors introduced in the definition of FMap can be used in pattern matching.
lookup{|t :: k |} :: Lookup{[k ]} t
lookup{|Unit|} (FMU fm) Unit = fm
lookup{|:+:|} lA lB (FMP (fma, fmb)) (Inl a) = lA fma a
lookup{|:+:|} lA lB (FMP (fma, fmb)) (Inr b) = lB fmb b
lookup{|:*:|} lA lB (FMT fma) (a:*:b) = do {fmb ← lA fma a; lB fmb b }
lookup{|Con d |} l (FMC fm) (Con b) = l fm b
lookup{|Label d |} l (FML fm) (Label b) = l fm b
11
4 Library
4.1 Introduction
A
Provided with the Generic H SKELL system is a library of useful generic functions.
These are summarised below; for the details, consult the library itself (in subdirectory
lib). We give the types of the generic functions for kind ∗ and ∗ → ∗, and usually a
short description.
A
Naming conventions When generic functions defined in the Generic H SKELL library
have an equivalent in the Haskell Prelude or libraries, the name of the generic function
is prefixed with a ‘g’.
These are slight generalizations of the minBound and maxBound members of the Bounded
class. They have the property that for all types t
However, these functions are also defined for types for which Bounded is not derivable;
i.e. types which are not enumerations or simple product types (see [5, Appendix D]).
For kinds other than ∗, the first parameters are not used: they can be ⊥.
12
constructors{|t :: ∗|} :: [ConDescr ]
constructors{|t :: ∗ → ∗|} :: [ConDescr ] → [ConDescr ]
labelsOf returns a list of descriptions of labels in a value, or the empty list when the
current type constructor has no labels.
4.5 Module Eq
eq{|t :: ∗|} :: t → t → Bool
eq{|t :: ∗ → ∗|} :: (a → b → Bool ) → t a → t b → Bool
13
4.7 Module MapM
mapMl , mapMr {|t :: ∗|} :: (Functor m, Monad m) ⇒ t → m t
mapMl , mapMr {|t :: ∗ → ∗|} :: (Functor m, Monad m) ⇒ (a → m b) → t a → m (t b)
These are the generic versions of the monadic map mapM in the Prelude. mapMl
traverses a datastructure from left to right (just like mapM ) while mapMr does this from
right to left. The Monad in the context should also be an instance of class Functor , but
that’s usually not problematic.
The generic versions of show and read (in classes Show and Read ).
The extra argument of type Bool is used internally to specify whether field labels are to
be printed (and separated by commas). It should usually be False.
Since calling these functions is a bit cumbersome, specialisations are provided:
rreduce is a generic version of foldr (note the reversed order of the last two arguments!),
while lreduce is a generic foldl . See [1, section 5.4].
crush{|t :: ∗ → ∗|} :: (a → a → a) → a → t a → a
14
crush is an instance of lreduce with a slightly more familiar type.
The following functions are all defined in terms of the above functions, and most have
counterparts in the Haskell Prelude:
flatten collects all values of type a in a list, and comp composes all functions contained
in a datatype.
A generic version of zipWith, with the difference that in case the structures don’t match,
Nothing is returned.
These functions are more direct generalizations of zip and unzip respectively. They are
defined as instances of gzipWith and gunzipWith.
15
5 Future Work
In the future, we plan to continue our work on the compiler. Among the many possible
extensions and improvements, we are initially considering:
• support for POPL-style definitions
• full support for modules
• adding a type checker
• a view mechanism (i.e. implicit maps between data types); better support for
fixpoints
• improved support for type-indexed data types
• XML, generic traversals, . . .
A
As we have not yet decided what the next major release of the Generic H SKELL
compiler will look like, these topics are subject to change. Any input and feedback is
welcome!
16
6 Meta-information
6.1 Contact
A A
The Generic H SKELL Project For information regarding the Generic H SKELL
project send email to [email protected].
Mailing List A low volume mailing list exists. Currently it serves as a place for dis-
A
tributing information relevant to Generic H SKELL and for announcing our project
meetings. This is the appropriate forum for general language discussions and whatnot.
The address is [email protected]. To subscribe to the mailing
list send an email to [email protected].
6.2 Caveats
A
The Generic H SKELL compiler is a research prototype. Many of its features, especially
the more experimental ones, may change as we gain more experience and understanding.
It should be noted that the compiler does not perform type checking of the Generic
A A
H SKELL source language. Thus type errors in Generic H SKELL source will often
only be discovered when the generated Haskell source is compiled.
17
4. The constructor descriptors for user-defined data types that have infix constructors
with non-default fixity will be generated incorrectly with the default fixity.
5. Usage of the keyword forall in types is a bit tricky. There are places where it is
needed, and others where it may cause strange errors. This will be clarified in the
future. Let the examples guide you for now.
6. We have not yet decided how to deal with generic definitions which are declared
across module boundaries. Any behaviour that these may give is incidental and
should not be considered as definitive.
7. Type-indexed data types do not work well with modules. All specialisations have
to be generated in the module in which they are defined.
8. A datatype (or newtype) which has a parameter of higher kinded type that does
not appear in the right hand side of the definition produces a bug in the Haskell
code generated by gh. The datatype Y below exhibits the behaviour.
data X a = X a
data Y b = Y (X (Y X ))
Parameter b has kind ∗ → ∗, but it does not occur in Y (X (Y X )). We do not
imagine that such types are very common, so the bug will remain for now.
6.5 Acknowledgements
Thanks to Ralf Hinze for frown :-(, to Arthur Baars and Doaitse Swiestra for ag, and
to Simon Marlow and Sven Panne for the original Happy Haskell grammar.
18
Bibliography
[1] Ralf Hinze. Generic Programs and Proofs. 2000. Habilitationsschrift, Bonn Univer-
sity.
[2] Ralf Hinze. Polytypic values possess polykinded types. In Roland Backhouse and
José Nuno Oliveira, editors, Mathematics of Program Construction, volume 1837 of
LNCS, pages 2–27. Springer-Verlag, 2000.
[3] Ralf Hinze, Johan Jeuring, and Andres Löh. Type-indexed datatypes. Unpublished,
2001. http://www.cs.uu.nl/~johanj/publications/tidata.ps.
[5] Simon Peyton Jones, John Hughes (editors), et al. Haskell 98 — A non-strict, purely
functional language. Available from http://haskell.org, February 1999.
19