Scrap your boilerplate ... in Haskell

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.


Index


An example --- the paradise benchmark

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.


The 3 early boilerplate papers

Paper I: "Scrap your boilerplate: a practical design pattern for generic programming"
appeared in Proceedings of TLDI 2003, ACM Press

Paper II: "Scrap more boilerplate: reflection, zips, and generalised casts"
appeared in Proceedings of ICFP 2004, ACM Press

Paper III: "Scrap your boilerplate with class: extensible generic functions"
appeared in Proceedings of ICFP 2005, ACM Press

This is all joint work by Ralf Lämmel and Simon Peyton-Jones.

Download:


More boilerplate papers


A suite of examples

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.

FAQ --- frequently asked questions

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.

Useful links


Authors


maintained by Ralf Lämmel (Email: Ralf.Lammel@microsoft.com)