The "Scrap your boilerplate" approach is a lightweight generic
programming approach for Haskell. The approach is supported in the GHC
>= 6.0 implementation of Haskell. Using this approach, you can write
generic functions such as traversal schemes (e.g.,
everywhere
and everything
), as well as
generic read, generic show and generic equality (i.e.,
gread
, gshow
, and geq
). This
approach is based on just a few primitives for type-safe cast and
processing constructor applications.
This page provides you with all the information about the boilerplate approach, with documentation, examples and others. If you want to get an idea, please have a look at the paradise benchmark, or just work through the index below to find what you are looking for.
Data.Generics | ("imports all modules") | [.html] | [.hs] |
Data.Typeable | ("class Typeable and type case") | [.html] | [.hs] |
Data.Generics.Basics | ("generic programming primitives") | [.html] | [.hs] |
Data.Generics.Instances | ("instances of class Data for Prelude types") | [.html] | [.hs] |
Data.Generics.Aliases | ("aliases for type case, generic types") | [.html] | [.hs] |
Data.Generics.Schemes | ("traversal schemes like everywhere") | [.html] | [.hs] |
Data.Generics.Text | ("generic read and show") | [.html] | [.hs] |
Data.Generics.Twins | ("twin traversals like generic equality") | [.html] | [.hs] |
-- Increase salary by percentage increase :: Float -> Company -> Company increase k = everywhere (mkT (incS k)) -- "interesting" code for increase incS :: Float -> Salary -> Salary incS k (S s) = S (s * (1+k)) |
That is, you write a function increase
that takes a
percentage k
and a company. You define this function to
apply a trivial worker incS
on salaries whenever
traversal hits on a salary. Your traversal will eventually look at all
nodes; this is achieved by the traversal scheme
everywhere
. To lift the worker incS
to the
generic level, you make a transformation from it using
mkT
. That's it. The two combinators
everywhere
and mkT
are defined in GHC's
library Data.Generics. The only
prerequisite for generic programming on your company datatypes is that
you add corresponding deriving
clauses to the datatype
declarations:
data Company = C [Dept] deriving (Eq, Show, Typeable, Data) data Dept = D Name Manager [Unit] deriving (Eq, Show, Typeable, Data) data Unit = PU Employee | DU Dept deriving (Eq, Show, Typeable, Data) data Employee = E Person Salary deriving (Eq, Show, Typeable, Data) data Person = P Name Address deriving (Eq, Show, Typeable, Data) data Salary = S Float deriving (Eq, Show, Typeable, Data) ... |
So you add deriving (Typeable, Data)
to each datatype
just in the same way as you normally trigger derivation of instances
for Eq
and Show
. The classes
Typeable
and Data
comprise members for
type-safe cast and processing constructor applications. These
are the boilerplate primitives for generic programming.
Abstract: We describe a design pattern for writing programs that traverse data structures built from rich mutually-recursive data types. Such programs often have a great deal of "boilerplate" code that simply walks the structure, hiding a small amount of "real" code that constitutes the reason for the traversal. Our technique allows most of this boilerplate to be written once and for all, or even generated mechanically, leaving the programmer free to concentrate on the important part of the algorithm. These generic programs are much more adaptive when faced with data structure evolution because they contain many fewer lines of type-specific code. Our approach is simple to understand, reasonably efficient, and it handles all the data types found in conventional functional programming languages. It makes essential use of rank-2 polymorphism, an extension found in some implementations of Haskell. Further it relies on a simple type-safe cast operator.
Paper II: "Scrap more boilerplate: reflection, zips, and generalised casts"
by Ralf
Lämmel and Simon Peyton-Jones,
to appear in Proceedings of ICFP 2004, ACM Press
Abstract: Writing boilerplate code is a royal pain. Generic programming promises to alleviate this pain by allowing the programmer to write a generic ``recipe'' for boilerplate code, and use that recipe in many places. In earlier work we introduced the ``Scrap your boilerplate'' approach to generic programming, which exploits Haskell's existing type-class mechanism to support generic transformations and queries. This paper completes the picture. We add a few extra ``introspective'' or ``reflective'' facilities, that together support a rich variety of serialisation and de-serialisation. We also show how to perform generic ``zips'', which at first appear to be somewhat tricky in our framework. Lastly, we generalise the ability to over-ride a generic function with a type-specific one. All of this can be supported in Haskell with independently-useful extensions: higher-rank types and type-safe cast. The GHC implementation of Haskell readily derives the required type classes for user-defined data types.
Download:
gread
)
gshow
)
geq
)
gread
, gshow
, geq
)
gshow
followed by gread
reproduces term")
gzip
)
showBin
and readBin
)
cast
from Data.Typeable
)
deriving
instances for the type classes
Typeable
and Data
comes readily with GHC, in
the case of hugs, it can be provided with DrIFT; see the "useful links" section for corresponding source
distributions. If you are interested in helping with making the
boilerplate approach work for hugs, please contact the authors.