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.
Status
The first two SYB papers are fully supported by GHC >= 6.4.
The compiler has built-in support for "deriving (Typeable, Data)".
The SYB library is part of the normal "Data" module hierachy.
The third SYB paper provides its own experimental code base, with TH support.
To give you an idea, put your XML hat on, think of a BIG XML schema
for the organisational data structure of a company, and now implement
the paradise benchmark, say increase all salaries by a certain
amount. This is how you can approach to paradise in Haskell (with GHC
extensions):
-- 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.
For the examples, we always list a Haskell module, maybe some
datatypes, and maybe the intended output of the program -- if relevant
for the example at hand. Some minimal explanation of the purpose of
the example is normally found in the Haskell module. The examples
shown are contained like this in the GHC testsuite. If you want to get
your examples included, please contact the authors.
How does the boilerplate approach relate to Strafunski?
The boilerplate approach emerged from the style of functional
strategies as used in Strafunski. This can best
traced in "Strategic
polymorphism requires just two combinators", where it is shown how
strategies can be defined in terms of the primitives chosen for the
boilerplate approach. Strafunski as a distribution provides more than
a generic programming approach: it comprises themes, examples and
tools that support generic programming in the application domain of
language processing. Also, the strategic programming style favoured in
Strafunksi differs from the boilerplate approach in so far that
the former is based on an ADT for functional strategies whereas the latter
favours freewheeling higher-order functional programming.
How does the boilerplate approach relate to Generic Haskell?
This question is addressed in some detail in the boilerplate paper and in papers on Strafunski. In essence,
Generic Haskell provides a very general mechanism to define generic
functions by induction on the type structure on the basis of
designated constructs. By contrast, the boilerplate approach is pretty
much lightweight while focusing on term traversal as the prime idiom
of generic programming. Also, the boilerplate approach aims at a
smooth integration of generic programming with Haskell. Furthermore,
the boilerplate approach supports a combinator style of generic
programming.
Can the Data.Generics modules be used with hugs?
The available SYB implementations do not work with hugs.
For one thing, there is no "deriving (Typeable, Data)" support
in hugs. The SYB code base also uses some GHC extensions of Haskell 98
that are not readily available in hugs. Rank-2 types as such
are not a problem of course. Hugs supports them very well.
Making SYB available for hugs would require someone who is willing
to extend the hugs implementation and change the SYB library and
test harness in the GHC CVS. The SYB authors do currently not
engage in such a major effort.