package nl.utwente.viskell.haskell.type;
import nl.utwente.viskell.ghcj.HaskellException;
import nl.utwente.viskell.haskell.env.Environment;
import nl.utwente.viskell.haskell.env.HaskellCatalog;
import nl.utwente.viskell.haskell.expr.Apply;
import nl.utwente.viskell.haskell.expr.Expression;
import nl.utwente.viskell.haskell.expr.Hole;
import nl.utwente.viskell.haskell.expr.Value;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class UnificationTest {
@Test
public void testUnifyUndefined() throws HaskellException {
Environment env = new HaskellCatalog().asEnvironment();
Expression e0 = env.useFun("const");
Type t0 = e0.inferType();
assertEquals("k -> x -> k", t0.prettyPrint());
Expression e1 = new Apply(e0, new Hole());
Type t1 = e1.inferType();
assertEquals("x -> k", t1.prettyPrint());
Expression e2 = new Apply(e1, new Hole());
Type t2 = e2.inferType();
TypeChecker.unify(e2, t2, new Hole().inferType());
//No exception thrown -> Types are the same, as expected. The test will fail if an Exception is thrown.
}
@Test
public void testUnifyFloats() throws HaskellException {
Environment env = new HaskellCatalog().asEnvironment();
Expression e0 = env.useFun("const");
Type t0 = e0.inferType();
assertEquals("k -> x -> k", t0.prettyPrint());
Expression e1 = new Apply(e0, new Value(Type.con("Float"), "5.0"));
Type t1 = e1.inferType();
assertEquals("x -> Float", t1.prettyPrint());
Expression e2 = new Apply(e1, new Value(Type.con("Float"), "5.0"));
Type t2 = e2.inferType();
assertEquals("Float", t2.prettyPrint());
}
@Test
public void testUnifyABool() throws HaskellException {
Environment env = new HaskellCatalog().asEnvironment();
Expression e0 = env.useFun("const");
e0.inferType();
Expression e1 = new Apply(e0, new Hole());
e1.inferType();
Expression e2 = new Apply(e1, new Hole());
Type t2 = e2.inferType();
Type t3 = TypeScope.unique("t");
Type t4 = Type.con("Bool");
TypeChecker.unify(e1, t3, t4);
TypeChecker.unify(e2, t2, t4);
}
@Test
public void testTypeclassCopy() throws HaskellException {
Environment env = new HaskellCatalog().asEnvironment();
TypeClass num = env.testLookupClass("Num");
TypeClass read = env.testLookupClass("Show");
TypeClass show = env.testLookupClass("Read");
TypeScope scope = new TypeScope();
Type ct = Type.con("Float");
Type t0 = scope.getVarTC("a", num, read);
Type t1 = scope.getVarTC("b", num, show);
Expression e0 = new Value(t0, "?");
Expression e1 = new Value(t1, "?");
Expression e2 = new Apply(new Apply(env.useFun("(+)"), e0), e1);
e2.inferType();
TypeChecker.unify(e0, t0, ct);
TypeChecker.unify(e1, t1, ct);
}
@Test
public void testInstanceChaining() throws HaskellException {
Environment env = new HaskellCatalog().asEnvironment();
// (id (id (1 :: Int)))
Expression e0 = new Apply(env.useFun("id"),
new Apply(env.useFun("id"),
new Value(Type.con("Int"), "1")));
Type t0 = e0.inferType();
assertEquals(t0.prettyPrint(), "Int");
}
@Test
public void testDeepUnification() throws HaskellException {
TypeScope scope = new TypeScope();
TypeVar a = scope.getVar("a");
TypeVar b = scope.getVar("b");
TypeVar x = scope.getVar("x");
TypeVar y = scope.getVar("y");
TypeChecker.unify("dummy", a, b);
TypeChecker.unify("dummy", x, y);
TypeChecker.unify("dummy", a, x);
TypeChecker.unify("dummy", b, Type.con("Int"));
assertEquals("Int", y.prettyPrint());
}
}