The ParameterList Object

The ParameterList object is used in the processing of clauses, it was introduced to allow list variables and patterns that include list variables to be used on expressions that aren't monoids. It has two applications. First, it allows matching against any object that has a list of parameters. Second it allows putting a list of parameters back into any object.

In the section on Pattern Matching to Monoids we discuss how an expression like:

can be written as Add[Add[x, y], f/g, Add[z, w]] so that a pattern match with the pattern:

can be done directly.

The ParameterList object is introduced to allow pattern matching using list variables against expressions that aren't monoids. For example, suppose we want to write a clause to replace the first parameter of any object with zero. It would be natural to write:

and we would expect that this clause, when applied to Fn[x, y, z] would return Fn[0, y, z]. But if Fn is not a monoid, then we can't write Fn[x, y, z] = Fn[x, Fn[y, z]] and so it's not clear how the pattern match should proceed.

To solve this problem, we do the pattern matching in two steps. First we bind the variable "f" to "Fn", then we put the parameters of Fn into a ParameterList object and match that to another ParameterList object built from the parameters of the pattern. So we now need to match ParameterList[x, y, z] to ParameterList[a, b...]. Since the ParameterList object is a monoid, we can rewrite the expression as ParameterList[x, ParameterList[y, z]] and proceed as we have previously.

Now we just need to figure out how to handle the ParameterList object when we are building the expression on the right-hand side of the Assert object. Because we have introduced the ParameterList object during the binding process, a direct build of the right-hand side from the bindings would generate:

Fn[0, ParameterList[y, z]]

rather than Fn[0, y, z]. To get the pattern match and build process to behave the way we expect, when we build the expression on the right-hand side of an Assert clause, we basically need to do the reverse of what was done during the pattern matching, where we introduced ParameterList objects to make the pattern match possible. Here we want to "collapse" any ParameterList object that was created during the pattern matching and populate f with the parameters of b rather than with the ParameterList object itself.

It seems reasonable to expect that for any clause similar to:

and so on, that the right-hand side should generate the same expression that we started with. The implementation of the ParameterList object described here satisfies that requirement.

Note that the ParameterList object can be used to pull out the parameters of an object and drop them into an entirely different object, something like:

Note also that the handling of pattern matching and expression building defined here means that a clause to simplify addition like this:

behaves as expected. If a is bound to more than one parameter, it will already be an Add object (not a ParameterList object).

It’s clear from examples like this that we don’t want to bind a to a ParameterList object in general. We only want to introduce the ParameterList object during the pattern matching if it’s required in order to be able match the structure of the expression with the structure of the pattern.

Finally, only a ParameterList that is created by binding a variable in the function currently being executed will be merged into a target object when that object is built, this is illustrated by the two unit tests shown here:

In both cases, the variable c is bound to ParameterList[x, 1], but in the first test, the x and 1 are merged into h because the ParameterList is created in the pattern matching in the clause. In the second unit test, the x and 1 are not merged. This approach makes it possible to manipulate the ParameterList object.