package typing; /** * This class represents a substitution, as returned by the unification * algorithm. * * @author Benedikt Meurer * @version $Id$ */ final class Substitution { /** * Allocates a new {@link Substitution} whose parent is the * {@link #EMPTY_SUBSTITUTION}. * * @param name the name of the type variable. * @param type the monomorphic type to substitute for <code>name</code>. */ Substitution(String name, MonoType type) { this(name, type, EMPTY_SUBSTITUTION); } /** * Allocates a new {@link Substitution} with the given parameters. * * @param name the name of the type variable. * @param type the monomorphic type to substitute for <code>name</code>. * @param parent the parent substitution, which will be applied * after the (<code>name</code>,<code>type</code>) * pair has been applied. */ Substitution(String name, MonoType type, Substitution parent) { this.name = name; this.type = type; this.parent = parent; } /** * Applies the substitution to the given {@link TypeVariable} * <code>tvar</code> and returns the matching {@link Type} for * <code>tvar</code>s name. If no such type exists in this * substitution, <code>tvar</code> is returned. * * @param tvar the {@link TypeVariable} to which this substitution * should be applied. * * @return the resulting {@link Type}. */ MonoType apply(TypeVariable tvar) { // once we've reached the empty substitution, // we know that there's no type for the name if (this == EMPTY_SUBSTITUTION) return tvar; // check if this substitution matches, otherwise // continue with the parent and so on... MonoType tau = (this.name.equals(tvar.getName())) ? this.type : tvar; // continue with the parent substitution return tau.substitute(this.parent); } /** * Returns the composition of this {@link Substitution} and <code>s</code>. * The composition will apply pairs from this substitution first and * afterwards the ones from <code>s</code>. * * @param s another {@link Substitution}. * * @return the composition of this and <code>s</code>. */ Substitution compose(Substitution s) { // if this is the empty substitution, the // result of the composition is simply s if (this == EMPTY_SUBSTITUTION) return s; // compose(parent, s) Substitution parent = this.parent.compose(s); // and prepend (name,type) pair return new Substitution(this.name, this.type, parent); } /** * Returns <code>true</code> if the substitution contains * a type, which in turn contains a free type variable of * the given <code>name</code>. * * @param name the name of the type variable to test. * * @return <code>true</code> if <code>name</code> is present * as free type variable in the substitution. */ boolean containsFreeTypeVariable(String name) { // if this is the empty substitution, then name is not present if (this == EMPTY_SUBSTITUTION) return false; // check if name is equal or this type contains name if (this.name.equals(name) || this.type.containsFreeTypeVariable(name)) return true; // check the parent return this.parent.containsFreeTypeVariable(name); } /** * Returns the string representation of the substitution. * This method is mainly useful for debugging purposes. * * @return the string representation of the substitution. * * @see java.lang.Object#toString() */ @Override public String toString() { StringBuilder builder = new StringBuilder(128); builder.append('['); for (Substitution subst = this; subst != EMPTY_SUBSTITUTION; subst = subst.parent) { if (subst != this) builder.insert(1, ", "); builder.insert(1, subst.type + "/" + subst.name); } builder.append(']'); return builder.toString(); } /** * The empty substitution. */ static final Substitution EMPTY_SUBSTITUTION = new Substitution(); private Substitution() { } // member attributes private String name; private MonoType type; private Substitution parent; }