Package org.openquark.cal.compiler

Source Code of org.openquark.cal.compiler.TypeExpr

/*
* Copyright (c) 2007 BUSINESS OBJECTS SOFTWARE LIMITED
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
*     * Redistributions of source code must retain the above copyright notice,
*       this list of conditions and the following disclaimer.
*     * Redistributions in binary form must reproduce the above copyright
*       notice, this list of conditions and the following disclaimer in the
*       documentation and/or other materials provided with the distribution.
*     * Neither the name of Business Objects nor the names of its contributors
*       may be used to endorse or promote products derived from this software
*       without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/


/*
* TypeExpr.java
* Created: July 17, 2000
* By: Bo Ilic
*/

package org.openquark.cal.compiler;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;

import org.openquark.cal.internal.serialization.ModuleSerializationTags;
import org.openquark.cal.internal.serialization.RecordInputStream;
import org.openquark.cal.internal.serialization.RecordOutputStream;
import org.openquark.cal.internal.serialization.RecordInputStream.RecordHeaderInfo;
import org.openquark.cal.module.Cal.Core.CAL_Prelude;


/**
* The internal representation of type expressions. Some examples are (schematically)
* a -> b, Int, (Int, a -> Boolean).
* <p>
* From the point of view of client packages outside the compiler package, TypeExpr is an immutable class.
* <p>
* Replacing an instantiated type variable by its instantiation results in an equivalent type.
* <p>
* TypeExpr is not an immutable class, but its state transitions are limited.
* The only state transitions allowed are:
* <ol>
*   <li> pruning (replacing instantiated type variables by their instantiations. This preserves
*    the type equivalence class).
*   <li> instantiating an uninstantiated type variable to a type constructor or to a type variable
*        with more stringent type class constraints. This makes the type expression a specialization
*        of the original type expression.
* </ol>
* <p>
* As a consequence, type expressions whose types do not depend on type variables will always
* represent the same type.
* <p>
* Creation date: (July 17, 2000)
* @author Bo Ilic
*/
abstract public class TypeExpr {
  
    /**
     * Serialization schema for a record indicating an already visited type expression.  This
     * goes with the record tag: EXISTING_TYPE_EXPR
     */
    private static final int alreadyVisitedTypeExprSerializationSchema = 0;
   
    /**    
     * Serialization schema for a record indicating constant type expression.  i.e. a type expression
     * defined as a static final value.
     */
    private static final int staticConstantTypeExprSerializationSchema = 0;
   
    /** the main effect is to print type variables with addresses rather than using letters */
    static final boolean DEBUG_INFO = false;

    //Here are some useful static constants for some of the most common types. Also, note the
    //static helper functions in this class which help with creating types.
    //Warning: do not add parametric types as public constants, since they can be mutated via type variable instantiation. 
                       
    /** the type of the empty record {} */
    static final TypeExpr EMPTY_RECORD =
        new RecordType(RecordVar.NO_FIELDS, Collections.<FieldName, TypeExpr>emptyMap());
   
    /**
     * The array of possible record tags used in calls to {@link RecordInputStream#findRecord(short[])} by
     * the {@link #load(RecordInputStream, ModuleTypeInfo, Map, Map, CompilerMessageLogger)} method.
     */
    private static final short[] TYPE_EXPR_RECORD_TAGS = new short[]{
        ModuleSerializationTags.ALREADY_VISITED_TYPE_EXPR,
        ModuleSerializationTags.STATIC_CONSTANT_TYPE_EXPR,
        ModuleSerializationTags.TYPE_CONSTRUCTOR,
        ModuleSerializationTags.TYPE_VAR,
        ModuleSerializationTags.TYPE_VAR_WITH_INSTANCE,
        ModuleSerializationTags.RECORD_TYPE,
        ModuleSerializationTags.TYPE_APP
    };
   
    /**
     * Whether to parenthesize a type expression sometimes depends on the parent node in the type expression
     * tree. For example,
     * Int -> Char must be parenthesized in (Int -> Char) -> Boolean because it is the domain of a function type
     * and it itself is a function.
     * Either Int Char must be parenthesized in Maybe (Either Int Char) because its parent is a textual type
     * constructor (Maybe) and it itself is a positive arity textual type constructor.
     *
     * This class is a type-safe enum indicating the information passed from the parent node in the type expression
     * graph that enables this node to decide if it needs parentheses.
     *
     * @author Bo Ilic
     */
    final static class ParenthesizationInfo {
       
        private final String name;
       
        /** indicates that no parentheses are necessary for the root of the type expression. */
        static final ParenthesizationInfo NONE = new ParenthesizationInfo("no parentheses required");
       
        /**
         * indicates that the typeExpr is an argument of a textual form type constructor or type var.
         *
         * An example for the (rare) case of a textual type var is the  type string for
         * "Functor.map (Prelude.undefined :: Maybe Int -> Either Char String)"
         * which is "Cal.Core.Functor.Functor a => a (Cal.Core.Prelude.Maybe Cal.Core.Prelude.Int) -> a (Cal.Core.Prelude.Either Cal.Core.Prelude.Char Cal.Core.Prelude.String)"
         */
        static final ParenthesizationInfo ARG_OF_TEXTUAL_TYPE_CONS_OR_VAR = new ParenthesizationInfo("arg of textual type constructor or type var");
       
        /** indicates that the typeExpr is the domain of a function type constructor (->). */
        static final ParenthesizationInfo DOMAIN_OF_FUNCTION = new ParenthesizationInfo("domain of function");
       
        private ParenthesizationInfo(String name) {
            this.name = name;
        }
       
        /** {@inheritDoc} */
        @Override
        public String toString() {
            return name;
        }
    }       
    /**
     * @return TypeConsApp if this TypeExpr has a TypeConsApp at its root then the TypeConsApp
     *      is returned, otherwise null.
     */
    public final TypeConsApp rootTypeConsApp() {
       
        TypeExpr typeExpr = this.prune();
        return (typeExpr instanceof TypeConsApp) ? (TypeConsApp) typeExpr : null;
    }
   
    /**
     * @return RecordType if this TypeExpr has a TypeApp at its root then the TypeApp
     *      is returned, otherwise null.   
     */   
    public final TypeApp rootTypeApp() {
       
        TypeExpr typeExpr = this.prune();
        return (typeExpr instanceof TypeApp) ? (TypeApp) typeExpr : null;
    }   
   
    /**
     * @return TypeVar if this TypeExpr has a TypeVar at its root then the TypeVar
     *      is returned, otherwise null.   
     */
    public final TypeVar rootTypeVar() {
       
        TypeExpr typeExpr = this.prune();
        return (typeExpr instanceof TypeVar) ? (TypeVar) typeExpr : null;
    }
   
    /**
     * @return RecordType if this TypeExpr has a RecordType at its root then the RecordType
     *      is returned, otherwise null.   
     */   
    public final RecordType rootRecordType() {
       
        TypeExpr typeExpr = this.prune();
        return (typeExpr instanceof RecordType) ? (RecordType) typeExpr : null;
    }
         
    /**
     * Checks if typeExpr2 can be specialized to typeExpr1. There are no side effects.
     *
     * Creation date: (5/16/01 3:24:26 PM)
     * @param typeExpr1
     * @param typeExpr2
     * @param contextModuleTypeInfo context in which this pattern matching takes place. This is needed to know what class instances are in scope.
     * @return boolean returns True if typeExpr2 can be specialized to typeExpr1.
     */
    public static boolean canPatternMatch(TypeExpr typeExpr1, TypeExpr typeExpr2, ModuleTypeInfo contextModuleTypeInfo) {       

        //Note: we copy both type expressions simultaneously since there may be referentially
        //identical type variables in common.
        //So for example, (a, Int) and (Char, a) can't unify whereas (a, Int) and (Char, a') can.
        TypeExpr[] copiedTypes = TypeExpr.copyTypeExprs(new TypeExpr[] {typeExpr1, typeExpr2});
        TypeExpr typeExprCopy1 = copiedTypes[0];
        TypeExpr typeExprCopy2 = copiedTypes[1];
                     
        try {
            TypeExpr.patternMatch(typeExprCopy1, typeExprCopy2, contextModuleTypeInfo);

        } catch (TypeException te) {
            return false;
        }
       
        return true;            
    }     
   
    /**
     * Please see the comment for patternMatchTypePieces. Returns true if patternMatchTypePieces would return without an
     * exception.
     *
     * Note: there are no side effects on the arguments.
     *
     * @param types1
     * @param types2
     * @param contextModuleTypeInfo
     * @return boolean
     */
    public static boolean canPatternMatchPieces(TypeExpr[] types1, TypeExpr[] types2, ModuleTypeInfo contextModuleTypeInfo) {

        try {
            TypeExpr.patternMatchPieces (types1, types2, contextModuleTypeInfo);
        } catch (TypeException te) {
            return false;
        }
       
        return true;   
    }   
   
    /**
     * Checks if unification of 2 type expressions would succeed. There are no side effects.
     * This function properly handles shared type variables between typeExpr1 and typeExpr2.   
     * So for example, (a, Int) and (Char, a) can't unify whereas (a, Int) and (Char, a') can.
     *
     * @param typeExpr1
     * @param typeExpr2
     * @param contextModuleTypeInfo context in which this unification takes place. This is needed to know what class instances are in scope.
     * @return boolean returns True if unification of the 2 type expressions would succeed.
     */
    public static boolean canUnifyType(TypeExpr typeExpr1, TypeExpr typeExpr2, ModuleTypeInfo contextModuleTypeInfo) {     
       
        // Copy the type expressions and try to unify them.
        //Note: we copy both type expressions simultaneously since there may be referentially
        //identical type variables in common.
       
        TypeExpr[] copiedTypes = TypeExpr.copyTypeExprs(new TypeExpr[] {typeExpr1, typeExpr2});
        TypeExpr typeExprCopy1 = copiedTypes[0];
        TypeExpr typeExprCopy2 = copiedTypes[1];
       
        try {
            TypeExpr.unifyType(typeExprCopy1, typeExprCopy2, contextModuleTypeInfo);

        } catch (TypeException te) {
            return false;
        }
       
        return true;
    }
   
    /**
     * Please see the comment for unifyTypePieces. Returns true if unifyTypePieces would return without an
     * exception.
     *
     * Note: there are no side effects on the arguments.
     *
     * @param types1
     * @param types2
     * @param contextModuleTypeInfo
     * @return whether unifyTypePieces would return without an exception.
     */
    public static boolean canUnifyTypePieces(TypeExpr[] types1, TypeExpr[] types2, ModuleTypeInfo contextModuleTypeInfo) {

        try {
            TypeExpr.unifyTypePieces (types1, types2, contextModuleTypeInfo);
        } catch (TypeException te) {
            return false;
        }
       
        return true;   
    }   
   
    /**
     * Calculates the degree of closeness between the 2 types. There are no side effects.
     *
     * <P>
     * The type closeness is a measure of the closeness between the 2 argument types. A value of -1 means that the
     * types do not unify. A value >= 0 means that the type do unify. The larger the number, the closer
     * the 2 types are. This value should not be persisted and may change in the future. It is intended for use
     * by UI clients such as Intellicut in filtering the list of possible candidates to display in the pick list
     * of gems to those that are a closer match. This number is not scaled. What this means is that you can't
     * interpret a closeness of 3 as a having a fixed meaning. In other words, if you fix the first argument,
     * unifyType can be considered as a function from TypeExpr -> Int and the maximum and minimum values attained
     * by this function of one variable on its domain gives the interpretation of the closeness magnitude for
     * that particular first argument.
     *
     * <P>
     * The intuition behind the heuristic is that it represents how many "coincidences"
     * there are between two types.  So every time we encounter two things that are the
     * same without being totally uninstantiated type variables, we add a point.
     *
     * <P>
     * The current heuristic gives 1 point for each time it matches:
     * <ol>
     *   <li> a type constructor
     *   <li> a record type
     *   <li> a record field name that was present in both "original" types
     * </ol>
     *
     * <P>
     * to something other than an uninstantiated variable.
     *
     * <P>
     * There are a couple of subtleties here:
     * <ol>
     *    <li> Matching a constrained type variable to a type that meets the contraint
     *         is just as good as matching the type itself.  So for example the type
     *         closeness of "Int" to "Num a => a" is 1, just like the closeness of
     *         "Int" to "Int".
     *    <li> Variables are instantiated as the type closeness check proceeds.  For
     *         example, the type closeness of "a -> a" to "Int -> Int" is 2:
     *          <ul>
     *             <li> 1 point for matching "->" type constructor
     *             <li> 0 points for matching first a to first Int, since a is uninstantiated
     *             <li> 1 point for matching second a to second Int, because by that point a
     *                  has been instantiated to Int.
     *           </ul>
     *    <li> Field types (in records) are sometimes matched against intermediate types,
     *         but field names are only matched against original types.  For example,
     *         when calculating the closeness of "Eq a => a" to "{x :: Int, y :: Char}",
     *         the type closeness is 3:       
     *         First we replace "Eq a => a" with the intermediate type "(Eq b, Eq c) => {x :: b, y :: c}"
     *         <ul>
     *             <li> 1 point for matching record types
     *             <li> 1 point for matching type "Eq b => b" to Int
     *             <li> 1 point for matching type "Eq c => c" to Char
     *             <li> 0 points for matching field names (x and y), because the field names
     *                  were not present in both original types 
     *         </ul>
     * <ol>
     * <P>  
     * @param typeExpr1
     * @param typeExpr2
     * @param contextModuleTypeInfo context in which this closeness measure takes place. This is needed to know what class instances are in scope.
     * @return int
     */
    public static int getTypeCloseness(TypeExpr typeExpr1, TypeExpr typeExpr2, ModuleTypeInfo contextModuleTypeInfo) {
     
        //Copy the type expressions and try to unify them.
       
        //Note: we copy both arguments simultaneously since there may be referentially
        //identical type variables in typeExpr1 and typeExpr2.
        //So for example, (a, Int) and (Char, a) can't unify whereas (a, Int) and (Char, a') can.
        TypeExpr[] copiedTypes = TypeExpr.copyTypeExprs(new TypeExpr[] {typeExpr1, typeExpr2});
        TypeExpr typeExprCopy1 = copiedTypes[0];
        TypeExpr typeExprCopy2 = copiedTypes[1];
      
        try {
            return typeExprCopy1.unifyType(typeExprCopy2, contextModuleTypeInfo);

        } catch (TypeException te) {
            return -1;
        }  
    }
   
    /**
     * Returns true if searchTypeExpr appears somewhere in this TypeExpr tree.
     * Note: We are doing object/pointer equality here.
     * Creation date: (08/08/01 9:28:13 AM)
     * @return boolean
     * @param searchTypeExpr
     */
    public abstract boolean containsTypeExpr(TypeExpr searchTypeExpr);
   
    /**
     * Returns true if searchTypeVar appears somewhere in this TypeExpr tree.
     * Note: We are doing object/pointer equality here. 
     *    
     * For example, "a -> b" contains the uninstantiated type variable a.
     * We do not allow unifications where a type variable is specialized to a type expression
     * in which the variable itself occurs since this leads to infinite types.
     * There are many difficulties in dealing with infinite types and the Hindley-Milner typing
     * algorithm just disallows them.
     *
     * @return boolean
     * @param searchTypeVar
     */
    final boolean containsUninstantiatedTypeVar(TypeVar searchTypeVar) {
        if (searchTypeVar.getInstance() != null) {
            throw new IllegalArgumentException();
        }
                   
        return containsTypeExpr(searchTypeVar);
    }
   
    /**
     * Returns true if searchRecordVar appears somewhere in this TypeExpr tree.
     * Note: We are doing object/pointer equality here.
     *
     * @return boolean
     * @param searchRecordVar
     */
    abstract boolean containsRecordVar(RecordVar searchRecordVar);
             
    /**
     * A polymorphic type expression is one that includes an uninstantiated type variable
     * or record variable.
     * Examples of polymorphic types are:
     * a
     * {r}
     * r\field1 => {r | field1 :: Double}
     * Int -> Either a b
     * Examples of non-polymorphic types are:
     * Int
     * {field1 :: Double -> Maybe Char, field2 :: Int}
     * [(Int, Boolean)]
     * @return true if this type expression is polymorphic
     */
    public abstract boolean isPolymorphic();
   
    /**
     * Makes a copy of this type expression. Instantiating the copy won't affect the original
     * type expression.
     * Creation date: (7/30/01 10:10:48 AM)
     * @return TypeExpr
     */
    public final TypeExpr copyTypeExpr() {
        return CopyEnv.freshType(this, null);
    }
   
    /**
     * Make copies of the given type expressions, maintaining referential equality in the duplicated expressions.
     *   eg. if the types to copy are [a -> b, c -> a], this will return [a' -> b', c' -> a'].
     *
     * @param typesToCopy the type expressions to be copied
     * @return TypeExpr[] the copies of the type expressions.  Referential equality among the types to copy
     *   will be retained in the duplicated types.
     */
    public static TypeExpr[] copyTypeExprs(TypeExpr[] typesToCopy) {       
        return CopyEnv.freshTypePieces(typesToCopy, null);     
   
   
    private static CopyEnv.TypesPair copyTypesPair(TypeExpr[] types1, TypeExpr[] types2) {
        return CopyEnv.freshTypesPair(types1, types2, null);                     
    }
   
    /**
     * Get a reference to this type expression with a number of parameters hidden at the beginning.
     * For instance, if the type expression is a -> (b -> bool) -> (c -> d) -> e, calling this method with n == 2
     * drops a and (b -> bool), returning (c -> d) -> e.
     * Note that if n == 1, this is equivalent to getResultType().
     *
     * Creation date: (30/07/2001 12:00:01 PM)
     * @param n the number of parameters to suppress from the beginning of this type expression
     * @return TypeExpr the resulting type expression
     */
    public final TypeExpr dropFirstNArgs(int n) {
        TypeExpr typeExpr = this.prune();
        for (int i = 0; i < n; i++) {
            if (!(typeExpr instanceof TypeConsApp && ((TypeConsApp) typeExpr).getName().equals(CAL_Prelude.TypeConstructors.Function))) {
                throw new IllegalStateException("Programming Error");
            }
                      
            typeExpr = ((TypeConsApp) typeExpr).getArg(1).prune();
        }
        return typeExpr;
    }
   
    /**
     * Returns the TypeExpr of a subcomponent of the param typeExpr indicated by params componentNumber and dataConstructor.
     *
     * Eg: If we called 'getComponentTypeExpr( (Either Double Bool), 0, Left), we get back 'Double'.
     *     This is because the type of the Either type is Either a b, with data constructors Left a and Right b.
     *
     * @param typeExpr the type for which the component should be returned.
     * @param componentNumber the zero-based index of the component to return.
     * @param dataConstructor the data constructor whose component should be returned.
     * @return TypeExpr the component type expression.
     */
    public static TypeExpr getComponentTypeExpr(TypeExpr typeExpr, int componentNumber, DataConstructor dataConstructor) {

        // eg. for Left a, the type is (a -> Either a b), the type pieces are [a, Either a b]
        TypeExpr[] dataConstructorTypePieces = dataConstructor.getTypeExpr().getTypePieces();

        if (dataConstructorTypePieces.length <= componentNumber) {
            throw new IllegalArgumentException("TypeExpr.getComponentTypeExpr() - invalid component number: " +
                                               componentNumber + " for data constructor " + dataConstructor);
        }

        // This is the TypeExpr that we want (although it may have parts not instantiated properly yet).
        TypeExpr targetTypeExpr = dataConstructorTypePieces[componentNumber];

        // So, let's do a search, and instantiate where appropriate.
        TypeExpr[] actualTypeList = typeExpr.rootTypeConsApp().getArgs();
        TypeConsApp dataConstructorResultType = dataConstructorTypePieces[dataConstructorTypePieces.length - 1].rootTypeConsApp();
        TypeExpr[] searchList = dataConstructorResultType.getArgs();
       
        return instantiateWithCorrespondingTypeExpr(targetTypeExpr, searchList, actualTypeList);
    }
   
    /**
     * Helper method for getComponentTypeExpr().
     * Returns a TypeExpr modeled after targetTypeExpr with the TypeVars instantiated according to searchList and actualTypeList.
     * (If targetTypeExpr is an uninstantiated TypeVar, it will find it's match in the searchList, then, it will be set to the corresponding
     * TypeExpr in the actualTypeList)
     *
     * Eg: If we called instantiateWithCorrespondingTypeExpr( 'a', ['b', 'a', 'c'], [Double, Int, Bool] ), then "Int" will be returned.
     * Eg: If we called instantiateWithCorrespondingTypeExpr( Maybe 'a', ['b', 'a'], [Double, Int] ), then "Maybe Int" will be returned.
     *
     * Warning: this is a mutator.      
     *
     * @param targetTypeExpr
     * @param searchList
     * @param actualTypeList 
     * @return targetTypeExpr, potentially pruned
     */  
    private static TypeExpr instantiateWithCorrespondingTypeExpr(TypeExpr targetTypeExpr, TypeExpr[] searchList, TypeExpr[] actualTypeList) {

        // After pruning, the targetTypeExpr must either be an uninstantiated TypeVar (which means we have to search thru the list),
        // or it's a TypeConsApp (which means we'll have to check its arg list [if any]).
        targetTypeExpr = targetTypeExpr.prune();

        if (targetTypeExpr instanceof TypeVar) {
           
            TypeVar typeVar = (TypeVar)targetTypeExpr;
                      
            for (int i = 0, n = searchList.length; i < n; ++i) {
               
                if (typeVar.equals(searchList[i])) {
                    return actualTypeList[i];                                      
                }
            }                       

            throw new IllegalArgumentException("Error in instantiateWithCorrespondingTypeExpr method: Unable to find matching TypeExpr.");
           
        } else if (targetTypeExpr instanceof RecordType) {
           
            RecordType recordType = targetTypeExpr.rootRecordType();           
            Map<FieldName, TypeExpr> hasFieldsMap = recordType.getHasFieldsMap();                           
            for (final Map.Entry<FieldName, TypeExpr> entry : hasFieldsMap.entrySet()) {
                                   
                TypeExpr fieldType = entry.getValue();           
                instantiateWithCorrespondingTypeExpr(fieldType, searchList, actualTypeList);
            }           
           
            return recordType;
          
        } else if (targetTypeExpr instanceof TypeConsApp) {

            TypeConsApp typeConsApp = (TypeConsApp) targetTypeExpr;
                                  
            for (int i = 0, nArgs = typeConsApp.getNArgs(); i < nArgs; ++i) {
                typeConsApp.setArg(i, instantiateWithCorrespondingTypeExpr(typeConsApp.getArg(i), searchList, actualTypeList));
            }
           
            return typeConsApp;
           
        } else {
            throw new IllegalArgumentException("Unhandled TypeExpr class: " + targetTypeExpr.getClass());
        }
    }
   
    /**
     * Returns the set of generic, type-class constrained and uninstantiated polymorphic variables (i.e. either type variables or
     * record variables) that occur in this TypeExpr. This set has a well-defined ordering determined by the traversal
     * of the TypeExpr tree.
     * (As a technical point, record types are traversed first via they polymorphic record variable, then
     * by field name order of the "has fields". In other words, the ordering returned below is independent of the representation
     * of the record type as a sequence of extensions).
     * Note that record variables can have both type-class constraints and lacks constraints. This refers only to the type-class
     * constraints.
     *
     * @param nonGenericVars nonGenericVars list of type variables that are not generic. Can be null to indicate an empty list.
     * @return Set (PolymorphicVar Set, where each PolymorphicVar is either a TypeVar or RecordVar)
     */
    final Set<PolymorphicVar> getGenericClassConstrainedPolymorphicVars(NonGenericVars nonGenericVars) {
        //use a LinkedHashSet to ensure iteration order.       
        Set<PolymorphicVar> varSet = new LinkedHashSet<PolymorphicVar>();
        getGenericClassConstrainedPolymorphicVars(varSet, nonGenericVars);
        return varSet;
    }
   
   
    /**
     * A helper function that returns the TypeVar with the specified typeClassTypeVarName
     * @param typeClassTypeVarName
     * @return TypeVar will not be null (the method fails in an exception if the type variable cannot be found).
     */
    final TypeVar getTypeClassTypeVar(String typeClassTypeVarName) {
       
        if (typeClassTypeVarName == null) {
            throw new NullPointerException("typeClassTypeVarName cannot be null.");
        }
                    
        //todoBI can give a more efficient implementation.              
        for (final PolymorphicVar typeVar : getGenericClassConstrainedPolymorphicVars(null)) {
          
            if (typeClassTypeVarName.equals(typeVar.getPreferredName())) {
                return (TypeVar)typeVar;
            }
        }
       
        throw new IllegalArgumentException("type class type variable '" + typeClassTypeVarName + "' not found");
    }

    /**
     * Helper function for finding the set of generic, constrained and uninstantiated polymorphic variables (i.e. either type variables or
     * record variables) that occur in this TypeExpr. This set has a well-defined ordering determined by the traversal of
     * the TypeExpr tree.
     * (As a technical point, record types are traversed first via they polymorphic record variable, then
     * by field name order of the "has fields". In other words, the ordering returned below is independent of the representation
     * of the record type as a sequence of extensions).    
     * Note that record variables can have both type-class constraints and lacks constraints. This refers only to the type-class
     * constraints.
     *
     * @param varSet Set (PolymorphicVar Set, where each PolymorphicVar is either a TypeVar or RecordVar)
     * @param nonGenericVars nonGenericVars list of type variables that are not generic. Can be null to indicate an empty list.
     */
    abstract void getGenericClassConstrainedPolymorphicVars(Set<PolymorphicVar> varSet, NonGenericVars nonGenericVars);
   
    /**
     * Returns the set of uninstantiated TypeVars that occur in this TypeExpr. This set has a
     * well-defined ordering determined by the traversal of the TypeExpr tree.
     * However, the ordering is *not* invariant with respect to the representation of record types as extensions,
     * and so in effect the ordering should not be used for any critical purpose. (This is in contrast with
     * getGenenericClassConstrainedPolymorphicVars. An invariant order could be defined, but it is more expensive,
     * and we don't need it currently).
     * @return Set
     */
    final Set<TypeVar> getUninstantiatedTypeVars() {
        //use a LinkedHashSet to ensure iteration order.       
        Set<TypeVar> varSet = new LinkedHashSet<TypeVar>();
        getUninstantiatedTypeVars(varSet);
        return varSet;
    }

    /**
     * Helper function for finding the set of uninstantiated TypeVars that occur in this
     * TypeExpr. This set has a well-defined ordering determined by the traversal of the TypeExpr tree.
     * However, the ordering is *not* invariant with respect to the representation of record types as extensions,
     * and so in effect the ordering should not be used for any critical purpose. (This is in contrast with
     * getGenenericClassConstrainedPolymorphicVars. An invariant order could be defined, but it is more expensive,
     * and we don't need it currently).
     *
     * @param varSet Set set of the uninstantiated TypeVars in this TypeExpr.   
     */
    abstract void getUninstantiatedTypeVars(Set<TypeVar> varSet);
  
    /**
     * Returns the set of uninstantiated RecordVars that occur in this TypeExpr.  Don't depend on this
     * set being in any particular order.
     * @return Set
     */
    final Set<RecordVar> getUninstantiatedRecordVars() {
        Set<RecordVar> varSet = new HashSet<RecordVar>();
        getUninstantiatedRecordVars(varSet);
        return varSet;
    }
   
    /**
     * Helper function for finding the set of uninstantiated RecordVars that occur in this TypeExpr.
     * Don't rely on the order of this set.
     */
    abstract void getUninstantiatedRecordVars(Set<RecordVar> varSet);
   
    /**
     * Get the TypeExpr corresponding to a reference to a subtype in another typeExpr
     * eg. if this typeExpr == (a -> (Either a b) -> c), correspondingSuperType == (d -> (Either d e) -> f), typeToFind == a,
     * this will return d.
     * Creation date: (04/04/2002 5:03:00 PM)
     */
    public abstract TypeExpr getCorrespondingTypeExpr(TypeExpr correspondingSuperType, TypeExpr typeToFind);
   
    /**
     * Returns the number of top level fully saturated applications of the Prelude.Function
     * type constructor in this TypeExpr. Intuitively, this can be considered the arity of a
     * function having this as its type.
     *
     * For example, if this TypeExpr corresponds to (a -> b) -> Boolean -> (c -> d) then it returns 3.
     * This counts the arrows as shown: (a -> b)-#1#->Bool-#2#->(c-#3#->d). Note that the
     * -> operator is right associative so that the parentheses around c->d are not
     * necessary.
     *
     * Creation date: (9/13/00 1:55:49 PM)
     * @return int
     */
    abstract public int getArity();
   
    /**
     * Similar to getTypePieces, except that it just returns the result type. It is more
     * efficient to use this method if that is all you need.
     * Creation date: (4/23/01 4:09:38 PM)
     * @return TypeExpr
     */
    public final TypeExpr getResultType() {

        TypeExpr typeExpr = this.prune();

        if (typeExpr instanceof TypeConsApp) {

            TypeConsApp typeConsApp = (TypeConsApp) typeExpr;

            if (typeConsApp.getName().equals(CAL_Prelude.TypeConstructors.Function) && typeConsApp.getNArgs() == 2) {
               
                TypeExpr rhs = typeConsApp.getArg(1);
                return rhs.getResultType();
            }
           
        } else if (typeExpr instanceof TypeApp) {
           
            TypeApp typeApp = (TypeApp)typeExpr;
           
            //recognize the special forms:
           
            //(TypeApp (TypeConsApp Function te1) te2)
            //and
            //(TypeApp (TypeApp (TypeConsApp Function) te1) te2)    
           
            TypeExpr operatorType = typeApp.getOperatorType().prune();
           
            if (operatorType instanceof TypeConsApp) {
               
                //recognize the form (TypeApp (TypeConsApp Function e1) (e2))
               
                TypeConsApp typeConsApp = (TypeConsApp)operatorType;
               
                if (typeConsApp.getName().equals(CAL_Prelude.TypeConstructors.Function) && typeConsApp.getNArgs() == 1) {   
                    return typeApp.getOperandType().getResultType();             
                }    
               
            } else if (operatorType instanceof TypeApp) {
               
                //recognize the form (TypeApp (TypeApp (TypeConsApp Function) te1) te2)
               
                TypeApp nextTypeApp = (TypeApp)operatorType;
               
                TypeExpr nextOperatorType = nextTypeApp.getOperatorType().prune();
               
                if (nextOperatorType instanceof TypeConsApp) {
                   
                    TypeConsApp typeConsApp = (TypeConsApp)nextOperatorType;
                   
                    if (typeConsApp.getName().equals(CAL_Prelude.TypeConstructors.Function) && typeConsApp.getNArgs() == 0) {   
                        return typeApp.getOperandType().getResultType();           
                    }                  
                }
            }         
        }

        return typeExpr;
    }
   
    /**
     * Returns a TypeExpr that represents the input types of this gem.
     * If the gem has no inputs then null is returned.
     * @return TypeExpr the expression that represents the input types
     */
    public final TypeExpr getArgumentType() {
       
        TypeExpr pieces[] = getTypePieces();
       
        if (pieces.length == 1) {
            return null;
       
        } else if (pieces.length == 2) {
            return pieces[0];
       
        } else {
           
            TypeExpr arguments = pieces[pieces.length - 2];
           
            for (int i = pieces.length - 3; i >= 0; i--) {
                arguments = makeFunType(pieces[i], arguments);
            }
           
            return arguments;
        }
    }
               
    /**
     * Note that the list type must be fully saturated for this method to return true. i.e. "isListType (Prelude.List)" returns false. 
     * Creation date: (July 25, 2002)
     * @return boolean true if this type is of the form [a] for any type a.    
     */
    public final boolean isListType() { 
       
        TypeExpr typeExpr = this.prune();
       
        if (typeExpr instanceof TypeConsApp) {
           
            //recognize the form (TypeConsApp List te)
           
            TypeConsApp typeConsApp = (TypeConsApp)typeExpr;
            return typeConsApp.getNArgs() == 1 && typeConsApp.getName().equals(CAL_Prelude.TypeConstructors.List);
           
        } else if (typeExpr instanceof TypeApp) {
           
            //recognize the form (TypeApp (TypeConsApp List) te)
           
            TypeExpr operatorType = ((TypeApp)typeExpr).getOperatorType();
            if (operatorType instanceof TypeConsApp) {
               
                TypeConsApp typeConsApp = (TypeConsApp)operatorType;
                return typeConsApp.getNArgs() == 0 && typeConsApp.getName().equals(CAL_Prelude.TypeConstructors.List);
               
            }           
        }
       
        return false;     
    }
   
    /**  
     * Creation date: (July 25, 2002)
     * @return boolean true if this type is a tuple of dimension 2 or more. e.g. (Int, a), ([Int], [(a,b)], c)    
     */
    public final boolean isTupleType() {            
        return getTupleDimension() >= 2;      
    }

    /**
     * Note that a non fully saturated application of Prelude.Function is not considered to be a function type e.g.
     * calling this method on "Prelude.Function Int" returns false.
     * @return true if this type is of the form a -> b, for some types a and b. For example, a -> b, Int -> [String] -> Boolean, etc.)
     */
    public final boolean isFunctionType() { 
        //we use getArity() in order to correctly handle TypeApp forms
       
        return getArity() > 0;      
    }
   
    /**
     * @param typeConsName the name of the type constructor
     * @return true if this type expression, after pruning, is a list type and its elements,
     * after pruning, are type constructors with the given name.
     */
    public final boolean isListTypeOf(QualifiedName typeConsName) {
       
        //todoBI recognize types using the TypeApp form
        
        TypeConsApp typeConsApp = this.rootTypeConsApp();
        if (typeConsApp != null &&
            typeConsApp.hasRootTypeConstructor(CAL_Prelude.TypeConstructors.List) &&
            typeConsApp.getNArgs() == 1) {
               
            return typeConsApp.getArg(0).hasRootTypeConstructor(typeConsName);            
        }           
       
        return false;
    }
   
    /**
     * Returns true if this TypeExpr is a non-parametric type (e.g. such as Int, String or Ordering) and
     * its name is the same as typeConsName.
     *
     * It is faster to use TypeExpr.sameType(TypeExpr) if you have access to the other TypeExpr to compare with.
     *     
     * @param typeConsName
     * @return true if this TypeExpr is a non-parametric type with type constructor name as specified.
     */
    public final boolean isNonParametricType(QualifiedName typeConsName) {
        TypeConsApp typeConsApp = this.rootTypeConsApp();
        return typeConsApp != null &&
            typeConsApp.getNArgs() == 0 &&
            typeConsApp.getName().equals(typeConsName);      
    }
   
    /**
     * An enumeration type is a:
     * -non parameteric type (i.e. the type has 0 arity)
     * -not a foreign type
     * -there is at least one data constructor
     * -all data constructors have 0 arity
     * -it is not Prelude.Boolean
     * For example, Prelude.Ordering is an enumeration type.
     *     
     * @return true if the given type is an enumeration, according to the above definition.
     */
    public static boolean isEnumType(TypeConstructor typeCons) {
        if (typeCons.getTypeArity() > 0 ||
            typeCons.getForeignTypeInfo() != null ||
            typeCons.getNDataConstructors() == 0 ||
            typeCons.getName().equals(CAL_Prelude.TypeConstructors.Boolean)) {
            return false;
        }
              
        for (int i = 0, nDataCons = typeCons.getNDataConstructors(); i < nDataCons; ++i) {
            DataConstructor dc = typeCons.getNthDataConstructor(i);
            if (dc.getArity() > 0) {
                return false;
            }
        }
       
        return true;

    }
   
    /**
     * For example, it this TypeExpr is "Prelude.Maybe Prelude.Int", and typeConsName is "Prelude.Maybe",
     * then hasRootTypeConstructor would return true.
     *
     * @param typeConsName the name of the type constructor
     * @return true if this type expression, after pruning, has as its root a type constructor with the given name
     */
    public final boolean hasRootTypeConstructor(QualifiedName typeConsName) {
        TypeExpr typeExpr = this.prune();
        return typeExpr instanceof TypeConsApp &&
               ((TypeConsApp) typeExpr).getName().equals(typeConsName);
    }

    /**
     * Returns the tuple dimension of the current type, or -1 if the type is not a tuple.
     * A tuple is by definition a record type of the form {#1 :: t1, ..., #n :: tn} where n>=2,
     * there are no gaps in the ordinal fields and there are no textual fields.
     *
     * For example, for
     * {#1 :: Int, #2 :: {name = "Fred", age = 40}}
     * we return 2.
     *
     * For
     * Just {#1 :: Int, #2 :: {name = "Fred", age = 40}}
     * we return -1.
     *
     * @return int the number of components of the tuple type (n in the above notation)
     */
    public final int getTupleDimension() {
        TypeExpr typeExpr = this.prune();
       
        if (typeExpr instanceof RecordType) {
           
            RecordType recordType = (RecordType)typeExpr;
            RecordVar prunedRecordVar = recordType.getPrunedRecordVar();
            if (!prunedRecordVar.isNoFields()) {
                return -1;
            }
           
            //todoBI this can be computed more efficiently. Don't need to create the set to find its
            //maximal element.
            SortedSet<FieldName> hasFieldsSet = recordType.getHasFields();
            int nHasFields = hasFieldsSet.size();
           
            if (nHasFields <= 1) {
                return -1;
            }
           
            FieldName lastFieldName = hasFieldsSet.last();                  
            if (lastFieldName instanceof FieldName.Ordinal) {
                if (((FieldName.Ordinal)lastFieldName).getOrdinal() == nHasFields) {
                    return nHasFields;
                }
            }
        }
       
        return -1;
    }  
           
    /**
     * Breaks up a type into an array of types holding the type of each argument followed by the return type where we
     * are interpreting this TypeExpr to be the TypeExpr of a function (or functional agent). Of course, this interpretation can
     * always be made! This method is intended for use primarily by a UI client for colouring arguments and return types based
     * on type equality,
     *
     * For example, the type (a->b) -> Double -> b will return [a->b, Double, b].
     * Creation date: (11/23/00 12:47:26 PM)
     * @return TypeExpr[] an array holding the type of each argument followed by the return type
     */
    public final TypeExpr[] getTypePieces() {
        return getTypePiecesHelper(getArity());      
    }
   
    /**
     * In certain cases there are multiple options as to how to break up a type into argument types and
     * return type. Primarily, this is the case if a functional return type is needed.
     * For example, if the type is Int -> Int -> Boolean. Then getTypePieces() will return:
     * [Int, Int, Boolean]. However, we may want to consider this as a type expression for a function of 1
     * argument returning a function. Then getTypePieces(1) returns: [Int, Int->Boolean].
     *        
     * @param arity must be less than or equal to the type arity of this type expression
     * @return TypeExpr[] type pieces array of length arity + 1.
     */
    public final TypeExpr[] getTypePieces(int arity) {
              
        if (arity < 0 || arity > getArity()) {
            throw new IllegalArgumentException("arity arguments must be non-negative and less than or equal to TypeExpr.getArity()");
        }
       
        return getTypePiecesHelper(arity);
    }
   
    private final TypeExpr[] getTypePiecesHelper(int arity) {
   
        TypeExpr[] typePieces = new TypeExpr[arity + 1];

        TypeExpr typeExpr = this.prune();

        for (int i = 0; i < arity; ++i) {
           
            //will match one of the 3 functional forms for te1 -> te2, or will fail in an IllegalStateException.
           
            if (typeExpr instanceof TypeConsApp) {
               
                TypeConsApp typeConsApp = (TypeConsApp)typeExpr;
               
                if (!typeConsApp.getName().equals(CAL_Prelude.TypeConstructors.Function) || typeConsApp.getNArgs() != 2) {
                    throw new IllegalStateException();
                }
               
                typePieces[i] = typeConsApp.getArg(0).prune();
                typeExpr = typeConsApp.getArg(1).prune();
               
            } else if (typeExpr instanceof TypeApp) {
               
                TypeApp typeApp = (TypeApp)typeExpr;
               
                TypeExpr operatorType = typeApp.getOperatorType().prune();
                if (operatorType instanceof TypeConsApp) {
                   
                    //recognize the form (TypeApp (TypeConsApp Function e1) (e2))
                   
                    TypeConsApp typeConsApp = (TypeConsApp)operatorType;
                   
                    if (!typeConsApp.getName().equals(CAL_Prelude.TypeConstructors.Function) || typeConsApp.getNArgs() != 1) {
                        throw new IllegalStateException();
                    }
                   
                    typePieces[i] = typeConsApp.getArg(0).prune();
                    typeExpr = typeApp.getOperandType().prune();
                   
                } else if (operatorType instanceof TypeApp) {
                                       
                   
                    TypeApp nextTypeApp = (TypeApp)operatorType;
                   
                    TypeExpr nextOperatorType = nextTypeApp.getOperatorType().prune();
                   
                    if (nextOperatorType instanceof TypeConsApp) {
                       
                        // recognize the form (TypeApp (TypeApp (TypeConsApp Function) te1) te2)
                       
                        TypeConsApp typeConsApp = (TypeConsApp)nextOperatorType;
                       
                        if (!typeConsApp.getName().equals(CAL_Prelude.TypeConstructors.Function) || typeConsApp.getNArgs() != 0) {   
                            throw new IllegalStateException();           
                        }
                       
                        typePieces[i] = nextTypeApp.getOperandType().prune();
                        typeExpr = typeApp.getOperandType().prune();
                    }  
                   
                } else {                   
                    throw new IllegalStateException();
                }               
            } else {
                throw new IllegalStateException();
            }
        
        }

        typePieces[arity] = typeExpr;

        return typePieces;
    }   
   
    /**
     * Add the uninstantiated typevars and record variables in this type expression to the
     * PolymorphicVarContext. This is useful when toString-ing several related TypeExpr objects,
     * in such a way that the variables within them do no overlap unless they are indeed the same.    
     *
     * Creation date: (10/01/01 11:58:50 AM)
     *
     * @param polymorphicVarContext object threaded through nested toString invocations
     *        to track the type and record variables within this TypeExpr.
     * @return PolymorphicVarContext the polymorphicVarContext argument, if it was not null,
     *      or a new PolymorphicVarContext, suitably populated, otherwise.
     */
    final PolymorphicVarContext indexPolymorphicVars(PolymorphicVarContext polymorphicVarContext) {
       
        if (polymorphicVarContext == null) {
            polymorphicVarContext = PolymorphicVarContext.make();
        }
       
        // indexes uninstantiated typevars as a side effect.      
        toSourceText(new StringBuilder(), polymorphicVarContext, ParenthesizationInfo.NONE, ScopedEntityNamingPolicy.FULLY_QUALIFIED);
              
        return polymorphicVarContext;     
    }   
   
    /**
     * @return the TypeExpr of the compose operator (#), namely (b->c) -> (a->b) -> (a->c).
     */
    static TypeExpr makeComposeType() {
       
        TypeExpr a = TypeExpr.makeParametricType();
        TypeExpr b = TypeExpr.makeParametricType();
        TypeExpr c = TypeExpr.makeParametricType();
        TypeExpr b2c = TypeExpr.makeFunType(b, c);
        TypeExpr a2b = TypeExpr.makeFunType(a, b);
        TypeExpr a2c = TypeExpr.makeFunType(a, c);
        return TypeExpr.makeFunType(b2c, TypeExpr.makeFunType(a2b, a2c));
    }
   
    /**
     * Create a type expression for the type of function from domain to codomain.
     *
     * Creation date: (1/22/01 1:08:03 PM)
     * @return TypeExpr the type expression for the function
     * @param domain the type of the domain. Cannot be null.
     * @param codomain the type of the codomain. Cannot be null.
     */
    public static TypeExpr makeFunType(TypeExpr domain, TypeExpr codomain) {
        if (domain == null) {
            throw new NullPointerException("domain type cannot be null");
        }
        if (codomain == null) {
            throw new NullPointerException("codomain type cannot be null");
        }
       
        TypeExpr[] args = new TypeExpr[] {domain, codomain};
        return new TypeConsApp(TypeConstructor.FUNCTION, args);
    }
   
    /**
     * Create a type expression for a list.
     *
     * @param elementTypeExpr the type of the element. Cannot be null.
     * @param contextModuleTypeInfo
     * @return TypeExpr the type expression for the list with given element type   
     */
    public static TypeExpr makeListType(TypeExpr elementTypeExpr, ModuleTypeInfo contextModuleTypeInfo) {
        if (elementTypeExpr == null) {
            throw new NullPointerException("elementTypeExpr cannot be null");
        }
       
        TypeConstructor listTypeCons = contextModuleTypeInfo.getVisibleTypeConstructor(CAL_Prelude.TypeConstructors.List);
        return new TypeConsApp(listTypeCons, new TypeExpr [] {elementTypeExpr});
    }
            
    /**
     * Create a type expression for a data type that is not dependent on any type variables.
     * For example, "Maybe a" is not such a type because of the "a" while "Boolean" is such a type.
     *
     * Creation date: (3/1/01 5:27:09 PM)    
     * @param rootTypeCons must have type arity == 0.
     * @return TypeExpr the type name of the non parametric type.
     */
    public static TypeExpr makeNonParametricType(TypeConstructor rootTypeCons) {
        
        if (rootTypeCons.getTypeArity() != 0) {
            throw new IllegalArgumentException("TypeExpr.makeNonParametricType can only be used for types with type arity 0. " +
                rootTypeCons + " has type arity " + rootTypeCons.getTypeArity() + ".");
        }

        return new TypeConsApp(rootTypeCons, null);
    }

    /**   
     * Create a type expression for the type "a".
     * 
     * @return TypeExpr
     */
    public static TypeExpr makeParametricType() {
        return new TypeVar();
    }  
   
    /**
     * Create a type expression for a tuple data constructor. For example, if n = 3, then the data
     * constructor Tuple3 or (,,,) has type a -> b -> c -> (a, b, c).
     *
     * @return TypeExpr the type expression for the tuple data cons
     * @param n size of the Tuple type
     */
    public static TypeExpr makeTupleDataConsType(int n) {

        if (n < 2) {
            throw new IllegalArgumentException("TypeExpr.makeTupleDataConsType: a tuple type must have at least 2 components.");
        }

        List<TypeExpr> componentTypeVarList = new ArrayList<TypeExpr>(n);
        for (int i = 0; i < n; ++i) {
            componentTypeVarList.add(new TypeVar());
        }

        TypeExpr typeExpr = makeTupleType(componentTypeVarList);

        for (int i = n - 1; i >= 0; --i) {
            typeExpr = makeFunType(componentTypeVarList.get(i), typeExpr);
        }

        return typeExpr;
    }
   
    /**
     * Create a type expression for a tuple each of whose entries is a different type var.
     *  
     * @param n size of the Tuple type
     * @return TypeExpr the type expression for the tuple
     * @throws IllegalArgumentException if n is not greater than 2
     */
    public static TypeExpr makeTupleType(int n) {

        if (n < 2) {
            throw new IllegalArgumentException("the tuple dimension must be greater than or equal to 2.");
        }

        List<TypeExpr> componentTypeList = new ArrayList<TypeExpr>(n);
        for (int i = 0; i < n; ++i) {
            componentTypeList.add(new TypeVar());
        }

        return makeTupleType(componentTypeList);
    }
   
    /**
     * Create a type expression for a tuple. The type of the tuple depends on the number of elements
     * of componentTypeExprList.
     *        
     * @param componentTypeExprList (TypeExpr) a list of the component type expressions.
     * @return TypeExpr the type expression for the tuple
     * @throws IllegalArgumentException if the number of elements in the list is not greater than 2
     */
    public static TypeExpr makeTupleType(List<TypeExpr> componentTypeExprList) {
       
        int nChildren = componentTypeExprList != null ? componentTypeExprList.size() : 0;
        if (nChildren < 2) {
            throw new IllegalArgumentException("the tuple dimension must be greater than or equal to 2.");
        }
       
                              
        Map<FieldName, TypeExpr> fieldNameToTypeMap = new HashMap<FieldName, TypeExpr>(); //FieldName -> TypeExpr      
        for (int i = 0; i < nChildren; ++i) {
            fieldNameToTypeMap.put(FieldName.makeOrdinalField(i + 1), componentTypeExprList.get(i));
        }
       
        return new RecordType(RecordVar.NO_FIELDS, fieldNameToTypeMap);
    }
   
    /**
     * Create a type expression for a free record. The result will be a record type containing
     * fields named as specified and associated to parametric values.
     *
     * Ex: makeRecordType([age,height]) creates record (r\age, r\height) == {r | age :: a, height :: b}
     * Ex: makeRecordType([]) == {r}
     * 
     * @param fieldNamesSet Set of field names.
     * @return RecordType the record type
     */
    public static RecordType makeFreeRecordType(Set<FieldName> fieldNamesSet) {
       
        // Create field name to field type map
        Map<FieldName, TypeExpr> extensionFieldsMap = new HashMap<FieldName, TypeExpr>();
        for (final FieldName fieldName : fieldNamesSet) {                  
           
            TypeExpr fieldType = TypeExpr.makeParametricType();
            extensionFieldsMap.put(fieldName, fieldType);
        }
       
        // Create a new free record with this type
        return new RecordType(RecordVar.makeRecordVar(null, new HashSet<FieldName>(extensionFieldsMap.keySet()), TypeClass.NO_CLASS_CONSTRAINTS, false), extensionFieldsMap);
    }
   
    /**
     * Create a new type expression for a record-polymorphic type containing the
     * specified fields and associated types.
     *
     * Ex: makePolymorphicRecordType( [("age"->Double), ("name"->a)] )
     *     creates the record (r\age,r\name) => {r | age :: Double, name :: a}
     * Ex: makePolymorphicRecordType( [] ) creates the free record {r}
     *
     * Note: The record fields will contain copies of the specified types, but referential equality
     * will be preserved.
     *
     * @param fieldNameToTypeMap Map (FieldName->TypeExpr) of field name to field type expression
     * @return RecordType the resulting record type
     */
    public static RecordType makePolymorphicRecordType(Map<FieldName, TypeExpr> fieldNameToTypeMap) {
       
        // Copy the field type expressions, preserving referential equality
       
        TypeExpr[] oldFieldTypes = fieldNameToTypeMap.values().toArray(new TypeExpr[fieldNameToTypeMap.values().size()]);
        TypeExpr[] newFieldTypes = copyTypeExprs(oldFieldTypes);
        List<TypeExpr> oldFieldTypesList = Arrays.asList(oldFieldTypes);
        List<TypeExpr> newFieldTypesList = Arrays.asList(newFieldTypes);
       
        // Check the extension field names, and make a copy of the map
       
        Map<FieldName, TypeExpr> newExtensionFieldsMap = new HashMap<FieldName, TypeExpr>(); //FieldName -> TypeExpr
        for (final Map.Entry<FieldName, TypeExpr> entry : fieldNameToTypeMap.entrySet()) {
                      
            FieldName fieldName = entry.getKey();           
           
            TypeExpr fieldType = entry.getValue();
            if (fieldType == null) {
                throw new IllegalArgumentException("The record field \"" + fieldName.getCalSourceForm() + "\" refers to a null type expression");
            }
           
            newExtensionFieldsMap.put(fieldName, newFieldTypesList.get(oldFieldTypesList.indexOf(fieldType)));
        }
       
        // Create a new free record with these fields
        return new RecordType(RecordVar.makeRecordVar(null, new HashSet<FieldName>(newExtensionFieldsMap.keySet()), TypeClass.NO_CLASS_CONSTRAINTS, false), newExtensionFieldsMap);
    }
   
    /**
     * Create a new type expression for a non-record-polymorphic type containing the
     * specified fields and associated types.
     *
     * For example,
     * makeNonPolymorphicRecordType([("age", Double), ("name", a)] ) == {age :: Double, name :: a}
     * makeNonPolymorphicRecordType([("age", Double), ("inner", (r\name) => {r | name :: String})])
     *     == (r\name)=>{age :: Double, inner :: {r | name :: String}}
     * makeNonPolymorphicRecordType([]) == {}
     *
     * Note: The record fields will contain copies of the specified types, but referential equality
     * will be preserved.
     *
     * @param fieldNameToTypeMap Map (FieldName->TypeExpr) of field name to field type expression
     * @return RecordType the resulting record type
     */
    public static RecordType makeNonPolymorphicRecordType(Map<FieldName, TypeExpr> fieldNameToTypeMap) {
       
        // Copy the field type expressions, preserving referential equality
       
        TypeExpr[] oldFieldTypes = fieldNameToTypeMap.values().toArray(new TypeExpr[fieldNameToTypeMap.values().size()]);
        TypeExpr[] newFieldTypes = copyTypeExprs(oldFieldTypes);
        List<TypeExpr> oldFieldTypesList = Arrays.asList(oldFieldTypes);
        List<TypeExpr> newFieldTypesList = Arrays.asList(newFieldTypes);
       
        // Check the extension field names, and make a copy of the map
       
        Map<FieldName, TypeExpr> newExtensionFieldsMap = new HashMap<FieldName, TypeExpr>();
        for (final Map.Entry<FieldName, TypeExpr> entry : fieldNameToTypeMap.entrySet()) {
                       
            FieldName fieldName = entry.getKey();          
           
            TypeExpr fieldType = entry.getValue();
            if (fieldType == null) {
                throw new IllegalArgumentException("The record field \"" + fieldName.getCalSourceForm() + "\" refers to a null type expression");
            }
           
            newExtensionFieldsMap.put(fieldName, newFieldTypesList.get(oldFieldTypesList.indexOf(fieldType)));
        }
       
        // Create a new free record with these fields
        return new RecordType(RecordVar.NO_FIELDS, newExtensionFieldsMap);
    }
   
    /**
     * Succeeds without throwing a TypeException if anotherTypeExpr can be specialized to this TypeExpr.
     * If it can, then anotherTypeExpr is thus specialized.
     * Creation date: (3/22/01 4:02:47 PM)
     * @param anotherTypeExpr
     * @param patternMatchContext 
     */
    abstract void patternMatch(TypeExpr anotherTypeExpr, PatternMatchContext patternMatchContext) throws TypeException;
   
    /**
     * Attempt to match inferredTypeExpr (binding the type variables occurring within it) to
     * declaredTypeExpr. During pattern matching, declaredTypeExpr is not modified at all.
     * Thus, unification really corresponds to two-way pattern matching.
     * Note that unlike the unifyType and canUnifyType methods, the order of arguments is significant.
     *
     * Creation date: (9/12/00 5:56:28 PM)
     * @param declaredTypeExpr type expression arising from a declaration e.g. head :: [Int] -> Int
     * @param inferredTypeExpr type expression arising from type inference e.g. for head it is [a] -> a
     * @param contextModuleTypeInfo context in which this pattern matching takes place. This is needed to know what class instances are in scope.
     * @throws TypeException
     */
    static void patternMatch(TypeExpr declaredTypeExpr, TypeExpr inferredTypeExpr, ModuleTypeInfo contextModuleTypeInfo) throws TypeException {

        patternMatch(declaredTypeExpr, inferredTypeExpr, (NonGenericVars)null, contextModuleTypeInfo);
    }
   
    /**
     * Attempt to match inferredTypeExpr (binding the type variables occurring within it) to
     * declaredTypeExpr. During pattern matching, declaredTypeExpr is not modified at all.
     * Thus, unification really corresponds to two-way pattern matching.
     * Note that unlike the unifyType and canUnifyType methods, the order of arguments is significant.
     * <P>
     * This method is intended for pattern matching for local type declarations, where the list of
     * non-generic type variables can be non-empty. A non-generic type variable in inferredTypeExpr
     * cannot be instantiated to a generic type variable in declaredTypeExpr. Note however, that it
     * is OK to instantiate a non-generic type variable to a type constructor.   
     * <P>
     * Creation date: (9/12/00 5:56:28 PM)
     * @param declaredTypeExpr type expression arising from a declaration e.g. head :: [Int] -> Int
     * @param inferredTypeExpr type expression arising from type inference e.g. for head it is [a] -> a
     * @param nonGenericVars the list of non-generic type variables when inferredTypeExpr is being defined.
     *          Can be null if there are no non-generic type variables.
     * @param contextModuleTypeInfo
     * @throws TypeException
     */
    static void patternMatch(TypeExpr declaredTypeExpr, TypeExpr inferredTypeExpr, NonGenericVars nonGenericVars, ModuleTypeInfo contextModuleTypeInfo) throws TypeException {

        if (contextModuleTypeInfo == null) {
            throw new NullPointerException();
        }
              
        declaredTypeExpr = declaredTypeExpr.prune();
        inferredTypeExpr = inferredTypeExpr.prune();       
                   
        PatternMatchContext patternMatchContext = new PatternMatchContext(declaredTypeExpr, nonGenericVars, contextModuleTypeInfo);
        declaredTypeExpr.patternMatch(inferredTypeExpr, patternMatchContext);
    }
   
    /**
     * If types1 = [t1, ..., tn], and types2 = [u1, ..., un], then this function
     * checks if types2 can be specialized to types1 i.e. u1 can be specialized to t1,
     * u2 can be specialized to t2, ...
     *
     * What this really means: we can assign types to the type variables occurring in types2,
     * (other than those also occurring in types1) so that u1(with assignments) == t1,
     * u2(with assignments) == t2, ...
     *                   
     * There are no side-effects on the arguments.
     *
     * The TypeExpr elements of the returned array have no instantiated type variables within them.
     *
     * This function correctly handles shared type variables i.e. the same type variable appearing
     * in types1 and in types2.
     *
     * The types2 array may be longer than the types1 array. For example:
     * patternMatchTypePieces ([Int], [a, a -> Char, Maybe a]) = [Int, Int -> Char, Maybe Int].
     * Another example:
     * patternMatchTypePieces ([c -> c], [a -> b, [a -> b]]) = [d -> d, [d -> d]].  
     * The types1 array may have null values. For example:
     * patternMatchTypePieces ([null, Int], [a -> Char, a])  = [Int -> Char, Int].
     * One can think of types1 as specifying constraints on types2, and there might not be a constraint for each type
     * expression in the types2 array. The returned value array, which has the same length
     * as types2, is types2 where the pattern matching constraints have been taken into account.
     *     
     * @param types1 an array whose length is <= the length of types1 and may contain null values
     * @param types2 an array whose length is >= the length of types1, and does not contain null values
     * @param contextModuleTypeInfo
     * @return TypeExpr[] an array having the same length as types2.
     * @throws TypeException
     */
    public static TypeExpr[] patternMatchPieces(TypeExpr[] types1, TypeExpr[] types2, ModuleTypeInfo contextModuleTypeInfo) throws TypeException {
       
        if (contextModuleTypeInfo == null) {
            throw new NullPointerException();
        }
       
        int nTypes1 = types1.length;
        int nTypes2 = types2.length;
        if (nTypes1 > nTypes2) {
            throw new IllegalArgumentException();       
        }
       
        //types2 may not contain null values
        if (TypeExpr.hasNullType(types2)) {
            throw new IllegalArgumentException();
        }      
               
        //so at this point nTypes2 >= nTypes1.
       
        //copy as a pair since there may be referentially identical type variables in types1 and types2.
        CopyEnv.TypesPair copyTypesPair = TypeExpr.copyTypesPair(types1, types2);
        TypeExpr[] copyTypes1 = copyTypesPair.getTypes1();
        TypeExpr[] copyTypes2 = copyTypesPair.getTypes2();
       
        for (int i = 0; i < nTypes1; ++i) {
           
            if (copyTypes1[i] != null) {           
                TypeExpr.patternMatch(copyTypes1[i], copyTypes2[i], contextModuleTypeInfo);
            }
        }
       
        for (int i = 0; i < nTypes2; ++i) {
            copyTypes2[i] = copyTypes2[i].deepPrune();           
        }
       
        return copyTypes2;                             
    }
   
    /**
     * Returns a TypeExpr without redundant instantiated type variables at the top of this TypeExpr.
     * The result of prune is always a non-instantiated type variable, a type constructor or a
     * record type.
     *
     * This function has no side-effects.
     *
     * Creation date: (7/17/00 3:59:38 PM)
     * @return TypeExpr the pruned type expression
     */
    abstract TypeExpr prune();
   
    /**
     * Similar to prune, except that the arguments (if any) are also pruned.
     * Also record variables are pruned.
     *
     * Another important difference is that this function has side effects- this TypeExpr is
     * modified to not have instantiation chains (except for possibly at the root of the TypeExpr
     * tree if it happens to be an instantiated type variable).
     *
     * Creation date: (18/07/01 1:15:10 PM)
     * @return TypeExpr a TypeExpr representing the same Type as this TypeExpr, but without any
     *    instantiated type or record variables in its tree representation.
     */
    abstract TypeExpr deepPrune();
   
    /**
     * Returns the most compact and efficient representation of a TypeExpr
     * within the equivalence class of TypeExpr objects that represent the same type.
     *
     * Technically, this function removes redundant instantiated type and record variables
     * as well as simplifies TypeApp nodes that could be written as TypeConsApp.
     * For example, (TypeApp (TypeApp (TypeConsApp Function []) Int) Char) is replaced
     * by its equivalent form (TypeConsApp Function [Int, Char]).
     *
     * This function has no side-effects. However, the type are record variables
     * of this TypeExpr and the returned TypeExpr are shared, so modification of one e.g.
     * via instantiations will also modify the other.
     *
     * Creation date: (18/07/01 1:15:10 PM)
     * @return TypeExpr a TypeExpr representing the same type as this TypeExpr, but using the
     *    simplest representative in the equivalence class.
     */
    abstract TypeExpr normalize();
   
    /**
     * Checks if 2 types are equal, up to renamings of type variables. For example, the types:
     * "a -> b -> [a] -> [b]", "b -> a -> [b] -> [a]" and "c -> d -> [c] -> [d]" would all be
     * considered to be the same type under this method.
     *
     * By definition, 2 types t1 and t2 are the same via this method if t1.toString().equals(t2.toString()).
     * However, this method will be significantly faster in many cases than comparing string representations,
     * especially in the case when the 2 types are different.       
     */
    public abstract boolean sameType(TypeExpr anotherTypeExpr);   
     
    /**
     * Returns the address of this object. Primarily intended for debugging purposes.
     * Creation date: (6/6/01 4:29:53 PM)
     * @return String
     */
    final String toAddressString() {
        return "" + super.hashCode();      
    }
   
    /**
     * Returns a string representation of this TypeExpr that also indicates the address of each
     * type variable appearing in the TypeExpr.
     * @return String
     */ 
    final String toDebugString() {
                                   
        StringBuilder debugTypeString = new StringBuilder();
       
        Set<TypeVar> typeVarsSet = getUninstantiatedTypeVars();
        int nVars = typeVarsSet.size();
        if (nVars > 0) { 
            debugTypeString.append('<');     
            int varN = 0;
            for (final TypeVar typeVar : typeVarsSet) {
               
                debugTypeString.append(PolymorphicVarContext.indexToVarName(varN + 1)).append("=").append(typeVar.toAddressString());
                if (varN < nVars - 1) {
                    debugTypeString.append(", ");
                }
                ++varN;
            }
            debugTypeString.append("> ");
        }
       
        debugTypeString.append(toString());
       
        return debugTypeString.toString();       
    }
                        
    /**
     * Returns a string representation of this TypeExpr. Type and record variables are represented
     * by lower case letters: a, b, c, ..., z, a1, a2, a3,... according to their first occurrence in the
     * non-context part of this TypeExpr. Type constructor and type class names appearing in the type
     * are fully qualified.
     * <p>
     * The String representation will be the same for equivalent type expressions i.e. ones that represent
     * the "same" type (where type variables are all considered to be quantified over the type itself).
     * Some defining characteristics:
     * <ul>
     *     <li>the resulting String is parseable as a CAL type.</li>
     *     <li>redundant parentheses are not displayed; all parentheses displayed are required for a correct parse</li>
     *     <li>If Prelude.Function or Prelude.List are fully saturated, then they will appear in their symbolic forms e.g.
     *         (Prelude.Int -> Prelude.Char rather than the equivalent Prelude.Function Prelude.Int Prelude.Char
     *         and [Prelude.String] rather than the equivalent Prelude.List Prelude.String).</li>
     *     <li>records will use tuple notation when possible, and otherwise use field-name notation where the fields are
     *         displayed in field-name order.</li>
     </ul>
     <p>
     * Creation date: (3/26/01 2:26:03 PM)
     * @return a canonical String representation of this TypeExpr
     */
    @Override
    public final String toString() {       
        return toString((PolymorphicVarContext)null, ScopedEntityNamingPolicy.FULLY_QUALIFIED);
    }
   
    /**
     * @see TypeExpr#toString(boolean, ScopedEntityNamingPolicy)
     */
    public final String toString(ScopedEntityNamingPolicy namingPolicy) {
        return toString((PolymorphicVarContext)null, namingPolicy);
    }
   
    /**
     * A version of TypeExpr.toString that provides more options for displaying the String in a more human-readable format.
     *
     * @param favorPreferredPolymorphicVarNames if true, then the preferred names of type are record variables will be used
     *      if possible. The definition of 'if possible' is subject to change so this is more of a preference to display prettier
     *      type strings in client code. if false, then type and record variables are represented by lower case letters:
     *       a, b, c, ..., z, a1, a2, a3,... according to their first occurrence in the non-context part of this TypeExpr
     * @param namingPolicy naming policy influencing how type constructor and type class names occurring within this TypeExpr
     *           are displayed in the returned value.
     * @return a String representation of this TypeExpr
     */
    public final String toString(boolean favorPreferredPolymorphicVarNames, ScopedEntityNamingPolicy namingPolicy) {
       
        PolymorphicVarContext polymorphicVarContext = PolymorphicVarContext.make(favorPreferredPolymorphicVarNames);
       
        if (favorPreferredPolymorphicVarNames) {
            indexPolymorphicVars(polymorphicVarContext);
        }
       
        return toString(polymorphicVarContext, namingPolicy);
    }
   
    /**
     * Similar to TypeExpr#toSourceModel(boolean, ScopedEntityNamingPolicy). Type are record variables are scoped over the
     * whole TypeExpr array argument, and so sharing between the different types in the array is properly shown.
     *
     * @see TypeExpr#toString(boolean, ScopedEntityNamingPolicy)
     * @param types
     * @param favorPreferredPolymorphicVarNames
     * @param namingPolicy
     * @return String[]
     */
    public static final String[] toStringArray(
        TypeExpr[] types,
        boolean favorPreferredPolymorphicVarNames,
        ScopedEntityNamingPolicy namingPolicy) {
       
        PolymorphicVarContext polymorphicVarContext = PolymorphicVarContext.make(favorPreferredPolymorphicVarNames);
      
        final int nTypes = types.length;
       
        if (favorPreferredPolymorphicVarNames) {
            for (int i = 0; i < nTypes; ++i) {
                types[i].indexPolymorphicVars(polymorphicVarContext);
            }
        }
              
        String[] typeStrings = new String[nTypes];
        for (int i = 0; i < nTypes; ++i) {
            typeStrings[i] = types[i].toString(polymorphicVarContext, namingPolicy);
        }
       
        return typeStrings;
    }
          
    /**
     * Returns a string representation of this TypeExpr. Type and record variables are represented
     * by lower case letters: a, b, c, ..., z, a1, a2, a3,... according to their first occurrence in the
     * polymorphicVarContext argument.
     * <p>
     * Creation date: (3/26/01 2:26:03 PM)
     * @param polymorphicVarContext object threaded through nested toString invocations
     *        to track the type and record variables within this TypeExpr. Can be null. If not null, then any
     *        uninstantiated type and record variables occurring in this TypeExpr that are not already
     *        in the context are added.
     * @param namingPolicy
     * @return the resultant string representation
     */
    public final String toString(PolymorphicVarContext polymorphicVarContext, ScopedEntityNamingPolicy namingPolicy) {

        // create a new PolymorphicVarContext if necessary
        if (polymorphicVarContext == null) {
            polymorphicVarContext = PolymorphicVarContext.make();
        }
       
        if (namingPolicy == null) {
            namingPolicy = ScopedEntityNamingPolicy.FULLY_QUALIFIED;          
        }            
             
        // generate the type string, without including the context (the stuff to the left of =>).
        StringBuilder sb = new StringBuilder();
        toSourceText(sb, polymorphicVarContext, ParenthesizationInfo.NONE, namingPolicy);
        String typeString = sb.toString();
    
        // now generate the context string 
        String contextString = makeContextString(polymorphicVarContext, namingPolicy);
       
        return contextString + typeString;
    }        

    /**
     * Returns a source model representation of this TypeExpr.
     *
     * @return SourceModel.TypeSignature the resultant source model representation
     */
    public final SourceModel.TypeSignature toSourceModel() {
        return toSourceModel((PolymorphicVarContext)null, ScopedEntityNamingPolicy.FULLY_QUALIFIED);
    }
   
    /**
     * Returns a SourceModel representation of this TypeExpr, but with more control about the formatting use
     * for type and record variables as well as type constructors and type classes occurring within the TypeExpr.
     * @param favorPreferredPolymorphicVarNames if true, then the preferred names of type are record variables will be used
     *      if possible. The definition of 'if possible' is subject to change so this is more of a preference to display prettier
     *      type strings in client code. if false, then type and record variables are represented by lower case letters:
     *       a, b, c, ..., z, a1, a2, a3,... according to their first occurrence in the non-context part of this TypeExpr
     * @param namingPolicy naming policy influencing how type constructor and type class names occurring within this TypeExpr
     *           are displayed in the returned value.
     * @return SourceModel.TypeSignature the resultant source model representation
     */
    public final SourceModel.TypeSignature toSourceModel(boolean favorPreferredPolymorphicVarNames, ScopedEntityNamingPolicy namingPolicy) {
        PolymorphicVarContext polymorphicVarContext = PolymorphicVarContext.make(favorPreferredPolymorphicVarNames);
        if (favorPreferredPolymorphicVarNames) {
            indexPolymorphicVars(polymorphicVarContext);
        }
               
        return toSourceModel(polymorphicVarContext, namingPolicy);        
    }
   
    /**
     * Similar to TypeExpr#toSourceModel(boolean, ScopedEntityNamingPolicy). Type are record variables are scoped over the
     * whole TypeExpr array argument, and so sharing between the different types in the array is properly shown.
     *
     * @see TypeExpr#toSourceModel(boolean, ScopedEntityNamingPolicy)
     * @param types
     * @param favorPreferredPolymorphicVarNames
     * @param namingPolicy
     * @return SourceModel.TypeSignature[]
     */
    public static final SourceModel.TypeSignature[] toSourceModelArray(
        TypeExpr[] types,
        boolean favorPreferredPolymorphicVarNames,
        ScopedEntityNamingPolicy namingPolicy) {
       
        PolymorphicVarContext polymorphicVarContext = PolymorphicVarContext.make(favorPreferredPolymorphicVarNames);
      
        final int nTypes = types.length;
       
        if (favorPreferredPolymorphicVarNames) {
            for (int i = 0; i < nTypes; ++i) {
                types[i].indexPolymorphicVars(polymorphicVarContext);
            }
        }
              
        SourceModel.TypeSignature[] typeSourceModels = new SourceModel.TypeSignature[nTypes];
        for (int i = 0; i < nTypes; ++i) {
            typeSourceModels[i] = types[i].toSourceModel(polymorphicVarContext, namingPolicy);
        }
       
        return typeSourceModels;
    }   
   
   
    /**
     * Returns a source model representation of this TypeExpr.
     * @param polymorphicVarContext object threaded through nested toString invocations
     *        to track the type and record variables within this TypeExpr. Can be null. If not null, then any
     *        uninstantiated type and record variables occurring in this TypeExpr that are not already
     *        in the context are added.
     * @param namingPolicy NamingPolicy to use when generating names
     * @return SourceModel.TypeSignature the resultant source model representation
     */
    final SourceModel.TypeSignature toSourceModel(PolymorphicVarContext polymorphicVarContext, ScopedEntityNamingPolicy namingPolicy) {
        // create a new PolymorphicVarContext if necessary
        if (polymorphicVarContext == null) {
            polymorphicVarContext = PolymorphicVarContext.make();
        }
       
        if (namingPolicy == null) {           
            namingPolicy = ScopedEntityNamingPolicy.FULLY_QUALIFIED;          
        }
                            
        // generate the source model for the type, without including the context (the stuff to the left of =>).
        SourceModel.TypeExprDefn typeSourceModel = makeDefinitionSourceModel(polymorphicVarContext, namingPolicy);
       
        // now generate the context portion
        SourceModel.Constraint[] contextSourceModel = makeContextSourceModel(polymorphicVarContext, namingPolicy);
       
        return SourceModel.TypeSignature.make(contextSourceModel, typeSourceModel);
    }

    /**
     * A helper function that returns all the constrained type and record variables occurring in
     * this TypeExpr, ordered by first occurrence.
     *
     * TypeVars and RecordVars can technically live in a separate namespace, but for reasons of
     * simplicity we share the namespace i.e. we could technically allow:
     * (a\field1, Num a) => {a | } -> {a | field1 :: a}
     * but actually require
     * (a\field1, Num b) => {a | } -> {a | field1 :: b}
     * for reasons of end-user simplicity in reading String representations of type expressions.
     *
     * @return (Set (Either TypeVar RecordVar))
     */
    final Set<PolymorphicVar> getConstrainedPolymorphicVars(){
        PolymorphicVarContext localizedVarContext = PolymorphicVarContext.make();
        //called for the side effect of populating localizedVarContext
        toSourceText(new StringBuilder(), localizedVarContext, ParenthesizationInfo.NONE, ScopedEntityNamingPolicy.FULLY_QUALIFIED);
        return localizedVarContext.getConstrainedPolymorphicVars();    
    }
   
    /**
     * Build a string representation of the context of a type expression with a given PolymorphicVarContext
     * that determines how the class constraints (on type variables) and the lacks constraints (on record variables)
     * will be displayed.
     * @param polymorphicVarContext object threaded through nested toString invocations
     *        to track the type and record variables within this TypeExpr.
     * @param namingPolicy
     * @return the string representation     
     */
    private final String makeContextString(PolymorphicVarContext polymorphicVarContext, ScopedEntityNamingPolicy namingPolicy) {
        
        Set<PolymorphicVar> contextPolymorphicVars = this.getConstrainedPolymorphicVars();

        List<String> contextList = new ArrayList<String>();
           
        for (final PolymorphicVar polymorphicVar : contextPolymorphicVars) {
                 
            if (polymorphicVar instanceof TypeVar) {
                        
                TypeVar typeVar = (TypeVar)polymorphicVar;
              
                String typeVarName = polymorphicVarContext.getPolymorphicVarName(typeVar);                             
                if (DEBUG_INFO) {
                    typeVarName += typeVar.toAddressString();
                }
               
                if (typeVar.noClassConstraints()) {
                    //there is a bug in getGenericConstrainedTypeVars...
                    throw new IllegalStateException();
                }
                          
                for (final TypeClass typeClass : typeVar.getTypeClassConstraintSet()) {
                                   
                    String typeClassName = namingPolicy.getName(typeClass);
                    contextList.add(typeClassName + " " + typeVarName);
                }
               
            } else if (polymorphicVar instanceof RecordVar) {
               
                RecordVar recordVar = (RecordVar)polymorphicVar;
                                                                           
                String recordVarName = polymorphicVarContext.getPolymorphicVarName(recordVar);                
               
                //add the type class constraints
               
                for (final TypeClass typeClass : recordVar.getTypeClassConstraintSet()) {
                               
                     String typeClassName = namingPolicy.getName(typeClass);
                     contextList.add(typeClassName + " " + recordVarName);
                }
               
                //add the lacks fields constraints
               
                Set<FieldName> lacksFieldsSet = recordVar.getLacksFieldsSet();
                List<String> lacksConstraints = new ArrayList<String>();
                for (final FieldName lacksFieldName : lacksFieldsSet) {
                   
                    lacksConstraints.add(recordVarName + "\\" + lacksFieldName.getCalSourceForm());
                }
               
                //need a canonical order for the lacks fields. This is alphabetical order.
                Collections.sort(lacksConstraints);
               
                contextList.addAll(lacksConstraints);
                                                 
            } else {
                throw new IllegalStateException();
            }
        }

        int nContexts = contextList.size();

        if (nContexts == 0) {       
            return "";
        }     

        StringBuilder result = new StringBuilder();

        if (nContexts > 1) {      
            result.append("(");
        }    

        for (int i = 0; i < nContexts; ++i) {
           
            if (i > 0) {           
                result.append(", ");
            }           
            result.append(contextList.get(i));
        }

        if (nContexts > 1) {       
            result.append(")");
        }

        result.append(" => ");

        return result.toString();
    }
   
    /**
     * Build a source model representation of the context of a type expression
     * with a given PolymorphicVarContext that determines how the class
     * constraints (on type variables) and the lacks constraints (on record
     * variables) will be displayed.
     *
     * @param polymorphicVarContext
     *            object threaded through nested makeDefinitionSourceModel
     *            invocations to track the type and record variables within this
     *            TypeExpr.
     * @param namingPolicy NamingPolicy to use when constructing names          
     * @return the source model representation of the context
     */
    private final SourceModel.Constraint[] makeContextSourceModel(PolymorphicVarContext polymorphicVarContext, ScopedEntityNamingPolicy namingPolicy) {
       
        Set<PolymorphicVar> contextPolymorphicVars = this.getConstrainedPolymorphicVars();

        List<SourceModel.Constraint> contextList = new ArrayList<SourceModel.Constraint>();
           
        for (final PolymorphicVar polymorphicVar : contextPolymorphicVars) {
          
            if (polymorphicVar instanceof TypeVar) {
                        
                TypeVar typeVar = (TypeVar)polymorphicVar;
                                     
                String typeVarName = polymorphicVarContext.getPolymorphicVarName(typeVar);  
                if (DEBUG_INFO) {
                    typeVarName += typeVar.toAddressString();
                }
               
                if (typeVar.noClassConstraints()) {
                    //there is a bug in getGenericConstrainedTypeVars...
                    throw new IllegalStateException();
                }
                          
                for (final TypeClass typeClass : typeVar.getTypeClassConstraintSet()) {
                   
                    final ModuleName typeClassModuleName = namingPolicy.getModuleNameForScopedEntity(typeClass);
                   
                    SourceModel.Name.TypeClass typeClassName =
                        SourceModel.Name.TypeClass.make(typeClassModuleName, typeClass.getName().getUnqualifiedName());
                   
                    contextList.add(SourceModel.Constraint.TypeClass.make(typeClassName, SourceModel.Name.TypeVar.make(typeVarName)));
                }
               
            } else if (polymorphicVar instanceof RecordVar) {
               
                RecordVar recordVar = (RecordVar)polymorphicVar;
                                                                           
                String recordVarName = polymorphicVarContext.getPolymorphicVarName(recordVar);             
              
                //add the type class constraints
               
                for (final TypeClass typeClass : recordVar.getTypeClassConstraintSet()) {
                                                           
                    final ModuleName typeClassModuleName = namingPolicy.getModuleNameForScopedEntity(typeClass);
                   
                    SourceModel.Name.TypeClass typeClassName =
                        SourceModel.Name.TypeClass.make(typeClassModuleName, typeClass.getName().getUnqualifiedName());
                   
                    contextList.add(SourceModel.Constraint.TypeClass.make(typeClassName, SourceModel.Name.TypeVar.make(recordVarName)));
                }
               
                //add the lacks fields constraints
               
                Set<FieldName> lacksFieldsSet = recordVar.getLacksFieldsSet();
                List<SourceModel.Constraint.Lacks> lacksConstraints = new ArrayList<SourceModel.Constraint.Lacks>();
                for (final FieldName lacksFieldName : lacksFieldsSet) {                  
                    lacksConstraints.add(SourceModel.Constraint.Lacks.make(SourceModel.Name.TypeVar.make(recordVarName), SourceModel.Name.Field.make(lacksFieldName)));
                }
               
                //need a canonical order for the lacks fields. This is alphabetical order.
                Collections.sort(lacksConstraints, new Comparator<SourceModel.Constraint.Lacks>() {
                    /** {@inheritDoc} */
                    public int compare(SourceModel.Constraint.Lacks o1, SourceModel.Constraint.Lacks o2) {
                        return o1.toSourceText().compareTo(o2.toSourceText());
                    }
                });
               
                contextList.addAll(lacksConstraints);
                                                 
            } else {
                throw new IllegalStateException();
            }
        }

        return contextList.isEmpty() ? null : contextList.toArray(new SourceModel.Constraint[0]);
    }

    /**
     * Build a textual representation of this type expression without including the context (the stuff to the left of =>).
     * Creation date: (10/11/00 5:52:57 PM)
     * @param sb StringBuilder to use to accumulate the resulting source text.
     * @param polymorphicVarContext object threaded through nested toString invocations
     *        to track the type and record variables within this TypeExpr.
     * @param parenthesizationInfo info on whether to parenthesize the root node of this type expression from the point of view
     *        of the parent of this node in the TypeExpr tree.
     * @param namingPolicy how to display type constructors e.g. whether unqualified, fully qualified etc.
  
     */
    abstract void toSourceText(StringBuilder sb,
        PolymorphicVarContext polymorphicVarContext,
        ParenthesizationInfo parenthesizationInfo,    
        ScopedEntityNamingPolicy namingPolicy);
   
    /**
     * Builds a source model representation of this type expression without
     * including the context (the stuff to the left of =>).
     *
     * @param polymorphicVarContext
     *            object threaded through nested makeDefinitionSourceModel
     *            invocations to track the type and record variables within this
     *            TypeExpr.
     * @param namingPolicy ScopedEntityNamingPolicy to use when creating names
     * @return the source model representation of the type expression definition
     *         (without the context)
     */
    abstract SourceModel.TypeExprDefn makeDefinitionSourceModel(PolymorphicVarContext polymorphicVarContext, ScopedEntityNamingPolicy namingPolicy);

    /**
     * A helper function for unifying this type expression with another type expression.
     * @param anotherTypeExpr
     * @param contextModuleTypeInfo
     * @return int type closeness. See the javadoc for TypeExpr.getTypeCloseness for a detailed description of the type closeness heuristic.
     */
    abstract int unifyType(TypeExpr anotherTypeExpr, ModuleTypeInfo contextModuleTypeInfo) throws TypeException;
   
    /**
     * Unify two type expressions. What this means is to find the most general type to which both
     * typeExpr1 and typeExpr2 can be specialized to. Unification fails when trying to match 2 different
     * type constructors (such as Boolean and Int) or when trying to instantiate a type variable to a term
     * containing that type variable (like a and a->b, where a circular structure would be built.)
     * As a general point of interest, this process is essentially the same as unification in Prolog.
     * <p>
     * Note that this function has side effects and modifies the TypeExpr arguments. If it fails in a TypeException,
     * the modification is undefined. Otherwise, the modification is the unification.
     * <p>
     * Creation date: (7/21/00 1:27:22 PM)
     * @param typeExpr1
     * @param typeExpr2
     * @param contextModuleTypeInfo context in which this unification takes place. This is needed to know what class instances are in scope.
     * @return int a measure of the closeness between the 2 argument types. The larger the number, the closer
     *  the 2 types are. This value should not be persisted and may change in the future. It is intended for use
     *  by UI clients such as Intellicut in filtering the list of possible candidates to display in the pick list
     *  of gems to those that are a closer match. This number is not scaled. What this means is that you can't
     *  interpret a closeness of 3 as a having a fixed meaning. In other words, if you fix the first argument,
     *  unifyType can be considered as a function from TypeExpr -> Int and the maximum and minimum values attained
     *  by this function of one variable on its domain gives the interpretation of the closeness magnitude for
     *  that particular first argument.  See the javadoc for TypeExpr.getTypeCloseness for a detailed description of the type closeness heuristic.
     */
    static int unifyType(TypeExpr typeExpr1, TypeExpr typeExpr2, ModuleTypeInfo contextModuleTypeInfo) throws TypeException {
   
        if (contextModuleTypeInfo == null) {
            throw new NullPointerException();
        }

        typeExpr1 = typeExpr1.prune();
        typeExpr2 = typeExpr2.prune();

        return typeExpr1.unifyType(typeExpr2, contextModuleTypeInfo);
    }
   
    /**
     * Similar to TypeExpr.unifyType except there are no side effects on the argument type expressions.
     * The returned TypeExpr has no instantiated type variables within it.
     * <p>
     * Note: this function correctly handles the case where typeExpr1 and typeExpr2 have shared type variables.
     * For example, we can't unify (a, Int) and (Char, a), but we can unify (a, Int) and (Char, a').
     * @param typeExpr1
     * @param typeExpr2
     * @param contextModuleTypeInfo
     * @return TypeExpr
     * @throws TypeException
     */
    public static TypeExpr unify(TypeExpr typeExpr1, TypeExpr typeExpr2, ModuleTypeInfo contextModuleTypeInfo) throws TypeException {
       
        //Note: we copy both type expressions simultaneously since there may be referentially
        //identical type variables in common.
        //So for example, (a, Int) and (Char, a) can't unify whereas (a, Int) and (Char, a') can.
        TypeExpr[] copiedTypes = TypeExpr.copyTypeExprs(new TypeExpr[] {typeExpr1, typeExpr2});
        TypeExpr typeExprCopy1 = copiedTypes[0];
        TypeExpr typeExprCopy2 = copiedTypes[1];
       
        TypeExpr.unifyType(typeExprCopy1, typeExprCopy2, contextModuleTypeInfo);
       
        return typeExprCopy1.deepPrune();              
    }
   
    /**
     * If types1 = [t1, ..., tn], and types2 = [u1, ..., un], then this function unifies t1, u1, then t2, u2,
     * ... and then tn, un, returning a copy of the result.
     * <p>
     * There are no side-effects on the arguments.
     * <p>
     * The TypeExpr elements of the returned array have no instantiated type variables within them.
     * <p>
     * This function correctly handles shared type variables. For example:
     * [a -> b, a, (a, b, Boolean)] and [Int -> Double, a, (a, c, d)] unifies to [Int -> Double, Int, (Int, Double, Boolean)].
     *
     * The types2 array may be longer than the types1 array. For example:
     * unifyTypePieces ([a], [Int, a -> Char]) = [Int, Int -> Char].   
     * The types1 array may have null values. For example:
     * unifyTypePieces ([null, a], [a -> Char, Int])  = [Int -> Char, Int].
     * One can think of types1 as specifying constraints on types2, and there might not be a constraint for each type
     * expression in the types2 array. The returned value array, which has the same length
     * as types2, is types2 where the unification constraints from types1 have been taken into account.
     *     
     * @param types1 an array whose length is <= the length of types2, and may contain null values
     * @param types2 an array whose length is >= the length of types and does not contain null values     
     * @param contextModuleTypeInfo
     * @return TypeExpr[] an array having the same length as types2.
     * @throws TypeException
     */
    public static TypeExpr[] unifyTypePieces(TypeExpr[] types1, TypeExpr[] types2, ModuleTypeInfo contextModuleTypeInfo) throws TypeException {
       
        if (contextModuleTypeInfo == null) {
            throw new NullPointerException();
        }
       
        int nTypes1 = types1.length;
        int nTypes2 = types2.length;
        if (nTypes1 > nTypes2) {       
            throw new IllegalArgumentException();       
        }
       
        //types2 may not contain null values
        if (TypeExpr.hasNullType(types2)) {
            throw new IllegalArgumentException();
        }     
               
        //so at this point nTypes2 >= nTypes1.
       
        //copy as a pair since there may be referentially identical type variables in types1 and types2.
        CopyEnv.TypesPair copyTypesPair = TypeExpr.copyTypesPair(types1, types2);
        TypeExpr[] copyTypes1 = copyTypesPair.getTypes1();
        TypeExpr[] copyTypes2 = copyTypesPair.getTypes2();
       
        for (int i = 0; i < nTypes1; ++i) {
           
            if (copyTypes1[i] != null) {           
                TypeExpr.unifyType(copyTypes1[i], copyTypes2[i], contextModuleTypeInfo);
            }
        }
       
        for (int i = 0; i < nTypes2; ++i) {
            copyTypes2[i] = copyTypes2[i].deepPrune();           
        }
       
        return copyTypes2;                             
    }
   
    /**    
     * @param types
     * @return boolean true if any of the elements in the types array are null.
     */
    static boolean hasNullType(TypeExpr[] types) {
        for (int i = 0, nTypes = types.length; i < nTypes; ++i) {
            if (types[i] == null) {
                return true;
            }
        }
       
        return false;
    }
   
    /**
     * Method usesForeignType.
     * @return boolean return true if any of the type constructors in the type was created with a foreign data declaration    
     */
    abstract public boolean usesForeignType ();   
   
    /**
     * The idea here is that unconstrainedTypes can be specialized to oldSpecializedTypes (such as when the inputs of a gem are specialized
     * by value panels), and someone has changed one of the oldSpecializedTypes, at index switchIndex to switchedType. Then what is the best
     * updatedSpecializedTypes that can be computed? In other words, unconstrainedTypes can be specialized to updatedSpecializedTypes,
     * updatedSpecializedTypes[switchIndex] is equal to switchType, and unconstrainedTypes has all the specializations of oldSpecializedTypes
     * that are not invalidated by switchType. For example:
     *
     * getUpdatedSpecializedTypes((Char, Long), 1, [(Int, Boolean), (Int, Double)], [(a, b), (a, c)]) == [(Char, Boolean), (Char, Long)].
     *
     * @param switchedType
     * @param switchIndex
     * @param oldSpecializedTypes
     * @param unconstrainedTypes
     * @param currentModuleTypeInfo
     * @return TypeExpr[] null if the specialization relationships described above between the arguments of this function do not hold
     */
    public static TypeExpr[] getUpdatedSpecializedTypes(TypeExpr switchedType, int switchIndex, TypeExpr[] oldSpecializedTypes, TypeExpr[] unconstrainedTypes, ModuleTypeInfo currentModuleTypeInfo) {
      
        TypeExpr unconstrainedSwitchType = unconstrainedTypes[switchIndex];
          
        if (!TypeExpr.canPatternMatchPieces(oldSpecializedTypes, unconstrainedTypes, currentModuleTypeInfo) ||
            !TypeExpr.canPatternMatch(switchedType, unconstrainedSwitchType, currentModuleTypeInfo)) {
            return null
        }
                   
        // Returns a map going from the typeVars in the unconstrained type that the value was connected with,
        //   to their associated TypeExprs in the newValueNode type.
        Map<TypeExpr, TypeExpr> affectedTypeVarToNewTypeMap = getTypeVarCorrespondenceMap(unconstrainedSwitchType, switchedType);
                   
        int nTypes = unconstrainedTypes.length;                   

        // Now construct the map from type var to its specialization.
        // First populate with its former specialization..
        Map<TypeExpr, TypeExpr> typeVarToSpecializationMap = new HashMap<TypeExpr, TypeExpr>();
        for (int i = 0; i < nTypes; ++i) {
            // Map type vars in the unconstrained type to copied corresponding type in the old value.
            Map<TypeExpr, TypeExpr> typeVarToOldSpecializedTypeMap = getTypeVarCorrespondenceMap(unconstrainedTypes[i], oldSpecializedTypes[i]);
            typeVarToSpecializationMap.putAll(typeVarToOldSpecializedTypeMap);

        }
        // Now add new specializations to ensure these take precedence.
        typeVarToSpecializationMap.putAll(affectedTypeVarToNewTypeMap);
       
       
        // Copy the type specializations, so we can use them in the return values.
       
        // First create of the type var specializations, in the same order that they appear in an iterator over the map keys.
        List<TypeExpr> typeVarSpecializationList = new ArrayList<TypeExpr>();
        for (final Map.Entry<TypeExpr, TypeExpr> entry : typeVarToSpecializationMap.entrySet()) {
            final TypeExpr specialization = entry.getValue();
            typeVarSpecializationList.add(specialization);
        }
       
        // Convert to an array, make a copy.
        TypeExpr[] typeVarSpecializationArray = typeVarSpecializationList.toArray(new TypeExpr[typeVarSpecializationList.size()]);
        TypeExpr[] typeVarSpecializationArrayCopy = copyTypeExprs(typeVarSpecializationArray);
       
        // Create a map from the type var to the copied specialized type.
        Map<TypeExpr, TypeExpr> typeVarToSpecializationCopyMap = new HashMap<TypeExpr, TypeExpr>();
        int index = 0;
        for (final TypeExpr typeVar : typeVarToSpecializationMap.keySet()) {
            typeVarToSpecializationCopyMap.put(typeVar, typeVarSpecializationArrayCopy[index]);
            index++;
        }
       
       
        // The array of updated specializations - starts out as a copy of the unconstrained types, has its type vars specialized.
        TypeExpr[] updatedSpecializedTypes = copyTypeExprs(unconstrainedTypes);
       
        // Create a map from type vars in the original to type vars in the copy.
        Map<TypeExpr, TypeExpr> typeVarToTypeVarCopyMap = new HashMap<TypeExpr, TypeExpr>();
        for (int i = 0; i < nTypes; i++) {
            typeVarToTypeVarCopyMap.putAll(getTypeVarCorrespondenceMap(unconstrainedTypes[i], updatedSpecializedTypes[i]));
        }

        // Specialize the type variables in the copy to the new types.
        for (final Map.Entry<TypeExpr, TypeExpr> entry : typeVarToSpecializationCopyMap.entrySet()) {
            TypeExpr affectedTypeVar = entry.getKey();
            TypeExpr affectedTypeVarCopy = typeVarToTypeVarCopyMap.get(affectedTypeVar);
            TypeExpr specializeTypeCopy = entry.getValue();

            try {
                TypeExpr.patternMatch(specializeTypeCopy, affectedTypeVarCopy, currentModuleTypeInfo);
            } catch (TypeException e) {                  
                return null;
            }
        }

        // Fully-prune..
        for (int i = 0; i < nTypes; ++i) {
            updatedSpecializedTypes[i] = updatedSpecializedTypes[i].deepPrune();      
        }
       
        return updatedSpecializedTypes;
    }
   
    /**
     * For use in finding associations between type vars in a given type and their corresponding types in a
     *   specialization of that type.
     *
     * Note: it is assumed that canPatternMatch(correspondingType, typeToMap).
     *
     * Example:
     *           typeToMap: (a, (Double, b))
     *   correspondingType: ((Foo, c), (Double, [d]))
     *  resulting mappings: a -> (Foo, c)
     *                      b -> [d]
     *
     * How do you find that 'a' corresponds to 'x'? You use this method.
     * Once you know that 'a' corresponds to 'x', then you can find all other values that corresponds to 'a' as well, and
     * manipulate them as necessary.
     * 
     * @param typeToMap the type expr from which to map type vars.
     * @param correspondingType the corresponding type expr for which type vars from typeToMap will be mapped. 
     *   This will be at least as specialized as the type to map.
     * @return Map (TypeExpr->TypeExpr) from type var in typeToMap to the corresponding type expr in corresponding type.
     */
    private static Map<TypeExpr, TypeExpr> getTypeVarCorrespondenceMap(TypeExpr typeToMap, TypeExpr correspondingType) {
        Map<TypeExpr, TypeExpr> returnMap = new HashMap<TypeExpr, TypeExpr>();
        TypeExpr.getTypeVarCorrespondenceMapHelper(typeToMap, correspondingType, returnMap);
        return returnMap;
    }

    /**
     * Helper for getTypeVarCorrespondenceMap
     * @param typeToMap
     * @param correspondingType
     * @param map
     */
    private static void getTypeVarCorrespondenceMapHelper(TypeExpr typeToMap, TypeExpr correspondingType, Map<TypeExpr, TypeExpr> map) {
 
        // If it's a type var, then we've completed mapping
        if (typeToMap instanceof TypeVar) {
            map.put(typeToMap, correspondingType);

        } else if (typeToMap instanceof TypeConsApp) {
            // If it's a data constructor, map its arguments.
            for (int i = 0, size = typeToMap.rootTypeConsApp().getNArgs(); i < size; i++) {
                getTypeVarCorrespondenceMapHelper(typeToMap.rootTypeConsApp().getArg(i),
                                                  correspondingType.rootTypeConsApp().getArg(i).prune(), map);
            }
        } else if (typeToMap instanceof RecordType) {
           
            Map<FieldName, TypeExpr> hasFieldsMap = ((RecordType)typeToMap).getHasFieldsMap();
            RecordType correspondingRecordType = correspondingType.rootRecordType();
            Map<FieldName, TypeExpr> correspondingHasFieldsMap = correspondingRecordType.getHasFieldsMap();          
           
            for (final Map.Entry<FieldName, TypeExpr> entry : hasFieldsMap.entrySet()) {
                              
                FieldName fieldName = entry.getKey();
                TypeExpr fieldType = entry.getValue();           
                TypeExpr correspondingFieldType = correspondingHasFieldsMap.get(fieldName);
               
                getTypeVarCorrespondenceMapHelper(fieldType, correspondingFieldType, map);                                       
            }           
                      
        } else {               
            throw new IllegalStateException("Programming error.");
        }
    }
   
    /**
     * Write this TypeExpr to the RecordOutputStream. Writing a single type object out is suspicious are you
     * sure you don't want the write(RecordOutputStream, TypeExpr[]) function?
     * @param s
     * @throws IOException
     */
    final void write (RecordOutputStream s) throws IOException {
        Map<TypeExpr, Short> visitedTypeExpr = new HashMap<TypeExpr, Short>();
        Map<RecordVar, Short> visitedRecordVar = new HashMap<RecordVar, Short>();
        write (s, visitedTypeExpr, visitedRecordVar);
    }
   
    /**
     * Write the array of types to the output stream. This will make sure all vars are mapped to the same objects.
     * @param s
     * @param types
     * @throws IOException
     */
    static final void write(RecordOutputStream s, ArrayList<TypeExpr> types) throws IOException{
        Map<TypeExpr, Short> visitedTypeExpr = new HashMap<TypeExpr, Short>();
        Map<RecordVar, Short> visitedRecordVar = new HashMap<RecordVar, Short>();
        for(int i = 0, nTypes = types.size(); i < nTypes; ++i){
            types.get(i).write (s, visitedTypeExpr, visitedRecordVar);
        }
    }
   
    /**
     * Write this TypeExpr to the RecordOutputStream.
     * @param s
     * @param visitedTypeExpr - map of TypeExpr instances that have been serialized.
     * @param visitedRecordVar - map of RecordVar instances that have been serialized.
     * @throws IOException
     */
    final void write (RecordOutputStream s, Map<TypeExpr, Short> visitedTypeExpr, Map<RecordVar, Short> visitedRecordVar) throws IOException {
                     
        if (this == EMPTY_RECORD) {
            s.startRecord(ModuleSerializationTags.STATIC_CONSTANT_TYPE_EXPR, staticConstantTypeExprSerializationSchema);
            s.writeUTF("EMPTY_RECORD");
            s.endRecord();
        } else {
           
            Short key = visitedTypeExpr.get(this);
            if (key != null) {
                // This instance has already been serialized so we simply reference it by key.
                s.startRecord(ModuleSerializationTags.ALREADY_VISITED_TYPE_EXPR, alreadyVisitedTypeExprSerializationSchema);
                s.writeShortCompressed(key.shortValue());
                s.endRecord();
            } else {
                // This instance has not been serialized.  Add it to the map of serialized TypeExpr
                // and write it out.
                key = new Short((short)visitedTypeExpr.size());
                visitedTypeExpr.put(this, key);
               
                writeActual(s, visitedTypeExpr, visitedRecordVar);
            }
        }
    }
   
    /**
     * Method to actually write the instance of TypeExpr to the RecordOutputStream.
     * @param s
     * @param visitedTypeExpr
     * @param visitedRecordVar
     * @throws IOException
     */
    abstract void writeActual (RecordOutputStream s, Map<TypeExpr, Short> visitedTypeExpr, Map<RecordVar, Short> visitedRecordVar) throws IOException;
   
    /**
     * Load a TypeExpr from the RecordInputStream.
     * @param s
     * @param mti - the containing ModuleTypeInfo
     * @param msgLogger the logger to which to log deserialization messages.
     * @return an instance of TypeExpr
     * @throws IOException
     */
    static final TypeExpr load (RecordInputStream s, ModuleTypeInfo mti, CompilerMessageLogger msgLogger) throws IOException {
        Map<Short, TypeExpr> visitedTypeExpr = new HashMap<Short, TypeExpr>();
        Map<Short, RecordVar> visitedRecordVar = new HashMap<Short, RecordVar>();
        return TypeExpr.load(s, mti, visitedTypeExpr, visitedRecordVar, msgLogger);
    }
  
    /**
     * Load an array of TypeExpr from the RecordInputStream. This will ensure that the same variables will
     * map the the same TypeVar objects.
     * @param s
     * @param mti
     * @param nTypes
     * @param msgLogger the logger to which to log deserialization messages.
     * @return An array of TypeExpr as read from the input stream.
     * @throws IOException
     */
    static final TypeExpr[] load (RecordInputStream s, ModuleTypeInfo mti, int nTypes, CompilerMessageLogger msgLogger) throws IOException {
        Map<Short, TypeExpr> visitedTypeExpr = new HashMap<Short, TypeExpr>();
        Map<Short, RecordVar> visitedRecordVar = new HashMap<Short, RecordVar>();
        TypeExpr[] types = new TypeExpr[nTypes];
        for(int i = 0; i < nTypes; ++i){
            types[i] = TypeExpr.load(s, mti, visitedTypeExpr, visitedRecordVar, msgLogger);
        }
        return types;
    }

    /**
     * Load a TypeExpr from the RecordInputStream.
     * @param s
     * @param mti
     * @param visitedTypeExpr - a map of loaded TypeExpr instances.
     * @param visitedRecordVar - a map of loaded RecordVar instances.
     * @param msgLogger the logger to which to log deserialization messages.
     * @return an instance of TypeExpr
     * @throws IOException
     */
    static final TypeExpr load (RecordInputStream s, ModuleTypeInfo mti, Map<Short, TypeExpr> visitedTypeExpr, Map<Short, RecordVar> visitedRecordVar, CompilerMessageLogger msgLogger) throws IOException {
       
        // Determine which specific class this serialized instance is.
        RecordHeaderInfo rhi = s.findRecord(TYPE_EXPR_RECORD_TAGS);
        if (rhi == null) {
            throw new IOException ("Unable to find TypeExpr record header.");
        }
       
        // Based on the record tag load the actual instance.
        switch (rhi.getRecordTag()) {
       
            case ModuleSerializationTags.STATIC_CONSTANT_TYPE_EXPR:
            {
                if (rhi.getSchema() > staticConstantTypeExprSerializationSchema) {
                    throw new IOException("Stored schema " + rhi.getSchema() " is greater than current schema " + staticConstantTypeExprSerializationSchema + " when loading a static constant TypeExpr.");
                }
                String constName = s.readUTF();
                s.skipRestOfRecord();
                                                         
                if (constName.equals ("EMPTY_RECORD")) {
                    return EMPTY_RECORD;
                } else {
                   
                }
            }
           
            case ModuleSerializationTags.ALREADY_VISITED_TYPE_EXPR:
            {
                // This is a reference to an existing instance.  The instance should be
                // in the visitedTypeExpr map.
                if (rhi.getSchema() > alreadyVisitedTypeExprSerializationSchema) {
                    throw new IOException("Stored schema " + rhi.getSchema() " is greater than current schema " + alreadyVisitedTypeExprSerializationSchema + " loading an already visited TypeExpr.");
                }
               
                // Read the key and do the lookup.
                short id = s.readShortCompressed();
                TypeExpr te = visitedTypeExpr.get(new Short(id));
                if (te == null) {
                    throw new IOException ("Unable to resolve previously encountered TypeExpr instance in TypeExpr.");
                }
                s.skipRestOfRecord();
                return te;
            }
           
            case ModuleSerializationTags.TYPE_CONSTRUCTOR:
            {
                return TypeConsApp.load(s, rhi.getSchema(), mti, visitedTypeExpr, visitedRecordVar, msgLogger);            
            }
           
            case ModuleSerializationTags.TYPE_APP:
            {
                return TypeApp.load(s, rhi.getSchema(), mti, visitedTypeExpr, visitedRecordVar, msgLogger);
            }
           
            case ModuleSerializationTags.TYPE_VAR:
            case ModuleSerializationTags.TYPE_VAR_WITH_INSTANCE:
            {
                return TypeVar.load(s, rhi.getRecordTag(), rhi.getSchema(), mti, visitedTypeExpr, visitedRecordVar, msgLogger);               
            }
           
            case ModuleSerializationTags.RECORD_TYPE:
            {
                return RecordType.load(s, rhi.getSchema(), mti, visitedTypeExpr, visitedRecordVar, msgLogger);              
            }
           
            default:
            {
                throw new IOException("Unexpected record tag " + rhi.getRecordTag() + " found when looking for TypeExpr.");
            }
        }
    }
}
TOP

Related Classes of org.openquark.cal.compiler.TypeExpr

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.