The new behaviour is to check that both sides of a dyadic operator have the same type. This means that multiplying time by a scalar is no longer possible, but it also means (due to the lack of checks after unification) that multiplying two lists is possible, or concatenating two integers. This needs to be fixed by adding another pass.
Instead of storing the Constr, which was messy, we now store a String (to allow comparison of constructor types during unification) and a function to reform the type at the end of the type checking.
This patch hides all the old typeOfExpression, typeOfName, typeOfVariable, etc, and unifies them into a single type-class with an "astTypeOf" function. The type-class is currently named Typed, but that can easily be changed (it's only explicitly referred to in the Types module). The patch is essentially the type-class with a giant find-and-replace on the other modules.
We now have three kinds of canned tree traversals, all of which are smart about
which types they're applied to: explicit-descent transformations,
implicit-descent transformations, and implicit-descent checks. I've only
provided depth-first application of the latter two, but we could do
breadth-first in the future if necessary.
It's redundant, since you can always compute them from the variable, and it
makes the code that deals with actuals rather cleaner.
On the other hand, it slightly complicates some of the tests, because any names
you use in an Actual need to be defined...
ErrorReport is of type (Maybe Meta, String), thereby adding an optional code position to error messages.
Die has been changed so that die and dieP are now implemented in terms of dieReport (:: ErrorReport -> m a). This involved changing less code than changing die to be of type ErrorReport -> m a. All that had to be changed directly was that Die instances now implement dieReport instead of die.
Any bits of code that "caught" errors has been changed so that it handles ErrorReport instead of String. This ErrorReport is eventually, in Main, passed to dieIO, which will soon be changed to read the file in and provide the context. Accordingly, MonadIO m has been added as a constraint to dieIO, and dieInternal has been changed to no longer use dieIO (because really we can't add the MonadIO constraint to dieInternal).
Various error messages have been changed. Notably, all instances of fail in ParseOccam have been changed to use die or, wherever possible, dieP. A similar thing has been done in EvalConstants and EvalLiterals.
The function showCode shows code as either occam or Rain depending on the frontend. This is then used by a formatCode function that acts similar to
printf, which makes it easy to format error messages that use showCode.