Exercise Template Haskell
In this exercise, we will have a closer look at Template Haskell. The hierarchical libraries
that are distributed with GHC include functions for writing Template Haskell code. Have a look
at the modules Language.Haskell.TH
You may want to consult the notes on Template Haskell
and Section 7.6
from the GHC User's Guide.
To use Template Haskell code from the ghci interpreter, you need to use the flag
This exercise was inspired by research on automatically repairing ill-typed expressions. For some typical programming errors,
a more advanced compiler could offer advise to a programmer how to correct an expression with a type error. In fact, the
offers such a facility. You are asked to write a number of functions that
can be used to correct an expression (details are given below).
You are given two Haskell modules as a starting point.
- Correct.hs: contains the functions you have to define. Currently, these functions are set to
- Test.hs: contains a number of functions for testing your implementation. In some cases, the expected answer is provided. In the other cases, the test corresponds to an optional question. Note that you also have to make changes to this module because the compiler must be informed about which parts are meta-programs.
Submit your solution by sending me the two changed Haskell modules, and give short
answers to the questions posed. Good luck!
1. Removing an argument
The following expression contains a type error.
test0 = filter even () [1..10]
One possibility to get rid of the type error is removing the
from the application. We will use the following convention:
corresponds to the function of the application (
to its first argument (
), etc. Implement a function
that is given a number
(greater than or equal to
), such that it conceptually ignores the
(remove 2) filter even () [1..10]
should yield the same result as
filter even [1..10]
n = 2
write a (lambda) expression by hand that behaves as
- Why can't
remove be defined as a normal (non-Template) Haskell function?
remove n with
n = 0 is a special case, since it removes the function of an application rather than some argument. Give an intuition for the meaning of
- What happens if we choose
n = 5 for the example presented above (the function
filter is supplied only three parameters)?
2. Inserting an argument
In the following expression, the higher-order function
should be given an initial value.
This expression can be repaired by inserting a second parameter.
test2 = foldr ((:) . toUpper) "afp 2005"
We will use the same convention for assigning numbers to the parameters.
(insertHole 2) foldr ((:) . toUpper) "afp 2005"
should be equal to
foldr ((:) . toUpper) hole "afp 2005"
is the polymorphic "error function" predefined in the module
Optional part for 2:
More ideally, we pass an additional parameter to
, which is the value to be inserted. For instance,
(insert 2 ) foldr ((:) . toUpper) "afp 2005"
foldr ((:) . toUpper)  "afp 2005"
. Explain your solution.
3. Permuting arguments
In the next example, the arguments to
are supplied in the wrong order.
test4 = foldr 0 (+) [1..10]
A permutation can be represented by a value of type
. For instance, the permutation
swaps the first and the second
argument of a function. Give an implementation of
for permuting arguments.
- Explain the difference between
permute [0,2,1] and
- The value
[0,1,1] is not a valid permutation, but it can be passed to
permute. What kind of corrections can we make by passing invalid permutations?
4. Inserting parentheses
Take a look at the following expression.
test5 = length filter even [1..10]
A common mistake made in functional programming is to forget some parentheses. The expression given above can be fixed by inserting two parentheses,
length (filter even [1..10])
. This is the purpose of the function
that you have to write.
This function should be supplied two integers: the first indicates the location of the opening parenthesis, the second integer indicates how many
parameters are enclosed by the parentheses. We use the convention that location
corresponds to the position just before the function
(in our example,
corresponds to the position between the function and its first argument (in our example, between
, we thus need
parens 1 3
- What can you say about the values of the two integers that can be supplied to
parens? Are there special cases?
5. Removing parentheses (optional)
In a similar way, we would like to remove a pair of parentheses.
foldr ((++)  ["Template", "Meta", "Programming"])
Following our line of reasoning, we could fix the type error by using
unparens 1 3
. Can you give a definition for
6. Library for correcting expression
Template Haskell offers a facility to splice in declarations. Therefore, we could automatically bring the functions
, etcetera into the top-level scope of a module. These specialized functions can be used as any normal Haskell function.
Of course, we can make use of the general
function (see Step 2).
Bring (a limited number of) specialized functions for
into the scope.
Optional part for 6:
Do the same for the other correcting functions.
7. Extending the library (optional)
Of course, there are more mistakes resulting in a type error that can be fixed automatically.
Can you think of more (useful) correcting functions?