package nl.utwente.viskell.haskell.type;
import com.google.common.base.Joiner;
import java.util.List;
import java.util.Optional;
/**
* Abstract class for Haskell types. Provides an interface for common methods.
*/
public abstract class Type {
/** @return The number of arguments that can be applied to a value of this type. */
public int countArguments() {
int count = 0;
Type type = this.getConcrete();
while (type instanceof FunType) {
count++;
type = ((FunType)type).getResult();
}
return count;
}
/**
* @return The readable representation of this type for in the UI.
*/
public String prettyPrint() {
return this.prettyPrint(0);
}
/**
* @param fixity of the context the type is shown in.
* The fixity is small positive number derived from operator precedence (see also Section 4.4.2 of the Haskell language report)
* @return The readable representation of this type for in the UI.
*/
public abstract String prettyPrint(final int fixity);
/**
* @param fixity of the context the type is shown in.
* @param args list of types applied to this type.
* @return the pretty representation of this type a list of of types
*/
protected String prettyPrintAppChain(final int fixity, final List<Type> args) {
final StringBuilder out = new StringBuilder();
out.append(this.prettyPrint(10));
out.append(' ');
out.append(Joiner.on(' ').join(args.stream().map(a -> a.prettyPrint(10)).iterator()));
if (fixity > 1) {
return "(" + out.toString() + ")";
}
return out.toString();
}
/**
* @return An equivalent deep copy of this type, using fresh type variables.
*/
public final Type getFresh() {
return this.getFresh(new TypeScope());
}
/**
* @param scope The scope wherein related shared type variable are maintained
* @return An equivalent deep copy of this type, using fresh type variables.
*/
public abstract Type getFresh(final TypeScope scope);
/** @return The type with all instantiated type variable replaced by concrete types. */
public abstract Type getConcrete();
/**
* @return The presence of the argument type variable some in this type.
*/
public abstract boolean containsOccurenceOf(TypeVar tvar);
@Override
public abstract String toString();
/** Ensures that this type stays maximal polymorphic by marking all internal type variables rigid */
public void enforcePolymorphism() {
for (TypeVar.TypeInstance tvi : TypeScope.gatherAllTypeVarInsts(this)) {
tvi.makeRigid();
}
}
/**
* Attempts to produce a concrete type with all type variables instantiated.
* @param backupFiller the type to use when no type can be found using type class defaulting.
* @return a type without any polymorphism, if successful.
*/
public Optional<Type> defaultedConcreteType(ConcreteType backupFiller) {
Type ftype = this.getFresh();
try {
for (TypeVar.TypeInstance tvi : TypeScope.gatherAllTypeVarInsts(ftype)) {
tvi.defaultOrElse(backupFiller);
}
Type ctype = ftype.getConcrete();
TypeChecker.unify("defaulting validation", ctype.getFresh(), this.getFresh());
return Optional.of(ctype);
} catch (HaskellTypeError e) {
return Optional.empty();
}
}
/**
* @return a new type constructor
* @param name of type constructor.
*/
public final static TypeCon con(String name) {
if ("[]".equals(name)) {
return new ListTypeCon();
}
if ("()".equals(name)) {
return new TupleTypeCon(0);
}
if (name.startsWith("(,")) {
return new TupleTypeCon(name.length()-1);
}
return new TypeCon(name);
}
/**
* @return a new type from a applied type constructor
* @param name of type constructor.
* @param args of type argument to constructor is applied too
*/
public final static Type con(String name, Type... args) {
Type t = Type.con(name);
for (Type a : args) {
t = new TypeApp(t, a);
}
return t;
}
/**
* @return a new function type in a chain with all arguments
* @param elems of types
*/
public final static Type fun(Type... elems) {
if (elems.length == 0) {
throw new IllegalArgumentException("can not create an empty function type");
}
final int last = elems.length - 1;
Type t = elems[last];
for (int n = last - 1; n >= 0; n--) {
t = new FunType(elems[n], t);
}
return t;
}
/**
* @return a chained type application of all the arguments
* @param elems of types
*/
public final static Type app(Type... elems) {
if (elems.length == 0) {
throw new IllegalArgumentException("can not create an empty type applications");
}
Type t = elems[0];
for (int n = 1; n < elems.length; n++) {
t = new TypeApp(t, elems[n]);
}
return t;
}
/**
* @return an applied list type construction
* @param elem element type
*/
public final static Type listOf(Type elem) {
return new TypeApp(new ListTypeCon(), elem);
}
/**
* @return a fully applied tuple type construction
* @param elems list of element types
*/
public final static Type tupleOf(Type... elems) {
Type t = new TupleTypeCon(elems.length);
for (Type e : elems) {
t = new TypeApp(t, e);
}
return t;
}
}