package nl.utwente.viskell.haskell.type;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
/**
* Haskell TypeClass with its instances and superclasses.
*/
public class TypeClass implements Comparable<TypeClass> {
/**
* An instance declaration
*/
final static class Instance implements Comparable<Instance> {
/**
* The type constructor that identifies this instance.
*/
private final TypeCon typecon;
/**
* The number of type arguments that needs to be constrained to make this instance valid
* For Example: Eq a => Eq (Maybe a) is the instance Maybe#1, and (Show l, Show r) => Show (Either l r) is Either#2
* Most Prelude instances are simple enough to not required sub-constraints, so this number will be often just 0.
*/
private final int constrainedArgs;
public Instance(TypeCon typecon, int constrainedArgs) {
super();
this.typecon = typecon;
this.constrainedArgs = constrainedArgs;
}
@Override
public String toString() {
return this.typecon.toString() + "#" + this.constrainedArgs;
}
@Override
public int compareTo(Instance other) {
return this.typecon.getName().compareTo(other.typecon.getName());
}
}
/**
* The name of this type class.
*/
private String name;
/**
* The instances of this type class.
*/
private Set<Instance> instances;
/**
* The superclasses of this type class.
*/
private Set<TypeClass> supers;
/** The optional type constructor to use for the typeclass defaulting. */
private Optional<TypeCon> defaultType;
/**
* @param name The name of this type class.
* @param types The types that are a member of this type class.
*/
public TypeClass(String name, TypeCon ... cons) {
this.name = name;
this.instances = new HashSet<>();
this.supers = new HashSet<>();
this.defaultType = Optional.empty();
for (TypeCon tc : cons) {
this.addInstance(tc, 0);
}
}
/**
* @return The name of this type class.
*/
public final String getName() {
return this.name;
}
/**
* @param tc The type constructor to add to this class
* @param constrainedArgs the number of type parameter that needs to be constrained to make the instance valid
*/
public final void addInstance(TypeCon tc, int constrainedArgs) {
this.instances.add(new Instance(tc, constrainedArgs));
}
public void setDefaultType(TypeCon defType) {
this.defaultType = Optional.of(defType);
}
protected Optional<TypeCon> getDefaultType() {
return this.defaultType;
}
/**
* @return the super classes of this type class
*/
protected Set<TypeClass> getSupers() {
return this.supers;
}
/**
* @param tc The super class that this class requires
*/
public final void addSuperClass(TypeClass tc) {
this.supers.add(tc);
// Also transitively add all the superclasses of this superclass for easier simplification
this.supers.addAll(tc.supers);
}
protected Set<TypeCon> allInstanceTypeCons() {
return this.instances.stream().map(i -> i.typecon).collect(Collectors.toSet());
}
/**
* @param type The type constructor to check.
* @return Whether the given type constructor is in this type class.
*/
public final boolean hasType(TypeCon type) {
Instance inst = new Instance(type, 0);
return this.instances.stream().anyMatch(i -> i.compareTo(inst) == 0);
}
/**
* @param type The type constructor to check.
* @return The number of constrained arguments the instance of this typecon in this class has, or -1 if not found.
*/
public int lookupConstrainedArgs(TypeCon con) {
for (Instance inst : this.instances) {
if (inst.typecon.equals(con)) {
return inst.constrainedArgs;
}
}
return -1;
}
public final String toString() {
return String.format("%s=>%s:%s", this.supers.stream().map(t ->t.getName()), this.name, this.instances.toString());
}
@Override
public int compareTo(TypeClass other) {
return this.getName().compareTo(other.getName());
}
}