diff --git a/src/qir/ast/QIRLambda.java b/src/qir/ast/QIRLambda.java
index b7f27bdd89c8c24aa8044941e3e46b29633c9f92..48c9046a5bd7c5056b8a3aea799384063d15a271 100644
--- a/src/qir/ast/QIRLambda.java
+++ b/src/qir/ast/QIRLambda.java
@@ -8,7 +8,7 @@ import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.source.SourceSection;
 
 import qir.driver.IQIRVisitor;
-import qir.types.QIRConstantType;
+import qir.types.QIRAnyType;
 import qir.types.QIRType;
 import qir.util.QIRAny;
 
@@ -45,7 +45,7 @@ public final class QIRLambda extends QIRNode {
     }
 
     public QIRLambda(final SourceSection source, final String funName, final QIRVariable var, final QIRNode body, final FrameDescriptor frameDescr) {
-        this(source, funName, QIRConstantType.ANY, var, body, frameDescr);
+        this(source, funName, QIRAnyType.getInstance(), var, body, frameDescr);
     }
 
     public final String getFunName() {
diff --git a/src/qir/ast/data/QIRRdestr.java b/src/qir/ast/data/QIRRdestr.java
index 5752f1aa2ce66c57ab8ffa2f333eea11f50dea9e..70a26a85b2783b111224f84cf506c898316df80b 100644
--- a/src/qir/ast/data/QIRRdestr.java
+++ b/src/qir/ast/data/QIRRdestr.java
@@ -15,20 +15,20 @@ public final class QIRRdestr extends QIRNode {
     /**
      * The {@link QIRRecord} to access.
      */
-    @Child private QIRNode tuple;
+    @Child private QIRNode record;
     /**
      * The name of the field to look for in the {@link QIRRecord}.
      */
     private final String colName;
 
-    public QIRRdestr(final SourceSection source, final QIRNode tuple, final String colName) {
+    public QIRRdestr(final SourceSection source, final QIRNode record, final String colName) {
         super(source);
-        this.tuple = tuple;
+        this.record = record;
         this.colName = colName;
     }
 
-    public final QIRNode getTuple() {
-        return tuple;
+    public final QIRNode getRecord() {
+        return record;
     }
 
     public final String getColName() {
@@ -37,7 +37,7 @@ public final class QIRRdestr extends QIRNode {
 
     @Override
     public final String toString() {
-        return tuple + "." + colName;
+        return record + "." + colName;
     }
 
     @Override
@@ -46,12 +46,12 @@ public final class QIRRdestr extends QIRNode {
             return true;
         if (!(other instanceof QIRRdestr))
             return false;
-        return tuple.equals(((QIRRdestr) other).tuple) && colName.equals(((QIRRdestr) other).colName);
+        return record.equals(((QIRRdestr) other).record) && colName.equals(((QIRRdestr) other).colName);
     }
 
     @Override
     public QIRNode executeGeneric(VirtualFrame frame) {
-        QIRNode t = tuple.executeGeneric(frame);
+        QIRNode t = record.executeGeneric(frame);
 
         // TODO: Should we allow this in QIR?
         if (t instanceof QIRLcons && ((QIRLcons) t).getTail() == QIRLnil.getInstance())
diff --git a/src/qir/ast/operator/QIRSortBy.java b/src/qir/ast/operator/QIRSortBy.java
index 2d3c9bf77627fe4038f1528d97a0cdbfbf273b4b..2b012f03a72b9d12fdb2b7c3187b9be4e2013a3b 100644
--- a/src/qir/ast/operator/QIRSortBy.java
+++ b/src/qir/ast/operator/QIRSortBy.java
@@ -13,11 +13,11 @@ import qir.util.QIRException;
  */
 public final class QIRSortBy extends QIROperator {
     /**
-     * The list of ordering rows.
+     * The list of sorting rows.
      */
-    private final QIRNode order;
+    private final QIRNode sort;
     /**
-     * A list of booleans telling for every row of {@link QIRSortBy#order} if it is ascending.
+     * A list of booleans telling for every row of {@link QIRSortBy#sort} if it is ascending.
      */
     private final QIRNode isAscending;
     /**
@@ -25,15 +25,15 @@ public final class QIRSortBy extends QIROperator {
      */
     private final QIRNode child;
 
-    public QIRSortBy(final SourceSection source, final QIRNode order, final QIRNode isAscending, final QIRNode child) {
+    public QIRSortBy(final SourceSection source, final QIRNode sort, final QIRNode isAscending, final QIRNode child) {
         super(source);
-        this.order = order;
+        this.sort = sort;
         this.isAscending = isAscending;
         this.child = child;
     }
 
-    public final QIRNode getOrder() {
-        return order;
+    public final QIRNode getSort() {
+        return sort;
     }
 
     public final QIRNode getIsAscending() {
@@ -46,7 +46,7 @@ public final class QIRSortBy extends QIROperator {
 
     @Override
     public final String toString() {
-        return "SortBy(" + order + ", " + isAscending + ", " + child + ")";
+        return "SortBy(" + sort + ", " + isAscending + ", " + child + ")";
     }
 
     @Override
@@ -55,7 +55,7 @@ public final class QIRSortBy extends QIROperator {
             return true;
         if (!(other instanceof QIRSortBy))
             return false;
-        return order.equals(((QIRSortBy) other).order) && isAscending.equals(((QIRSortBy) other).isAscending) && child.equals(((QIRSortBy) other).child);
+        return sort.equals(((QIRSortBy) other).sort) && isAscending.equals(((QIRSortBy) other).isAscending) && child.equals(((QIRSortBy) other).child);
     }
 
     @Override
diff --git a/src/qir/driver/IQIRVisitor.java b/src/qir/driver/IQIRVisitor.java
index 5eef74d3ac92009b45385f712b49f9f207f85a37..ad6cdf4c2e6f488a95d7e16b2b4eeb8d69d15b51 100644
--- a/src/qir/driver/IQIRVisitor.java
+++ b/src/qir/driver/IQIRVisitor.java
@@ -22,9 +22,9 @@ public interface IQIRVisitor<T> {
 
     public abstract T visit(final QIRFilter qirFilter);
 
-    public abstract T visit(final QIRGroupBy qirGroup);
+    public abstract T visit(final QIRGroupBy qirGroupBy);
 
-    public abstract T visit(final QIRSortBy qirOrder);
+    public abstract T visit(final QIRSortBy qirSortBy);
 
     public abstract T visit(final QIRJoin qirJoin);
 
diff --git a/src/qir/driver/QIRDriver.java b/src/qir/driver/QIRDriver.java
index cbc67eac1fa84b46f91eddb665afec721ca3d3d4..980de17b3b371d4d531fdb70695008d7cd1b6361 100644
--- a/src/qir/driver/QIRDriver.java
+++ b/src/qir/driver/QIRDriver.java
@@ -14,16 +14,18 @@ import qir.ast.QIRVariable;
  */
 public class QIRDriver {
     /**
-     * Executes a QIR query. First, the query is normalized using the {@link QIRGreedyNormalizationVisitor}
-     * module, then the query is translated by the {@link QIRGenericTranslationVisitor} module into a query written
-     * in the different languages understandable by the different data providers, finally it is run by
-     * the {@link QIREvaluationVisitor} module.
+     * Executes a QIR query. First, the query is normalized using the
+     * {@link QIRGreedyNormalizationVisitor} module, then the query is translated by the
+     * {@link QIRGenericTranslationVisitor} module into a query written in the different languages
+     * understandable by the different data providers, finally it is run by the
+     * {@link QIREvaluationVisitor} module.
      *
      * @param query The QIR query to execute
      * @return The results of the query
      */
     public static final QIRNode run(final QIRNode query) {
-        return query.accept(new QIRGreedyNormalizationVisitor()).accept(QIRGenericTranslationVisitor.instance).executeGeneric(Truffle.getRuntime().createVirtualFrame(new Object[]{}, new FrameDescriptor()));
+        return query.accept(new QIRGreedyNormalizationVisitor()).accept(QIRGenericTranslationVisitor.instance).executeGeneric(
+                        Truffle.getRuntime().createVirtualFrame(new Object[]{}, new FrameDescriptor()));
     }
 
     /**
diff --git a/src/qir/driver/QIRFreeVarsVisitor.java b/src/qir/driver/QIRFreeVarsVisitor.java
index 9ec947751f167501e64d1b4c692ba96b37b5e3e8..f33c814d94f38a7903f2f8cfee7ebe7b77091dc1 100644
--- a/src/qir/driver/QIRFreeVarsVisitor.java
+++ b/src/qir/driver/QIRFreeVarsVisitor.java
@@ -37,16 +37,16 @@ final class QIRFreeVarsVisitor implements IQIRVisitor<Map<String, QIRVariable>>
     }
 
     @Override
-    public final Map<String, QIRVariable> visit(final QIRGroupBy qirGroup) {
-        qirGroup.getGroup().accept(this);
-        return qirGroup.getChild().accept(this);
+    public final Map<String, QIRVariable> visit(final QIRGroupBy qirGroupBy) {
+        qirGroupBy.getGroup().accept(this);
+        return qirGroupBy.getChild().accept(this);
     }
 
     @Override
-    public final Map<String, QIRVariable> visit(final QIRSortBy qirOrder) {
-        qirOrder.getOrder().accept(this);
-        qirOrder.getIsAscending().accept(this);
-        return qirOrder.getChild().accept(this);
+    public final Map<String, QIRVariable> visit(final QIRSortBy qirSortBy) {
+        qirSortBy.getSort().accept(this);
+        qirSortBy.getIsAscending().accept(this);
+        return qirSortBy.getChild().accept(this);
     }
 
     @Override
@@ -212,7 +212,7 @@ final class QIRFreeVarsVisitor implements IQIRVisitor<Map<String, QIRVariable>>
 
     @Override
     public final Map<String, QIRVariable> visit(final QIRRdestr qirTdestr) {
-        return qirTdestr.getTuple().accept(this);
+        return qirTdestr.getRecord().accept(this);
     }
 
     @Override
diff --git a/src/qir/driver/QIRGenericTranslationVisitor.java b/src/qir/driver/QIRGenericTranslationVisitor.java
index 0f4cfa3d0448fdc71b487ce8d14c91a8f7f096a8..bfc522df6f27497ad3c238d8674a5d5db1057e6c 100644
--- a/src/qir/driver/QIRGenericTranslationVisitor.java
+++ b/src/qir/driver/QIRGenericTranslationVisitor.java
@@ -10,7 +10,8 @@ import qir.ast.operator.*;
 import qir.util.QIRException;
 
 /**
- * {@link QIRGenericTranslationVisitor} represents the translation from QIR to an executable QIR tree.
+ * {@link QIRGenericTranslationVisitor} represents the translation from QIR to an executable QIR
+ * tree.
  */
 final class QIRGenericTranslationVisitor implements IQIRVisitor<QIRNode> {
     private QIRGenericTranslationVisitor() {
@@ -53,27 +54,28 @@ final class QIRGenericTranslationVisitor implements IQIRVisitor<QIRNode> {
     }
 
     @Override
-    public final QIRNode visit(final QIRGroupBy qirGroup) {
-        final QIRNode child = qirGroup.getChild().accept(this);
+    public final QIRNode visit(final QIRGroupBy qirGroupBy) {
+        final QIRNode child = qirGroupBy.getChild().accept(this);
 
         if (child instanceof QIRDBNode)
             try {
-                return new QIRDBNode<>(child.getSourceSection(), ((QIRDBNode<?>) child).getDriver(), new QIRGroupBy(qirGroup.getSourceSection(), qirGroup.getGroup(), child));
+                return new QIRDBNode<>(child.getSourceSection(), ((QIRDBNode<?>) child).getDriver(), new QIRGroupBy(qirGroupBy.getSourceSection(), qirGroupBy.getGroup(), child));
             } catch (QIRException e) {
             }
-        return new QIRGroupBy(qirGroup.getSourceSection(), qirGroup.getGroup().accept(this), child);
+        return new QIRGroupBy(qirGroupBy.getSourceSection(), qirGroupBy.getGroup().accept(this), child);
     }
 
     @Override
-    public final QIRNode visit(final QIRSortBy qirOrder) {
-        final QIRNode child = qirOrder.getChild().accept(this);
+    public final QIRNode visit(final QIRSortBy qirSortBy) {
+        final QIRNode child = qirSortBy.getChild().accept(this);
 
         if (child instanceof QIRDBNode)
             try {
-                return new QIRDBNode<>(child.getSourceSection(), ((QIRDBNode<?>) child).getDriver(), new QIRSortBy(qirOrder.getSourceSection(), qirOrder.getOrder(), qirOrder.getIsAscending(), child));
+                return new QIRDBNode<>(child.getSourceSection(), ((QIRDBNode<?>) child).getDriver(),
+                                new QIRSortBy(qirSortBy.getSourceSection(), qirSortBy.getSort(), qirSortBy.getIsAscending(), child));
             } catch (QIRException e) {
             }
-        return new QIRSortBy(qirOrder.getSourceSection(), qirOrder.getOrder().accept(this), qirOrder.getIsAscending().accept(this), child);
+        return new QIRSortBy(qirSortBy.getSourceSection(), qirSortBy.getSort().accept(this), qirSortBy.getIsAscending().accept(this), child);
     }
 
     @Override
@@ -245,7 +247,7 @@ final class QIRGenericTranslationVisitor implements IQIRVisitor<QIRNode> {
 
     @Override
     public final QIRNode visit(final QIRRdestr qirTdestr) {
-        return new QIRRdestr(qirTdestr.getSourceSection(), qirTdestr.getTuple().accept(this), qirTdestr.getColName());
+        return new QIRRdestr(qirTdestr.getSourceSection(), qirTdestr.getRecord().accept(this), qirTdestr.getColName());
     }
 
     @Override
diff --git a/src/qir/driver/QIRGreedyNormalizationVisitor.java b/src/qir/driver/QIRGreedyNormalizationVisitor.java
index 53abed5fc7f38c80f000ad76b3058300912a3295..a6888a341f422194ff05dd81d6ac766be6b990e8 100644
--- a/src/qir/driver/QIRGreedyNormalizationVisitor.java
+++ b/src/qir/driver/QIRGreedyNormalizationVisitor.java
@@ -11,8 +11,8 @@ import qir.ast.expression.relational.*;
 import qir.ast.operator.*;
 
 /**
- * {@link QIRGreedyNormalizationVisitor} is the QIR greedy normalization module. TODO: Check behavior if
- * reduction fails, especially in ldestr.
+ * {@link QIRGreedyNormalizationVisitor} is the QIR greedy normalization module. TODO: Check
+ * behavior if reduction fails, especially in ldestr.
  */
 final class QIRGreedyNormalizationVisitor implements IQIRVisitor<QIRNode> {
     private final HashMap<String, QIRNode> env = new HashMap<>();
@@ -33,13 +33,13 @@ final class QIRGreedyNormalizationVisitor implements IQIRVisitor<QIRNode> {
     }
 
     @Override
-    public final QIRNode visit(final QIRGroupBy qirGroup) {
-        return new QIRGroupBy(qirGroup.getSourceSection(), qirGroup.getGroup().accept(this), qirGroup.getChild().accept(this));
+    public final QIRNode visit(final QIRGroupBy qirGroupBy) {
+        return new QIRGroupBy(qirGroupBy.getSourceSection(), qirGroupBy.getGroup().accept(this), qirGroupBy.getChild().accept(this));
     }
 
     @Override
-    public final QIRNode visit(final QIRSortBy qirOrder) {
-        return new QIRSortBy(qirOrder.getSourceSection(), qirOrder.getOrder().accept(this), qirOrder.getIsAscending().accept(this), qirOrder.getChild().accept(this));
+    public final QIRNode visit(final QIRSortBy qirSortBy) {
+        return new QIRSortBy(qirSortBy.getSourceSection(), qirSortBy.getSort().accept(this), qirSortBy.getIsAscending().accept(this), qirSortBy.getChild().accept(this));
     }
 
     @Override
@@ -235,7 +235,7 @@ final class QIRGreedyNormalizationVisitor implements IQIRVisitor<QIRNode> {
 
     @Override
     public final QIRNode visit(final QIRRdestr qirTdestr) {
-        final QIRNode tuple = qirTdestr.getTuple().accept(this);
+        final QIRNode tuple = qirTdestr.getRecord().accept(this);
         final String colName = qirTdestr.getColName();
 
         for (QIRNode current = tuple; current instanceof QIRRcons; current = ((QIRRcons) current).getTail())
diff --git a/src/qir/driver/QIRTranslator.java b/src/qir/driver/QIRTranslator.java
index 8bc3539c7f59361d89cbb9ca7b77375b17b0d4cd..e89b1707a10ee647074c5bbefaaf3dc74ee32523 100644
--- a/src/qir/driver/QIRTranslator.java
+++ b/src/qir/driver/QIRTranslator.java
@@ -32,13 +32,13 @@ public abstract class QIRTranslator<T> implements IQIRVisitor<T> {
     }
 
     @Override
-    public T visit(final QIRGroupBy qirGroup) {
-        throw new QIRException(this.getClass().getSimpleName() + " error: unsupported QIR node " + qirGroup.getClass().getSimpleName() + ".");
+    public T visit(final QIRGroupBy qirGroupBy) {
+        throw new QIRException(this.getClass().getSimpleName() + " error: unsupported QIR node " + qirGroupBy.getClass().getSimpleName() + ".");
     }
 
     @Override
-    public T visit(final QIRSortBy qirOrder) {
-        throw new QIRException(this.getClass().getSimpleName() + " error: unsupported QIR node " + qirOrder.getClass().getSimpleName() + ".");
+    public T visit(final QIRSortBy qirSortBy) {
+        throw new QIRException(this.getClass().getSimpleName() + " error: unsupported QIR node " + qirSortBy.getClass().getSimpleName() + ".");
     }
 
     @Override
diff --git a/src/qir/driver/hbase/QIRHBaseQueryVisitor.java b/src/qir/driver/hbase/QIRHBaseQueryVisitor.java
index 5bd5cc90ac799314109fd8e1aef48ffb3ebb36cd..44e859b221e5e4e4bf642cbdd8914c893a60310e 100644
--- a/src/qir/driver/hbase/QIRHBaseQueryVisitor.java
+++ b/src/qir/driver/hbase/QIRHBaseQueryVisitor.java
@@ -78,12 +78,12 @@ final class QIRHBaseQueryVisitor extends QIRTranslator<HBaseQuery> {
     }
 
     @Override
-    public final HBaseQuery visit(QIRGroupBy qirGroup) {
-        if (!(qirGroup.getGroup() instanceof QIRLambda))
-            throw new QIRException("QIR HBase Group not implemented for group type: " + qirGroup.getGroup().getClass());
-        if (!(qirGroup.getChild() instanceof QIRProject))
+    public final HBaseQuery visit(QIRGroupBy qirGroupBy) {
+        if (!(qirGroupBy.getGroup() instanceof QIRLambda))
+            throw new QIRException("QIR HBase Group not implemented for group type: " + qirGroupBy.getGroup().getClass());
+        if (!(qirGroupBy.getChild() instanceof QIRProject))
             throw new QIRException("Invalid QIR HBase Group");
-        final HBaseQuery child = qirGroup.getChild().accept(this);
+        final HBaseQuery child = qirGroupBy.getChild().accept(this);
         final Job job;
         try {
             job = Job.getInstance(conf);
diff --git a/src/qir/driver/sql/QIRHiveStringVisitor.java b/src/qir/driver/sql/QIRHiveStringVisitor.java
index 0260ae5f796ad813672ab9287e99f01a109a313d..17ef16fcadda0acaa1784f4ea0e28b12e926b613 100644
--- a/src/qir/driver/sql/QIRHiveStringVisitor.java
+++ b/src/qir/driver/sql/QIRHiveStringVisitor.java
@@ -84,7 +84,7 @@ public class QIRHiveStringVisitor extends QIRSQLStringVisitor {
 
     @Override
     public final String visit(final QIRRdestr qirTdestr) {
-        QIRNode tuple = qirTdestr.getTuple();
+        QIRNode tuple = qirTdestr.getRecord();
 
         if (tuple instanceof QIROperator)
             throw new QIRException("Invalid subquery in Hive.");
diff --git a/src/qir/driver/sql/QIRSQLStringVisitor.java b/src/qir/driver/sql/QIRSQLStringVisitor.java
index 33f1a42beed765dab369ef4e5da886a01c6f9199..61557dbcda8b18e5fa9d605666b06a9cc1a19d66 100644
--- a/src/qir/driver/sql/QIRSQLStringVisitor.java
+++ b/src/qir/driver/sql/QIRSQLStringVisitor.java
@@ -83,25 +83,25 @@ class QIRSQLStringVisitor extends QIRTranslator<String> {
     }
 
     @Override
-    public final String visit(final QIRGroupBy qirGroup) {
-        if (qirGroup.getGroup() instanceof QIRLambda)
-            return qirGroup.getChild().accept(this) + " group by " + ((QIRLambda) qirGroup.getGroup()).getBody().accept(this);
-        throw new QIRException("QIR SQL GroupBy not implemented for group type: " + qirGroup.getGroup().getClass());
+    public final String visit(final QIRGroupBy qirGroupBy) {
+        if (qirGroupBy.getGroup() instanceof QIRLambda)
+            return qirGroupBy.getChild().accept(this) + " group by " + ((QIRLambda) qirGroupBy.getGroup()).getBody().accept(this);
+        throw new QIRException("QIR SQL GroupBy not implemented for group type: " + qirGroupBy.getGroup().getClass());
     }
 
     @Override
-    public final String visit(final QIRSortBy qirOrder) {
-        if (qirOrder.getOrder() instanceof QIRLambda && ((QIRLambda) qirOrder.getOrder()).getBody() instanceof QIRLcons && qirOrder.getIsAscending() instanceof QIRLambda &&
-                        ((QIRLambda) qirOrder.getIsAscending()).getBody() instanceof QIRLcons) {
-            QIRNode rows = ((QIRLambda) qirOrder.getOrder()).getBody();
-            QIRNode ascs = ((QIRLambda) qirOrder.getIsAscending()).getBody();
+    public final String visit(final QIRSortBy qirSortBy) {
+        if (qirSortBy.getSort() instanceof QIRLambda && ((QIRLambda) qirSortBy.getSort()).getBody() instanceof QIRLcons && qirSortBy.getIsAscending() instanceof QIRLambda &&
+                        ((QIRLambda) qirSortBy.getIsAscending()).getBody() instanceof QIRLcons) {
+            QIRNode rows = ((QIRLambda) qirSortBy.getSort()).getBody();
+            QIRNode ascs = ((QIRLambda) qirSortBy.getIsAscending()).getBody();
             String o = ((QIRLcons) rows).getValue().accept(this) + (((QIRLcons) ascs).getValue().equals(QIRBoolean.TRUE) ? "" : " desc");
 
             for (rows = ((QIRLcons) rows).getTail(), ascs = ((QIRLcons) ascs).getTail(); rows instanceof QIRLcons; rows = ((QIRLcons) rows).getTail(), ascs = ((QIRLcons) ascs).getTail())
                 o += ", " + ((QIRLcons) rows).getValue().accept(this) + (((QIRLcons) ascs).getValue().equals(QIRBoolean.TRUE) ? "" : " desc");
-            return "select * from (" + qirOrder.getChild().accept(this) + ") as " + ((QIRLambda) qirOrder.getOrder()).getVar().accept(this) + " order by " + o;
+            return "select * from (" + qirSortBy.getChild().accept(this) + ") as " + ((QIRLambda) qirSortBy.getSort()).getVar().accept(this) + " order by " + o;
         }
-        throw new QIRException("QIR SQL OrderBy not implemented for: " + qirOrder.getOrder().getClass() + " and " + qirOrder.getIsAscending().getClass());
+        throw new QIRException("QIR SQL SortBy not implemented for: " + qirSortBy.getSort().getClass() + " and " + qirSortBy.getIsAscending().getClass());
     }
 
     @Override
@@ -332,7 +332,7 @@ class QIRSQLStringVisitor extends QIRTranslator<String> {
 
     @Override
     public String visit(final QIRRdestr qirTdestr) {
-        QIRNode tuple = qirTdestr.getTuple();
+        QIRNode tuple = qirTdestr.getRecord();
 
         if (tuple instanceof QIRVariable)
             return qirTdestr.toString();
diff --git a/src/qir/parser/Parser.frame b/src/qir/parser/Parser.frame
index d66a70ae4b57f7e74c2baf26133e515c707af9a8..aa301329d98cae04157ba26ff93b32a4ef60ff29 100644
--- a/src/qir/parser/Parser.frame
+++ b/src/qir/parser/Parser.frame
@@ -32,7 +32,6 @@ import java.util.List;
 import java.util.ArrayList;
 import java.util.Map;
 import java.util.HashMap;
-import java.util.Arrays;
 
 import com.oracle.truffle.api.frame.FrameDescriptor;
 import com.oracle.truffle.api.source.Source;
diff --git a/src/qir/parser/Parser.java b/src/qir/parser/Parser.java
index cfc86b4228880a60f4634edca0257ff83dddf07e..74920a97a7e89b2dddf3cceae1bc708e0b1b0a62 100644
--- a/src/qir/parser/Parser.java
+++ b/src/qir/parser/Parser.java
@@ -5,7 +5,6 @@ import java.util.List;
 import java.util.ArrayList;
 import java.util.Map;
 import java.util.HashMap;
-import java.util.Arrays;
 
 import com.oracle.truffle.api.frame.FrameDescriptor;
 import com.oracle.truffle.api.source.Source;
@@ -26,7 +25,7 @@ public class Parser {
 	public static final int _stringLiteral = 2;
 	public static final int _numberLiteral = 3;
 	public static final int _doubleLiteral = 4;
-	public static final int maxT = 59;
+	public static final int maxT = 58;
 
 	static final boolean _T = true;
 	static final boolean _x = false;
@@ -283,32 +282,32 @@ public class Parser {
 			res = result; 
 			break;
 		}
-		case 40: {
+		case 39: {
 			QIRList result = Listcons();
 			res = result; 
 			break;
 		}
-		case 42: {
+		case 41: {
 			QIRLdestr result = Listdestr();
 			res = result; 
 			break;
 		}
-		case 45: {
+		case 44: {
 			QIRExternal result = External();
 			res = result; 
 			break;
 		}
-		case 46: {
+		case 45: {
 			QIRTable result = Table();
 			res = result; 
 			break;
 		}
-		case 47: {
+		case 46: {
 			QIRBigNumber result = Bignumber();
 			res = result; 
 			break;
 		}
-		case 48: case 49: {
+		case 47: case 48: {
 			QIRBoolean result = Boolean();
 			res = result; 
 			break;
@@ -323,7 +322,7 @@ public class Parser {
 			res = result; 
 			break;
 		}
-		case 50: {
+		case 49: {
 			QIRNull result = Null();
 			res = result; 
 			break;
@@ -352,42 +351,42 @@ public class Parser {
 			res = new ExternalOperator(srcFromToken(srcToken), opName.val, args); 
 			break;
 		}
-		case 51: {
+		case 50: {
 			QIRProject result = Project();
 			res = result; 
 			break;
 		}
-		case 52: {
+		case 51: {
 			QIRScan result = Scan();
 			res = result; 
 			break;
 		}
-		case 53: {
+		case 52: {
 			QIRFilter result = Filter();
 			res = result; 
 			break;
 		}
-		case 54: {
+		case 53: {
 			QIRGroupBy result = Groupby();
 			res = result; 
 			break;
 		}
-		case 55: {
+		case 54: {
 			QIRSortBy result = Sortby();
 			res = result; 
 			break;
 		}
-		case 56: {
+		case 55: {
 			QIRJoin result = Join();
 			res = result; 
 			break;
 		}
-		case 57: {
+		case 56: {
 			QIRLeftJoin result = Leftjoin();
 			res = result; 
 			break;
 		}
-		case 58: {
+		case 57: {
 			QIRRightJoin result = Rightjoin();
 			res = result; 
 			break;
@@ -398,7 +397,7 @@ public class Parser {
 			Expect(20);
 			break;
 		}
-		default: SynErr(60); break;
+		default: SynErr(59); break;
 		}
 		return res;
 	}
@@ -415,7 +414,7 @@ public class Parser {
 		Expect(21);
 		final Token srcToken = t;
 		Token funName = null;
-		QIRType type = new QIRFunctionType(Arrays.asList(QIRConstantType.ANY, QIRConstantType.ANY)); 
+		QIRType type = new QIRFunctionType(new QIRType[]{QIRAnyType.getInstance()}, QIRAnyType.getInstance()); 
 		if (la.kind == 22) {
 			Get();
 			Expect(1);
@@ -451,7 +450,7 @@ public class Parser {
 		res = QIRRnil.getInstance();
 		final Token srcToken = t;
 		Token idToken;
-		QIRType type = QIRConstantType.ANY; 
+		QIRType type = QIRAnyType.getInstance(); 
 		if (la.kind == 1) {
 			Get();
 			idToken = t; 
@@ -478,9 +477,9 @@ public class Parser {
 
 	QIRList  Listcons() {
 		QIRList  res;
-		Expect(40);
+		Expect(39);
 		res = QIRLnil.getInstance();
-		QIRType type = QIRConstantType.ANY;
+		QIRType type = QIRAnyType.getInstance();
 		final Token srcToken = t; 
 		if (StartOf(1)) {
 			QIRNode value = QIR();
@@ -491,7 +490,7 @@ public class Parser {
 				res = new QIRLcons(srcFromToken(srcToken), value, res); 
 			}
 		}
-		Expect(41);
+		Expect(40);
 		if (la.kind == 23) {
 			Get();
 			type = ListType();
@@ -502,11 +501,11 @@ public class Parser {
 
 	QIRLdestr  Listdestr() {
 		QIRLdestr  res;
-		Expect(42);
+		Expect(41);
 		final Token srcToken = t; 
 		QIRNode list = QIR();
+		Expect(42);
 		Expect(43);
-		Expect(44);
 		Expect(24);
 		QIRNode ifEmpty = QIR();
 		Expect(27);
@@ -519,7 +518,7 @@ public class Parser {
 
 	QIRExternal  External() {
 		QIRExternal  res;
-		Expect(45);
+		Expect(44);
 		final Token srcToken = t; 
 		Expect(1);
 		final Token eName = t; 
@@ -530,7 +529,7 @@ public class Parser {
 
 	QIRTable  Table() {
 		QIRTable  res;
-		Expect(46);
+		Expect(45);
 		final Token srcToken = t; 
 		QIRNode tableName = QIR();
 		Expect(19);
@@ -546,7 +545,7 @@ public class Parser {
 
 	QIRBigNumber  Bignumber() {
 		QIRBigNumber  res;
-		Expect(47);
+		Expect(46);
 		final Token srcToken = t; 
 		Expect(3);
 		final Token number = t; 
@@ -558,13 +557,13 @@ public class Parser {
 	QIRBoolean  Boolean() {
 		QIRBoolean  res;
 		res = null; 
-		if (la.kind == 48) {
+		if (la.kind == 47) {
 			Get();
 			res = QIRBoolean.TRUE; 
-		} else if (la.kind == 49) {
+		} else if (la.kind == 48) {
 			Get();
 			res = QIRBoolean.FALSE; 
-		} else SynErr(61);
+		} else SynErr(60);
 		return res;
 	}
 
@@ -584,7 +583,7 @@ public class Parser {
 
 	QIRNull  Null() {
 		QIRNull  res;
-		Expect(50);
+		Expect(49);
 		res = QIRNull.getInstance(); 
 		return res;
 	}
@@ -598,7 +597,7 @@ public class Parser {
 
 	QIRProject  Project() {
 		QIRProject  res;
-		Expect(51);
+		Expect(50);
 		final Token srcToken = t; 
 		QIRNode formatter = QIR();
 		Expect(19);
@@ -610,7 +609,7 @@ public class Parser {
 
 	QIRScan  Scan() {
 		QIRScan  res;
-		Expect(52);
+		Expect(51);
 		final Token srcToken = t; 
 		QIRNode child = QIR();
 		Expect(20);
@@ -620,7 +619,7 @@ public class Parser {
 
 	QIRFilter  Filter() {
 		QIRFilter  res;
-		Expect(53);
+		Expect(52);
 		final Token srcToken = t; 
 		QIRNode filter = QIR();
 		Expect(19);
@@ -632,7 +631,7 @@ public class Parser {
 
 	QIRGroupBy  Groupby() {
 		QIRGroupBy  res;
-		Expect(54);
+		Expect(53);
 		final Token srcToken = t; 
 		QIRNode group = QIR();
 		Expect(19);
@@ -644,21 +643,21 @@ public class Parser {
 
 	QIRSortBy  Sortby() {
 		QIRSortBy  res;
-		Expect(55);
+		Expect(54);
 		final Token srcToken = t; 
-		QIRNode order = QIR();
+		QIRNode sort = QIR();
 		Expect(19);
 		QIRNode isAscending = QIR();
 		Expect(19);
 		QIRNode child = QIR();
 		Expect(20);
-		res = new QIRSortBy(srcFromToken(srcToken), order, isAscending, child); 
+		res = new QIRSortBy(srcFromToken(srcToken), sort, isAscending, child); 
 		return res;
 	}
 
 	QIRJoin  Join() {
 		QIRJoin  res;
-		Expect(56);
+		Expect(55);
 		final Token srcToken = t; 
 		QIRNode filter = QIR();
 		Expect(19);
@@ -672,7 +671,7 @@ public class Parser {
 
 	QIRLeftJoin  Leftjoin() {
 		QIRLeftJoin  res;
-		Expect(57);
+		Expect(56);
 		final Token srcToken = t; 
 		QIRNode filter = QIR();
 		Expect(19);
@@ -686,7 +685,7 @@ public class Parser {
 
 	QIRRightJoin  Rightjoin() {
 		QIRRightJoin  res;
-		Expect(58);
+		Expect(57);
 		final Token srcToken = t; 
 		QIRNode filter = QIR();
 		Expect(19);
@@ -709,7 +708,7 @@ public class Parser {
 			QIRType type = NonFunctionType();
 			types.add(type); 
 		}
-		res = types.size() == 1 ? first : new QIRFunctionType(types); 
+		res = types.size() == 1 ? first : new QIRFunctionType(types.subList(0, types.size() - 1).toArray(new QIRType[types.size() - 1]), types.get(types.size() - 1)); 
 		return res;
 	}
 
@@ -744,13 +743,13 @@ public class Parser {
 			res = ConstantType();
 		} else if (la.kind == 28) {
 			res = RecordType();
-		} else if (la.kind == 39) {
+		} else if (la.kind == 38) {
 			res = ListType();
 		} else if (la.kind == 18) {
 			Get();
 			res = Type();
 			Expect(20);
-		} else SynErr(62);
+		} else SynErr(61);
 		return res;
 	}
 
@@ -760,50 +759,45 @@ public class Parser {
 		switch (la.kind) {
 		case 32: {
 			Get();
-			res = QIRConstantType.ANY; 
+			res = QIRAnyType.getInstance(); 
 			break;
 		}
 		case 33: {
 			Get();
-			res = QIRConstantType.NUMBER; 
+			res = QIRNumberType.getInstance(); 
 			break;
 		}
 		case 34: {
 			Get();
-			res = QIRConstantType.BIG_NUMBER; 
+			res = QIRBigNumberType.getInstance(); 
 			break;
 		}
 		case 35: {
 			Get();
-			res = QIRConstantType.DOUBLE; 
+			res = QIRDoubleType.getInstance(); 
 			break;
 		}
 		case 36: {
 			Get();
-			res = QIRConstantType.BOOLEAN; 
+			res = QIRBooleanType.getInstance(); 
 			break;
 		}
 		case 37: {
 			Get();
-			res = QIRConstantType.STRING; 
-			break;
-		}
-		case 38: {
-			Get();
-			res = QIRConstantType.NULL; 
+			res = QIRStringType.getInstance(); 
 			break;
 		}
-		default: SynErr(63); break;
+		default: SynErr(62); break;
 		}
 		return res;
 	}
 
 	QIRListType  ListType() {
 		QIRListType  res;
+		Expect(38);
 		Expect(39);
-		Expect(40);
 		QIRType type = Type();
-		Expect(41);
+		Expect(40);
 		res = new QIRListType(type); 
 		return res;
 	}
@@ -822,9 +816,9 @@ public class Parser {
 	}
 
 	private static final boolean[][] set = {
-		{_T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x},
-		{_x,_T,_T,_T, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_T,_T, _x,_x,_T,_x, _x,_T,_x,_x, _x,_T,_x,_x, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_x,_T,_x, _x,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_x, _x},
-		{_x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_T,_T,_T, _T,_T,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x}
+		{_T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x},
+		{_x,_T,_T,_T, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_T,_T, _x,_x,_T,_x, _x,_T,_x,_x, _x,_T,_x,_x, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _x,_T,_x,_x, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_x,_x},
+		{_x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_T,_T,_T, _T,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x}
 
 	};
 
@@ -895,32 +889,31 @@ class Errors {
 			case 35: s = "\"double\" expected"; break;
 			case 36: s = "\"bool\" expected"; break;
 			case 37: s = "\"string\" expected"; break;
-			case 38: s = "\"null\" expected"; break;
-			case 39: s = "\"list\" expected"; break;
-			case 40: s = "\"[\" expected"; break;
-			case 41: s = "\"]\" expected"; break;
-			case 42: s = "\"match\" expected"; break;
-			case 43: s = "\"with\" expected"; break;
-			case 44: s = "\"empty\" expected"; break;
-			case 45: s = "\"External(\" expected"; break;
-			case 46: s = "\"Table(\" expected"; break;
-			case 47: s = "\"BigNumber(\" expected"; break;
-			case 48: s = "\"true\" expected"; break;
-			case 49: s = "\"false\" expected"; break;
-			case 50: s = "\"NULL\" expected"; break;
-			case 51: s = "\"%Project(\" expected"; break;
-			case 52: s = "\"%Scan(\" expected"; break;
-			case 53: s = "\"%Filter(\" expected"; break;
-			case 54: s = "\"%GroupBy(\" expected"; break;
-			case 55: s = "\"%SortBy(\" expected"; break;
-			case 56: s = "\"%Join(\" expected"; break;
-			case 57: s = "\"%LeftJoin(\" expected"; break;
-			case 58: s = "\"%RightJoin(\" expected"; break;
-			case 59: s = "??? expected"; break;
-			case 60: s = "invalid Expression"; break;
-			case 61: s = "invalid Boolean"; break;
-			case 62: s = "invalid NonFunctionType"; break;
-			case 63: s = "invalid ConstantType"; break;
+			case 38: s = "\"list\" expected"; break;
+			case 39: s = "\"[\" expected"; break;
+			case 40: s = "\"]\" expected"; break;
+			case 41: s = "\"match\" expected"; break;
+			case 42: s = "\"with\" expected"; break;
+			case 43: s = "\"empty\" expected"; break;
+			case 44: s = "\"External(\" expected"; break;
+			case 45: s = "\"Table(\" expected"; break;
+			case 46: s = "\"BigNumber(\" expected"; break;
+			case 47: s = "\"true\" expected"; break;
+			case 48: s = "\"false\" expected"; break;
+			case 49: s = "\"NULL\" expected"; break;
+			case 50: s = "\"%Project(\" expected"; break;
+			case 51: s = "\"%Scan(\" expected"; break;
+			case 52: s = "\"%Filter(\" expected"; break;
+			case 53: s = "\"%GroupBy(\" expected"; break;
+			case 54: s = "\"%SortBy(\" expected"; break;
+			case 55: s = "\"%Join(\" expected"; break;
+			case 56: s = "\"%LeftJoin(\" expected"; break;
+			case 57: s = "\"%RightJoin(\" expected"; break;
+			case 58: s = "??? expected"; break;
+			case 59: s = "invalid Expression"; break;
+			case 60: s = "invalid Boolean"; break;
+			case 61: s = "invalid NonFunctionType"; break;
+			case 62: s = "invalid ConstantType"; break;
 			default: s = "error " + n; break;
 		}
 		printMsg(line, col, s);
diff --git a/src/qir/parser/Scanner.java b/src/qir/parser/Scanner.java
index ae6983c1eaa22f5dd26566dc6a1f13a0cde62390..e379b5987a5cb12a3ecb8a8be1232efc6f385523 100644
--- a/src/qir/parser/Scanner.java
+++ b/src/qir/parser/Scanner.java
@@ -253,8 +253,8 @@ class StartStates {
 public class Scanner {
 	static final char EOL = '\n';
 	static final int  eofSym = 0;
-	static final int maxT = 59;
-	static final int noSym = 59;
+	static final int maxT = 58;
+	static final int noSym = 58;
 
 
 	public Buffer buffer; // scanner buffer
@@ -323,14 +323,13 @@ public class Scanner {
 		literals.put("double", new Integer(35));
 		literals.put("bool", new Integer(36));
 		literals.put("string", new Integer(37));
-		literals.put("null", new Integer(38));
-		literals.put("list", new Integer(39));
-		literals.put("match", new Integer(42));
-		literals.put("with", new Integer(43));
-		literals.put("empty", new Integer(44));
-		literals.put("true", new Integer(48));
-		literals.put("false", new Integer(49));
-		literals.put("NULL", new Integer(50));
+		literals.put("list", new Integer(38));
+		literals.put("match", new Integer(41));
+		literals.put("with", new Integer(42));
+		literals.put("empty", new Integer(43));
+		literals.put("true", new Integer(47));
+		literals.put("false", new Integer(48));
+		literals.put("NULL", new Integer(49));
 
 	}
 	
@@ -554,15 +553,15 @@ public class Scanner {
 				case 33:
 					{t.kind = 31; break loop;}
 				case 34:
-					{t.kind = 40; break loop;}
+					{t.kind = 39; break loop;}
 				case 35:
-					{t.kind = 41; break loop;}
+					{t.kind = 40; break loop;}
 				case 36:
-					{t.kind = 45; break loop;}
+					{t.kind = 44; break loop;}
 				case 37:
-					{t.kind = 46; break loop;}
+					{t.kind = 45; break loop;}
 				case 38:
-					{t.kind = 47; break loop;}
+					{t.kind = 46; break loop;}
 				case 39:
 					if (ch == 'r') {AddCh(); state = 40; break;}
 					else {state = 0; break;}
@@ -585,7 +584,7 @@ public class Scanner {
 					if (ch == '(') {AddCh(); state = 46; break;}
 					else {state = 0; break;}
 				case 46:
-					{t.kind = 51; break loop;}
+					{t.kind = 50; break loop;}
 				case 47:
 					if (ch == 'a') {AddCh(); state = 48; break;}
 					else {state = 0; break;}
@@ -596,7 +595,7 @@ public class Scanner {
 					if (ch == '(') {AddCh(); state = 50; break;}
 					else {state = 0; break;}
 				case 50:
-					{t.kind = 52; break loop;}
+					{t.kind = 51; break loop;}
 				case 51:
 					if (ch == 'i') {AddCh(); state = 52; break;}
 					else {state = 0; break;}
@@ -616,7 +615,7 @@ public class Scanner {
 					if (ch == '(') {AddCh(); state = 57; break;}
 					else {state = 0; break;}
 				case 57:
-					{t.kind = 53; break loop;}
+					{t.kind = 52; break loop;}
 				case 58:
 					if (ch == 'r') {AddCh(); state = 59; break;}
 					else {state = 0; break;}
@@ -639,7 +638,7 @@ public class Scanner {
 					if (ch == '(') {AddCh(); state = 65; break;}
 					else {state = 0; break;}
 				case 65:
-					{t.kind = 54; break loop;}
+					{t.kind = 53; break loop;}
 				case 66:
 					if (ch == 'r') {AddCh(); state = 67; break;}
 					else {state = 0; break;}
@@ -656,7 +655,7 @@ public class Scanner {
 					if (ch == '(') {AddCh(); state = 71; break;}
 					else {state = 0; break;}
 				case 71:
-					{t.kind = 55; break loop;}
+					{t.kind = 54; break loop;}
 				case 72:
 					if (ch == 'o') {AddCh(); state = 73; break;}
 					else {state = 0; break;}
@@ -670,7 +669,7 @@ public class Scanner {
 					if (ch == '(') {AddCh(); state = 76; break;}
 					else {state = 0; break;}
 				case 76:
-					{t.kind = 56; break loop;}
+					{t.kind = 55; break loop;}
 				case 77:
 					if (ch == 'e') {AddCh(); state = 78; break;}
 					else {state = 0; break;}
@@ -696,7 +695,7 @@ public class Scanner {
 					if (ch == '(') {AddCh(); state = 85; break;}
 					else {state = 0; break;}
 				case 85:
-					{t.kind = 57; break loop;}
+					{t.kind = 56; break loop;}
 				case 86:
 					if (ch == 'i') {AddCh(); state = 87; break;}
 					else {state = 0; break;}
@@ -725,7 +724,7 @@ public class Scanner {
 					if (ch == '(') {AddCh(); state = 95; break;}
 					else {state = 0; break;}
 				case 95:
-					{t.kind = 58; break loop;}
+					{t.kind = 57; break loop;}
 				case 96:
 					recEnd = pos; recKind = 11;
 					if (ch >= '1' && ch <= '9') {AddCh(); state = 12; break;}
diff --git a/src/qir/parser/qir.atg b/src/qir/parser/qir.atg
index b9e35ee272ad9aad91c9b2b5a45cd5f1e42d55cc..2934101c9ef59ace663d3a7751d17ee16306a35d 100644
--- a/src/qir/parser/qir.atg
+++ b/src/qir/parser/qir.atg
@@ -153,7 +153,7 @@ identifier																	(. res = new QIRVariable(srcFromToken(t), t.val); .)
 Lambda<out QIRLambda res> =
 "fun"																		(. final Token srcToken = t;
 																			   Token funName = null;
-																			   QIRType type = new QIRFunctionType(Arrays.asList(QIRConstantType.ANY, QIRConstantType.ANY)); .)
+																			   QIRType type = new QIRFunctionType(new QIRType[]{QIRAnyType.getInstance()}, QIRAnyType.getInstance()); .)
 [ "#" identifier															(. funName = t; .)
 ] [ ":" Type<out type> ] Variable<out QIRVariable var> "->"
 QIR<out QIRNode body>														(. res = new QIRLambda(srcFromToken(srcToken), funName == null ? null : funName.val, type, var, body, new FrameDescriptor()); .)
@@ -169,7 +169,7 @@ Tuplecons<out QIRRecord res> =
 "{"																			(. res = QIRRnil.getInstance();
 																			   final Token srcToken = t;
 																			   Token idToken;
-																			   QIRType type = QIRConstantType.ANY; .)
+																			   QIRType type = QIRAnyType.getInstance(); .)
 [
 identifier																	(. idToken = t; .)
 "=" QIR<out QIRNode value>													(. res = new QIRRcons(srcFromToken(srcToken), idToken.val, value, res); .)
@@ -184,7 +184,7 @@ Type<out QIRType res> =														(. res = null; .)
 NonFunctionType<out QIRType first>											(. final List<QIRType> types = new ArrayList<>();
 																			   types.add(first); .)
 { "->" NonFunctionType<out QIRType type>									(. types.add(type); .)
-}																			(. res = types.size() == 1 ? first : new QIRFunctionType(types); .)
+}																			(. res = types.size() == 1 ? first : new QIRFunctionType(types.subList(0, types.size() - 1).toArray(new QIRType[types.size() - 1]), types.get(types.size() - 1)); .)
 )
 .
 
@@ -199,13 +199,12 @@ ConstantType<out res>
 
 ConstantType<out QIRType res> =												(. res = null; .)
 (
-"any"																		(. res = QIRConstantType.ANY; .)
-| "int"																		(. res = QIRConstantType.NUMBER; .)
-| "big_int"																	(. res = QIRConstantType.BIG_NUMBER; .)
-| "double"																	(. res = QIRConstantType.DOUBLE; .)
-| "bool"																	(. res = QIRConstantType.BOOLEAN; .)
-| "string"																	(. res = QIRConstantType.STRING; .)
-| "null"																	(. res = QIRConstantType.NULL; .)
+"any"																		(. res = QIRAnyType.getInstance(); .)
+| "int"																		(. res = QIRNumberType.getInstance(); .)
+| "big_int"																	(. res = QIRBigNumberType.getInstance(); .)
+| "double"																	(. res = QIRDoubleType.getInstance(); .)
+| "bool"																	(. res = QIRBooleanType.getInstance(); .)
+| "string"																	(. res = QIRStringType.getInstance(); .)
 )
 .
 
@@ -226,7 +225,7 @@ ListType<out QIRListType res> =
 
 Listcons<out QIRList res> =
 "["																			(. res = QIRLnil.getInstance();
-																			   QIRType type = QIRConstantType.ANY;
+																			   QIRType type = QIRAnyType.getInstance();
 																			   final Token srcToken = t; .)
 [
 QIR<out QIRNode value>														(. res = new QIRLcons(srcFromToken(srcToken), value, res); .)
@@ -305,8 +304,8 @@ QIR<out QIRNode group> "," QIR<out QIRNode child> ')'						(. res = new QIRGroup
 
 Sortby<out QIRSortBy res> =
 "%SortBy("																	(. final Token srcToken = t; .)
-QIR<out QIRNode order> "," QIR<out QIRNode isAscending>
-"," QIR<out QIRNode child> ')'												(. res = new QIRSortBy(srcFromToken(srcToken), order, isAscending, child); .)
+QIR<out QIRNode sort> "," QIR<out QIRNode isAscending>
+"," QIR<out QIRNode child> ')'												(. res = new QIRSortBy(srcFromToken(srcToken), sort, isAscending, child); .)
 .
 
 Join<out QIRJoin res> =
diff --git a/src/qir/types/QIRAnyType.java b/src/qir/types/QIRAnyType.java
new file mode 100644
index 0000000000000000000000000000000000000000..962ccd597a1c06505d1d951dd6e41ea605caeee8
--- /dev/null
+++ b/src/qir/types/QIRAnyType.java
@@ -0,0 +1,22 @@
+package qir.types;
+
+public class QIRAnyType extends QIRType {
+    protected QIRAnyType() {
+    }
+
+    private static final QIRAnyType instance = new QIRAnyType();
+
+    public static final QIRAnyType getInstance() {
+        return instance;
+    }
+
+    @Override
+    public String toString() {
+        return "Any";
+    }
+
+    @Override
+    protected boolean isSubtypeOfSpecific(final QIRType other) {
+        return other == instance;
+    }
+}
diff --git a/src/qir/types/QIRBigNumberType.java b/src/qir/types/QIRBigNumberType.java
new file mode 100644
index 0000000000000000000000000000000000000000..892d596ea6e82136f0b852d15d80b82f515d8f23
--- /dev/null
+++ b/src/qir/types/QIRBigNumberType.java
@@ -0,0 +1,17 @@
+package qir.types;
+
+public class QIRBigNumberType extends QIRConstantType {
+    private QIRBigNumberType() {
+    }
+
+    private static final QIRBigNumberType instance = new QIRBigNumberType();
+
+    public static final QIRBigNumberType getInstance() {
+        return instance;
+    }
+
+    @Override
+    public String toString() {
+        return "BigNumber";
+    }
+}
diff --git a/src/qir/types/QIRBooleanType.java b/src/qir/types/QIRBooleanType.java
new file mode 100644
index 0000000000000000000000000000000000000000..7105515a05989cf0e2509df102371b86be40bf38
--- /dev/null
+++ b/src/qir/types/QIRBooleanType.java
@@ -0,0 +1,17 @@
+package qir.types;
+
+public class QIRBooleanType extends QIRConstantType {
+    private QIRBooleanType() {
+    }
+
+    private static final QIRBooleanType instance = new QIRBooleanType();
+
+    public static final QIRBooleanType getInstance() {
+        return instance;
+    }
+
+    @Override
+    public String toString() {
+        return "Boolean";
+    }
+}
diff --git a/src/qir/types/QIRBottomType.java b/src/qir/types/QIRBottomType.java
new file mode 100644
index 0000000000000000000000000000000000000000..1e7e8083e245b890b1280f5f548dc537d7ddbe20
--- /dev/null
+++ b/src/qir/types/QIRBottomType.java
@@ -0,0 +1,22 @@
+package qir.types;
+
+public class QIRBottomType extends QIRType {
+    private QIRBottomType() {
+    }
+
+    private static final QIRBottomType instance = new QIRBottomType();
+
+    public static final QIRBottomType getInstance() {
+        return instance;
+    }
+
+    @Override
+    public String toString() {
+        return "Bottom";
+    }
+
+    @Override
+    protected boolean isSubtypeOfSpecific(final QIRType other) {
+        return true;
+    }
+}
diff --git a/src/qir/types/QIRConstantType.java b/src/qir/types/QIRConstantType.java
index 38e2f67541847a6ba9a66dea16180979f31c5e9a..52142c029084c91ca4314014d129eee188d92c22 100644
--- a/src/qir/types/QIRConstantType.java
+++ b/src/qir/types/QIRConstantType.java
@@ -1,11 +1,8 @@
 package qir.types;
 
-public enum QIRConstantType implements QIRType {
-    ANY,
-    NUMBER,
-    BIG_NUMBER,
-    DOUBLE,
-    BOOLEAN,
-    STRING,
-    NULL
+public abstract class QIRConstantType extends QIRType {
+    @Override
+    protected final boolean isSubtypeOfSpecific(final QIRType other) {
+        return this == other;
+    }
 }
diff --git a/src/qir/types/QIRDoubleType.java b/src/qir/types/QIRDoubleType.java
new file mode 100644
index 0000000000000000000000000000000000000000..6852b979c55731014ea1d2157a28917a11acee08
--- /dev/null
+++ b/src/qir/types/QIRDoubleType.java
@@ -0,0 +1,17 @@
+package qir.types;
+
+public class QIRDoubleType extends QIRConstantType {
+    private QIRDoubleType() {
+    }
+
+    private static final QIRDoubleType instance = new QIRDoubleType();
+
+    public static final QIRDoubleType getInstance() {
+        return instance;
+    }
+
+    @Override
+    public String toString() {
+        return "Double";
+    }
+}
diff --git a/src/qir/types/QIRFunctionType.java b/src/qir/types/QIRFunctionType.java
index d48fea9f06307a20b2cde018ab833d184172821b..3104e5a3953d4c0194c66623d266aa8f99f9758b 100644
--- a/src/qir/types/QIRFunctionType.java
+++ b/src/qir/types/QIRFunctionType.java
@@ -1,22 +1,46 @@
 package qir.types;
 
-import java.util.ArrayList;
-import java.util.List;
+import java.util.Arrays;
 import java.util.stream.Collectors;
 
-public final class QIRFunctionType implements QIRType {
-    private final List<QIRType> types;
+public final class QIRFunctionType extends QIRType {
+    private final QIRType[] domain;
+    private final QIRType returnType;
 
-    public QIRFunctionType(final List<QIRType> types) {
-        this.types = types;
+    public QIRFunctionType(final QIRType[] domain, final QIRType returnType) {
+        this.domain = domain;
+        this.returnType = returnType;
     }
 
-    public final List<QIRType> getDomain() {
-        return types.size() > 0 ? types.subList(0, types.size() - 1) : new ArrayList<>();
+    public static final QIRFunctionType ANY(final int domainSize) {
+        QIRType[] domain = new QIRType[domainSize];
+        Arrays.fill(domain, QIRAnyType.getInstance());
+        return new QIRFunctionType(domain, QIRAnyType.getInstance());
+    }
+
+    public final QIRType[] getDomain() {
+        return domain;
+    }
+
+    public final QIRType getReturnType() {
+        return returnType;
     }
 
     @Override
     public final String toString() {
-        return types.stream().map(Object::toString).collect(Collectors.joining(" -> "));
+        return Arrays.stream(domain).map(Object::toString).collect(Collectors.joining(" -> ")) + " -> " + returnType;
+    }
+
+    @Override
+    protected final boolean isSubtypeOfSpecific(final QIRType other) {
+        if (!(other instanceof QIRFunctionType))
+            return false;
+        final QIRFunctionType o = (QIRFunctionType) other;
+        if (domain.length != o.domain.length)
+            return false;
+        for (int i = 0; i < domain.length; i++)
+            if (!domain[i].isSubtypeOf(o.domain[i]))
+                return false;
+        return returnType.isSubtypeOf(o.returnType);
     }
 }
diff --git a/src/qir/types/QIRListType.java b/src/qir/types/QIRListType.java
index 8db46f2b4288ed4f142aedb1f208567e1d4a6475..e8fdaaa82eb356e78edc0c974228f1dc47d2b7c4 100644
--- a/src/qir/types/QIRListType.java
+++ b/src/qir/types/QIRListType.java
@@ -1,14 +1,25 @@
 package qir.types;
 
-public class QIRListType implements QIRType {
-    private QIRType type;
+public class QIRListType extends QIRType {
+    private QIRType elementType;
 
-    public QIRListType(final QIRType type) {
-        this.type = type;
+    public QIRListType(final QIRType elementType) {
+        this.elementType = elementType;
+    }
+
+    public static final QIRListType ANY = new QIRListType(QIRAnyType.getInstance());
+
+    public final QIRType getElementType() {
+        return elementType;
     }
 
     @Override
     public final String toString() {
-        return type + " list";
+        return elementType + " list";
+    }
+
+    @Override
+    protected boolean isSubtypeOfSpecific(final QIRType other) {
+        return other instanceof QIRListType && elementType.isSubtypeOf(((QIRListType) other).elementType);
     }
 }
diff --git a/src/qir/types/QIRNumberType.java b/src/qir/types/QIRNumberType.java
new file mode 100644
index 0000000000000000000000000000000000000000..00b4e7899d8017459c6adc163997c181760e8f5b
--- /dev/null
+++ b/src/qir/types/QIRNumberType.java
@@ -0,0 +1,17 @@
+package qir.types;
+
+public class QIRNumberType extends QIRConstantType {
+    private QIRNumberType() {
+    }
+
+    private static final QIRNumberType instance = new QIRNumberType();
+
+    public static final QIRNumberType getInstance() {
+        return instance;
+    }
+
+    @Override
+    public String toString() {
+        return "Number";
+    }
+}
diff --git a/src/qir/types/QIRRecordType.java b/src/qir/types/QIRRecordType.java
index 21ae6a0a97a8901192b23cdde668d2cc947a6056..1f6f7fd1d6d3323bffccb15076e9adfd8f928bd6 100644
--- a/src/qir/types/QIRRecordType.java
+++ b/src/qir/types/QIRRecordType.java
@@ -1,17 +1,38 @@
 package qir.types;
 
+import java.util.HashMap;
 import java.util.Map;
 import java.util.stream.Collectors;
 
-public class QIRRecordType implements QIRType {
-    private Map<String, QIRType> types;
+public class QIRRecordType extends QIRType {
+    private Map<String, QIRType> fieldTypes;
 
-    public QIRRecordType(final Map<String, QIRType> types) {
-        this.types = types;
+    public QIRRecordType(final Map<String, QIRType> fieldTypes) {
+        this.fieldTypes = fieldTypes;
+    }
+
+    public static final QIRRecordType ANY = new QIRRecordType(new HashMap<>());
+    public static final QIRRecordType BOTTOM = new QIRRecordType(new HashMap<>());
+
+    public final Map<String, QIRType> getFieldTypes() {
+        return fieldTypes;
     }
 
     @Override
     public final String toString() {
-        return "{ " + types.entrySet().stream().map(e -> e.getKey() + " : " + e.getValue()).collect(Collectors.joining(" ; ")) + " }";
+        return "{ " + fieldTypes.entrySet().stream().map(e -> e.getKey() + " : " + e.getValue()).collect(Collectors.joining(" ; ")) + " }";
+    }
+
+    @Override
+    protected boolean isSubtypeOfSpecific(QIRType other) {
+        if (this == BOTTOM)
+            return true;
+        if (!(other instanceof QIRRecordType))
+            return false;
+        final Map<String, QIRType> otherTypes = ((QIRRecordType) other).fieldTypes;
+        for (String key : otherTypes.keySet())
+            if (!fieldTypes.containsKey(key) || !otherTypes.get(key).isSubtypeOf(fieldTypes.get(key)))
+                return false;
+        return true;
     }
 }
diff --git a/src/qir/types/QIRSomeType.java b/src/qir/types/QIRSomeType.java
new file mode 100644
index 0000000000000000000000000000000000000000..a4f3133aa576dc41e26d5231698f0b8634a18c83
--- /dev/null
+++ b/src/qir/types/QIRSomeType.java
@@ -0,0 +1,31 @@
+package qir.types;
+
+import java.util.Optional;
+
+public class QIRSomeType extends QIRType {
+    private static int idGen = 0;
+
+    private final int id;
+    private Optional<QIRType> infered = Optional.empty();
+
+    public QIRSomeType() {
+        this.id = idGen++;
+    }
+
+    public QIRType getInferedType() {
+        return infered.isPresent() ? infered.get() : this;
+    }
+
+    @Override
+    public String toString() {
+        return infered.isPresent() ? infered.get() + "('a" + id + ")" : "'a" + id;
+    }
+
+    @Override
+    protected boolean isSubtypeOfSpecific(final QIRType other) {
+        if (infered.isPresent() && !other.isSubtypeOf(infered.get()))
+            return false;
+        infered = Optional.of(other);
+        return true;
+    }
+}
diff --git a/src/qir/types/QIRStringType.java b/src/qir/types/QIRStringType.java
new file mode 100644
index 0000000000000000000000000000000000000000..8f12160d2d11858d740e261077adfae7c65c8d49
--- /dev/null
+++ b/src/qir/types/QIRStringType.java
@@ -0,0 +1,17 @@
+package qir.types;
+
+public class QIRStringType extends QIRConstantType {
+    private QIRStringType() {
+    }
+
+    private static final QIRStringType instance = new QIRStringType();
+
+    public static final QIRStringType getInstance() {
+        return instance;
+    }
+
+    @Override
+    public String toString() {
+        return "String";
+    }
+}
diff --git a/src/qir/types/QIRType.java b/src/qir/types/QIRType.java
index 6f004382fc74cca4cfe5db2494a0ec1692a71755..17e4b1b8b8e69415c780670c5e502ecd7a1a8883 100644
--- a/src/qir/types/QIRType.java
+++ b/src/qir/types/QIRType.java
@@ -1,4 +1,9 @@
 package qir.types;
 
-public interface QIRType {
+public abstract class QIRType {
+    protected abstract boolean isSubtypeOfSpecific(final QIRType other);
+
+    public final boolean isSubtypeOf(final QIRType other) {
+        return other == QIRAnyType.getInstance() || isSubtypeOfSpecific(other);
+    }
 }
diff --git a/src/qir/typing/QIRDefaultTypeSystemVisitor.java b/src/qir/typing/QIRDefaultTypeSystemVisitor.java
new file mode 100644
index 0000000000000000000000000000000000000000..e2ca78accc0e774960445883d91adb840be3805e
--- /dev/null
+++ b/src/qir/typing/QIRDefaultTypeSystemVisitor.java
@@ -0,0 +1,267 @@
+package qir.typing;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import qir.ast.QIRApply;
+import qir.ast.QIRDBNode;
+import qir.ast.QIRExportableTruffleNode;
+import qir.ast.QIRExternal;
+import qir.ast.QIRIf;
+import qir.ast.QIRLambda;
+import qir.ast.QIRUnexportableTruffleNode;
+import qir.ast.QIRVariable;
+import qir.ast.data.QIRLcons;
+import qir.ast.data.QIRLdestr;
+import qir.ast.data.QIRLnil;
+import qir.ast.data.QIRRcons;
+import qir.ast.data.QIRRdestr;
+import qir.ast.data.QIRRnil;
+import qir.ast.data.QIRTable;
+import qir.ast.expression.QIRBigNumber;
+import qir.ast.expression.QIRBinaryNode;
+import qir.ast.expression.QIRBoolean;
+import qir.ast.expression.QIRDouble;
+import qir.ast.expression.QIRNull;
+import qir.ast.expression.QIRNumber;
+import qir.ast.expression.QIRString;
+import qir.ast.expression.arithmetic.QIRDiv;
+import qir.ast.expression.arithmetic.QIRMinus;
+import qir.ast.expression.arithmetic.QIRMod;
+import qir.ast.expression.arithmetic.QIRPlus;
+import qir.ast.expression.arithmetic.QIRStar;
+import qir.ast.expression.logic.QIRAnd;
+import qir.ast.expression.logic.QIRNot;
+import qir.ast.expression.logic.QIROr;
+import qir.ast.expression.relational.QIREqual;
+import qir.ast.expression.relational.QIRLowerOrEqual;
+import qir.ast.expression.relational.QIRLowerThan;
+import qir.ast.operator.QIRFilter;
+import qir.ast.operator.QIRGroupBy;
+import qir.ast.operator.QIRJoin;
+import qir.ast.operator.QIRLeftJoin;
+import qir.ast.operator.QIRLimit;
+import qir.ast.operator.QIRProject;
+import qir.ast.operator.QIRRightJoin;
+import qir.ast.operator.QIRScan;
+import qir.ast.operator.QIRSortBy;
+import qir.types.QIRAnyType;
+import qir.types.QIRBigNumberType;
+import qir.types.QIRBooleanType;
+import qir.types.QIRDoubleType;
+import qir.types.QIRFunctionType;
+import qir.types.QIRListType;
+import qir.types.QIRNumberType;
+import qir.types.QIRRecordType;
+import qir.types.QIRSomeType;
+import qir.types.QIRStringType;
+import qir.types.QIRType;
+
+public class QIRDefaultTypeSystemVisitor extends QIRTypeSystemVisitor<QIRType> {
+    public final QIRType visit(final QIRProject qirProject) {
+        final QIRListType childType = expectIfSubtype(qirProject.getChild().accept(this), QIRListType.ANY);
+        final QIRFunctionType formatterType = expectIfSubtype(qirProject.getFormatter().accept(this), new QIRFunctionType(new QIRType[]{childType.getElementType()}, QIRAnyType.getInstance()));
+
+        return new QIRListType(formatterType.getReturnType());
+    }
+
+    public final QIRType visit(final QIRScan qirScan) {
+        throw new QIRTypeErrorException(this.getClass(), qirScan);
+    }
+
+    public final QIRType visit(final QIRFilter qirFilter) {
+        final QIRListType childType = expectIfSubtype(qirFilter.getChild().accept(this), QIRListType.ANY);
+
+        checkSubtype(qirFilter.getFilter().accept(this), new QIRFunctionType(new QIRType[]{childType.getElementType()}, QIRBooleanType.getInstance()));
+        return new QIRListType(childType.getElementType());
+    }
+
+    public final QIRType visit(final QIRGroupBy qirGroupBy) {
+        throw new QIRTypeErrorException(this.getClass(), qirGroupBy);
+    }
+
+    public final QIRType visit(final QIRSortBy qirSortBy) {
+        throw new QIRTypeErrorException(this.getClass(), qirSortBy);
+    }
+
+    public final QIRType visit(final QIRJoin qirJoin) {
+        throw new QIRTypeErrorException(this.getClass(), qirJoin);
+    }
+
+    public final QIRType visit(final QIRLeftJoin qirJoin) {
+        throw new QIRTypeErrorException(this.getClass(), qirJoin);
+    }
+
+    public final QIRType visit(final QIRRightJoin qirJoin) {
+        throw new QIRTypeErrorException(this.getClass(), qirJoin);
+    }
+
+    public final QIRType visit(final QIRLimit qirLimit) {
+        throw new QIRTypeErrorException(this.getClass(), qirLimit);
+    }
+
+    public final <DBRepr> QIRType visit(final QIRDBNode<DBRepr> qirDBNode) {
+        throw new QIRTypeErrorException(this.getClass(), qirDBNode);
+    }
+
+    public final QIRType visit(final QIRExternal qirExternal) {
+        throw new QIRTypeErrorException(this.getClass(), qirExternal);
+    }
+
+    public final QIRType visit(final QIRVariable qirVariable) {
+        if (env.containsKey(qirVariable.id))
+            return env.get(qirVariable.id);
+        throw new QIRTypeErrorException(this.getClass(), qirVariable);
+    }
+
+    public final QIRType visit(final QIRLambda qirLambda) {
+        final QIRType varType = new QIRSomeType();
+        env.put(qirLambda.getVar().id, varType);
+        return new QIRFunctionType(new QIRType[]{varType}, qirLambda.getBody().accept(this));
+    }
+
+    public final QIRType visit(final QIRApply qirApply) {
+        final QIRType rightType = qirApply.getRight().accept(this);
+        return expectIfSubtype(qirApply.getLeft().accept(this), new QIRFunctionType(new QIRType[]{rightType}, QIRAnyType.getInstance())).getReturnType();
+    }
+
+    public final QIRType visit(final QIRIf qirIf) {
+        checkSubtype(qirIf.getCondition().accept(this), QIRBooleanType.getInstance());
+
+        return expectCommonType(qirIf.getThenNode().accept(this), qirIf.getElseNode().accept(this));
+    }
+
+    private final QIRType visitBinaryArithmetic(final QIRBinaryNode qirBinary) {
+        final QIRType left = qirBinary.getLeft().accept(this);
+
+        if (!left.isSubtypeOf(QIRNumberType.getInstance()) && !left.isSubtypeOf(QIRBigNumberType.getInstance()) && !left.isSubtypeOf(QIRDoubleType.getInstance()))
+            throw new QIRTypeErrorException(this.getClass(), new QIRType[]{QIRNumberType.getInstance(), QIRBigNumberType.getInstance(), QIRDoubleType.getInstance()}, left);
+        return expectCommonType(left, qirBinary.getRight().accept(this));
+    }
+
+    private final QIRType visitBinaryLogic(final QIRBinaryNode qirBinary) {
+        checkCommonType(qirBinary.getLeft().accept(this), qirBinary.getRight().accept(this));
+        return QIRBooleanType.getInstance();
+    }
+
+    public final QIRType visit(final QIRPlus qirPlus) {
+        return visitBinaryArithmetic(qirPlus);
+    }
+
+    public final QIRType visit(final QIRMinus qirMinus) {
+        return visitBinaryArithmetic(qirMinus);
+    }
+
+    public final QIRType visit(final QIRStar qirStar) {
+        return visitBinaryArithmetic(qirStar);
+    }
+
+    public final QIRType visit(final QIRDiv qirDiv) {
+        return visitBinaryArithmetic(qirDiv);
+    }
+
+    public final QIRType visit(final QIRMod qirMod) {
+        return visitBinaryArithmetic(qirMod);
+    }
+
+    public final QIRType visit(final QIRAnd qirAnd) {
+        return visitBinaryLogic(qirAnd);
+    }
+
+    public final QIRType visit(final QIROr qirOr) {
+        return visitBinaryLogic(qirOr);
+    }
+
+    public final QIRType visit(final QIREqual qirEqual) {
+        return visitBinaryLogic(qirEqual);
+    }
+
+    public final QIRType visit(final QIRLowerOrEqual qirLowerOrEqual) {
+        return visitBinaryLogic(qirLowerOrEqual);
+    }
+
+    public final QIRType visit(final QIRLowerThan qirLowerThan) {
+        return visitBinaryLogic(qirLowerThan);
+    }
+
+    public final QIRType visit(final QIRNot qirNot) {
+        final QIRType exprType = qirNot.getExpr().accept(this);
+        checkSubtype(exprType, QIRBooleanType.getInstance());
+        return exprType;
+    }
+
+    public final QIRType visit(final QIRTable qirTable) {
+        throw new QIRTypeErrorException(this.getClass(), qirTable);
+    }
+
+    public final QIRType visit(final QIRLnil qirLnil) {
+        return QIRListType.ANY;
+    }
+
+    public final QIRType visit(final QIRLcons qirLcons) {
+        final QIRListType tailType = expectIfSubtype(qirLcons.getTail().accept(this), QIRListType.ANY);
+
+        return new QIRListType(expectIfSubtype(qirLcons.getValue().accept(this), tailType.getElementType()));
+    }
+
+    public final QIRType visit(final QIRLdestr qirLdestr) {
+        final QIRListType listType = expectIfSubtype(qirLdestr.getList().accept(this), QIRListType.ANY);
+        final QIRType returnType = qirLdestr.getIfEmpty().accept(this);
+
+        checkSubtype(qirLdestr.getHandler().accept(this), new QIRFunctionType(new QIRType[]{listType.getElementType(), listType}, returnType));
+        return returnType;
+    }
+
+    public final QIRType visit(final QIRRnil qirRnil) {
+        return QIRRecordType.ANY;
+    }
+
+    public final QIRType visit(final QIRRcons qirRcons) {
+        final QIRRecordType tailType = expectIfSubtype(qirRcons.getTail().accept(this), QIRRecordType.ANY);
+
+        final Map<String, QIRType> fieldTypes = new HashMap<>();
+        tailType.getFieldTypes().entrySet().forEach(e -> fieldTypes.put(e.getKey(), e.getValue()));
+        fieldTypes.put(qirRcons.getId(), qirRcons.getValue().accept(this));
+        return new QIRRecordType(fieldTypes);
+    }
+
+    public final QIRType visit(final QIRRdestr qirRdestr) {
+        final String colName = qirRdestr.getColName();
+        final Map<String, QIRType> expectedRecordFields = new HashMap<>();
+
+        expectedRecordFields.put(colName, new QIRSomeType());
+        return expectIfSubtype(qirRdestr.getRecord().accept(this), new QIRRecordType(expectedRecordFields)).getFieldTypes().get(colName);
+    }
+
+    public final QIRType visit(final QIRString qirString) {
+        return QIRStringType.getInstance();
+    }
+
+    public final QIRType visit(final QIRNumber qirNumber) {
+        return QIRNumberType.getInstance();
+    }
+
+    public final QIRType visit(final QIRBigNumber qirBigNumber) {
+        return QIRBigNumberType.getInstance();
+    }
+
+    public final QIRType visit(final QIRDouble qirDouble) {
+        return QIRDoubleType.getInstance();
+    }
+
+    public final QIRType visit(final QIRBoolean qirBoolean) {
+        return QIRBooleanType.getInstance();
+    }
+
+    public final QIRType visit(final QIRNull qirNull) {
+        return QIRAnyType.getInstance();
+    }
+
+    public final QIRType visit(final QIRExportableTruffleNode qirTruffleNode) {
+        throw new QIRTypeErrorException(this.getClass(), qirTruffleNode);
+    }
+
+    public final QIRType visit(final QIRUnexportableTruffleNode qirTruffleNode) {
+        throw new QIRTypeErrorException(this.getClass(), qirTruffleNode);
+    }
+}
diff --git a/src/qir/typing/QIRTypeErrorException.java b/src/qir/typing/QIRTypeErrorException.java
new file mode 100644
index 0000000000000000000000000000000000000000..b52718adf4d1d0476d8da7bef89a35d18412aaf9
--- /dev/null
+++ b/src/qir/typing/QIRTypeErrorException.java
@@ -0,0 +1,34 @@
+package qir.typing;
+
+import java.util.Arrays;
+import java.util.stream.Collectors;
+
+import qir.ast.QIRNode;
+import qir.ast.QIRVariable;
+import qir.types.QIRRecordType;
+import qir.types.QIRType;
+import qir.util.QIRException;
+
+public class QIRTypeErrorException extends QIRException {
+    private static final long serialVersionUID = 2089121923670702478L;
+
+    public QIRTypeErrorException(final Class<?> typer, final QIRType expected, final QIRType actual) {
+        super(typer.getName() + " type error. Expected: " + expected + ", got: " + actual);
+    }
+
+    public QIRTypeErrorException(final Class<?> typer, final QIRType[] expected, final QIRType actual) {
+        super(typer.getName() + " type error. Expected: " + Arrays.stream(expected).map(Object::toString).collect(Collectors.joining(" or ")) + ", got: " + actual);
+    }
+
+    public QIRTypeErrorException(final Class<?> typer, final QIRNode unknown) {
+        super(typer.getName() + " could not type unknown expression " + unknown);
+    }
+
+    public QIRTypeErrorException(final Class<?> typer, final QIRVariable freeVariable) {
+        super(typer.getName() + " could not type free variable " + freeVariable);
+    }
+
+    public QIRTypeErrorException(final Class<?> typer, final QIRRecordType recordType, final String unknownColName) {
+        super(typer.getName() + " could not type column name " + unknownColName + " in record type " + recordType);
+    }
+}
diff --git a/src/qir/typing/QIRTypeSystemVisitor.java b/src/qir/typing/QIRTypeSystemVisitor.java
new file mode 100644
index 0000000000000000000000000000000000000000..390fea25b36db31f1950943ead3835ea6269fb16
--- /dev/null
+++ b/src/qir/typing/QIRTypeSystemVisitor.java
@@ -0,0 +1,36 @@
+package qir.typing;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import qir.driver.IQIRVisitor;
+import qir.types.QIRSomeType;
+import qir.types.QIRType;
+
+public abstract class QIRTypeSystemVisitor<T> implements IQIRVisitor<T> {
+    protected final Map<String, QIRType> env = new HashMap<>();
+
+    protected void checkSubtype(final QIRType actual, final QIRType expected) {
+        if (!actual.isSubtypeOf(expected))
+            throw new QIRTypeErrorException(this.getClass(), expected, actual);
+    }
+
+    @SuppressWarnings("unchecked")
+    protected <U extends QIRType> U expectIfSubtype(final QIRType actual, final U expected) {
+        checkSubtype(actual, expected);
+        return (U) (actual instanceof QIRSomeType ? ((QIRSomeType) actual).getInferedType() : actual);
+    }
+
+    protected void checkCommonType(final QIRType actual, final QIRType expected) {
+        if (!actual.isSubtypeOf(expected) && !expected.isSubtypeOf(actual))
+            throw new QIRTypeErrorException(this.getClass(), expected, actual);
+    }
+
+    protected QIRType expectCommonType(final QIRType actual, final QIRType expected) {
+        if (actual.isSubtypeOf(expected))
+            return expected;
+        if (expected.isSubtypeOf(actual))
+            return actual;
+        throw new QIRTypeErrorException(this.getClass(), expected, actual);
+    }
+}
diff --git a/src/qir/util/QIRException.java b/src/qir/util/QIRException.java
index 19fcf7980d8bce8d17202679e2c99a4df37710e5..294642ffceb6a571d3676f3e7c61ead4a6c137d7 100644
--- a/src/qir/util/QIRException.java
+++ b/src/qir/util/QIRException.java
@@ -3,11 +3,10 @@ package qir.util;
 /**
  * {@link QIRException} can be thrown by any module of QIR.
  */
-public final class QIRException extends RuntimeException {
+public class QIRException extends RuntimeException {
     private static final long serialVersionUID = -8609737632453908968L;
 
     public QIRException(final String message) {
         super(message);
-        initCause(new Throwable("Java stack trace"));
     }
 }