Simplification pattern via sequence of possibly effective reduction rules

I have some Scala code which I translated from another language which just doesn’t seem very idiomatic in Scala. The code works, so I’m hesitant to refactor it unless there’s a significant increase in readability, maintainability, or performance …

Basically I have a class hierarchy, and each class has a cannonicalizeOnce method. Each method iterates over a set of hard-coded 0-arity functions, each of which attempts to canonicalize the object, by a set of reduction/simplification rules which might result in a simpler object or might result in a copy of the object or exact same object.

The loop which iterates over this list of functions detects when a different object has been generated and terminates the iteration. If the entire loop finishes its iteration without generating a new object, then the method is called on the superclass via super.canonicalizeOnce(...). This final step is handled as a bit of an ugly hack, i.e., the last element of the list of functions is in fact () => super.canonicalizeOnce(...).

If the call-site returns the object, this, then it is assumed to be in canonical form. Otherwise, the function is called again until a fixed point is found. Actually the call site is simply a call to a find-fixed-point function.

The important issue is that every class might have its own set of reduction rules which should be attempted in addition to those reduction rules of the less specific class.

The beauty and the ugliness of the algorithm is that each of the 0-ary functions simply returns the given element to indicate that the search should continue. This is beautiful because each individual simplification need not test to see whether it has computed the same object, it just blindly returns the newly computed object; if that happens to be the same, then the process continues. This is ugly because if the 0-ary function explicitly decides that its simplification conditions are not met, then it must explicitly return this to signal to the caller to continue the search, which admittedly is not a very declarative signal.

Does this protocol sound like a well known pattern? Could I refactor it to conform to that pattern to make the code easier for the next person to read and understand, or to make the code easier to extend?