A plate is a record type that is parametrized by an applicative functor <code>f</code>. There is one field for each type in the mutually recursive structure we want to write generic functions for. Each field has type <code>A -> f A</code> where <code>A</code> is one of the data types.

A plate is a record type that is parametrized by an applicative functor <code>f</code>. There is one field for each type in the mutually recursive structure we want to write generic functions for. Each field has type <code>A -> f A</code> where <code>A</code> is one of the data types.

−

To use the Multiplate library we have to make <code>Plate</code> and instance of the <code>Multiplate</code> class. The instance requires that we write two functions: <code>multiplate</code> and <code>mkPlate</code>. Let's define each of these functions in turn.

+

To use the Multiplate library we have to make <code>Plate</code> an instance of the <code>Multiplate</code> class. The instance requires that we write two functions: <code>multiplate</code> and <code>mkPlate</code>. Let's define each of these functions in turn.

<pre>

<pre>

Line 159:

Line 160:

"x" := Con 2863

"x" := Con 2863

</pre>

</pre>

+

+

== Alternative Plates ==

+

+

Multiplate does not have to be used in the above way. Any structure can be made an instance of <code>Multiplate</code> as long as the following two Multiplate laws are satisfied:

A plate is a record type that is parametrized by an applicative functor f. There is one field for each type in the mutually recursive structure we want to write generic functions for. Each field has type A -> f A where A is one of the data types.

To use the Multiplate library we have to make Plate an instance of the Multiplate class. The instance requires that we write two functions: multiplate and mkPlate. Let's define each of these functions in turn.

> instance Multiplate Plate where

We have to write one piece of boilerplate code for multiplate. However, once this is implemented, no further boilerplate code need be written.
multiplate takes a Plate as a parameter. The idea is that for each expression in our language we will call this a function from this Plate parameter on the children of our expression and then combine the results.

Notice that when an expression has no children, as in the case of v in v := e, we simply use pure v.
pure is used to handle the default case in buildExpr, also have no subexpressions.

Next we have to define mkPlate. mkPlate is a function that builds a Plate given a generic builder function that produces values of type a -> f a. However these generic builder functions require a bit of help. The need to know what the projection function for the field that they are building is, so we pass that as a parameter to them.

> mkPlate build = Plate (build expr) (build decl)

That's it. Now we are ready to use out generic library to process our mutually recursive data structure without using any more boilerplate.

Suppose we we want to get a list of all variables used in an expression. To do this we would use preorderFold with the list monoid. The first step is to build a Plate that handles the cases we care about. What we can do is use the default purePlate which does nothing, and modify it to handle the cases we care about.

Now we can can build a plate that will get variables from all subexpressions and concatenate them together into one big list

variablesPlate = preorderFold getVariablesPlate

In a real program we would either put getVariablesPlate into variablesPlates's where clause or else simply inline
the definition.

variablesPlate is a record of functions that will give a list of variables for each type in our mutually recursive record. Say we have an Expr we want to apply this to.

e1 :: Expr
e1 = Let ("x" := Con 42) (Add (EVar "x") (EVar "x"))

We can project out the function for Expr's from our plate apply it to e1 and then unwrap the Constant wrapper. There is a little helper function, called foldFor, that will upgrade of projection function to remove the Constant wrapper for us.

Now we can can build a plate that will repeatedly apply this transformation from bottom up.

constFoldPlate = mapFamily doConstFold

Let's build an declaration to test.

d1 :: Decl
d1 = "x" := (Add (Mul (Con 42) (Con 68)) (Con 7))

We can project out the function for Decl's from our plate apply it to d1 and then unwrap the Identity wrapper. Again, there is a little helper function, called traverseFor, that will upgrade of projection function to remove the Identity wrapper for us.