Repository: gluon-lang/gluon
Pull request #188 (2016-10-16 03:37:29)

Use rows for variant constructors
Like extensible records, you can't actually do much with these at the moment, but it _does_ simplify the internal representation somewhat.


Patch:
diff --git a/base/src/types.rs b/base/src/types.rs
index 700d5d97..1cab6924 100644
--- a/base/src/types.rs
+++ b/base/src/types.rs
@@ -222,25 +222,18 @@ pub enum Type<Id, T = ArcType<Id>> {
     Hole,
     /// An opaque type
     Opaque,
-    /// An application with multiple arguments.
-    /// `Map String Int` would be represented as `App(Map, [String, Int])`
-    App(T, Vec<T>),
-    /// A variant type `| A Int Float | B`.
-    /// The second element of the tuple is the function type which the constructor has which in the
-    /// above example means that A's type is `Int -> Float -> A` and B's is `B`
-    Variants(Vec<(Id, T)>),
-    /// Representation for type variables
-    Variable(TypeVariable),
-    /// Variant for "generic" variables. These occur in signatures as lowercase identifers `a`, `b`
-    /// etc and are what unbound type variables are eventually made into.
-    Generic(Generic<Id>),
     /// A builtin type
     Builtin(BuiltinType),
-    /// A record type
+    /// A type application with multiple arguments. For example,
+    /// `Map String Int` would be represented as `App(Map, [String, Int])`.
+    App(T, Vec<T>),
+    /// Record constructor, of kind `Row -> Type`
     Record(T),
-    /// The empty row
+    /// Variant constructor, of kind `Row -> Type`
+    Variant(T),
+    /// The empty row, of kind `Row`
     EmptyRow,
-    /// Row extension
+    /// Row extension, of kind `... -> Row -> Row`
     ExtendRow {
         /// The associated types of this record type
         types: Vec<Field<Id, Alias<Id, T>>>,
@@ -249,8 +242,19 @@ pub enum Type<Id, T = ArcType<Id>> {
         /// The rest of the row
         rest: T,
     },
-    /// An identifier type. Anything that is not a builtin type.
+    /// An identifier type. These are created during parsing, but should all be
+    /// resolved into `Type::Alias`es during type checking.
+    ///
+    /// Identifiers are also sometimes used inside aliased types to avoid cycles
+    /// in reference counted pointers. This is a bit of a wart at the moment and
+    /// _may_ cause spurious unification failures.
     Ident(Id),
+    /// An unbound type variable that may be unified with other types. These
+    /// will eventually be converted into `Type::Generic`s during generalization.
+    Variable(TypeVariable),
+    /// A variable that needs to be instantiated with a fresh type variable
+    /// when the binding is refered to.
+    Generic(Generic<Id>),
     Alias(AliasData<Id, T>),
 }
 
@@ -277,8 +281,12 @@ impl<Id, T> Type<Id, T>
         }
     }
 
-    pub fn variants(vs: Vec<(Id, T)>) -> T {
-        T::from(Type::Variants(vs))
+    pub fn variant(fields: Vec<Field<Id, T>>) -> T {
+        Type::poly_variant(fields, Type::empty_row())
+    }
+
+    pub fn poly_variant(fields: Vec<Field<Id, T>>, rest: T) -> T {
+        T::from(Type::Variant(Type::extend_row(Vec::new(), fields, rest)))
     }
 
     pub fn record(types: Vec<Field<Id, Alias<Id, T>>>, fields: Vec<Field<Id, T>>) -> T {
@@ -461,8 +469,8 @@ impl<Id> ArcType<Id> {
         }
     }
 
-    pub fn field_iter(&self) -> FieldIterator<Self> {
-        FieldIterator {
+    pub fn row_iter(&self) -> RowIterator<Self> {
+        RowIterator {
             typ: self,
             current: 0,
         }
@@ -514,18 +522,18 @@ impl<'a, Id: 'a, T> Iterator for TypeFieldIterator<'a, T>
 }
 
 #[derive(Clone)]
-pub struct FieldIterator<'a, T: 'a> {
+pub struct RowIterator<'a, T: 'a> {
     typ: &'a T,
     current: usize,
 }
 
-impl<'a, T> FieldIterator<'a, T> {
+impl<'a, T> RowIterator<'a, T> {
     pub fn current_type(&self) -> &'a T {
         self.typ
     }
 }
 
-impl<'a, Id: 'a, T> Iterator for FieldIterator<'a, T>
+impl<'a, Id: 'a, T> Iterator for RowIterator<'a, T>
     where T: Deref<Target = Type<Id, T>>,
 {
     type Item = &'a Field<Id, T>;
@@ -750,23 +758,31 @@ impl<'a, I, T, E> DisplayType<'a, I, T, E>
                     }
                 }
             }
-            Type::Variants(ref variants) => {
+            Type::Variant(ref row) => {
                 let mut first = true;
                 let mut doc = arena.nil();
-                for variant in variants {
-                    if !first {
-                        doc = doc.append(arena.newline());
-                    }
-                    first = false;
-                    doc = doc.append("| ")
-                        .append(variant.0.as_ref());
-                    for arg in arg_iter(&variant.1) {
-                        doc = chain![arena;
-                                     doc,
-                                     " ",
-                                     dt(self.env, Prec::Constructor, &arg).pretty(arena)];
+
+                match **row {
+                    Type::EmptyRow => (),
+                    Type::ExtendRow { ref fields, .. } => {
+                        for field in fields.iter() {
+                            if !first {
+                                doc = doc.append(arena.newline());
+                            }
+                            first = false;
+                            doc = doc.append("| ")
+                                .append(field.name.as_ref());
+                            for arg in arg_iter(&field.typ) {
+                                doc = chain![arena;
+                                            doc,
+                                            " ",
+                                            dt(self.env, Prec::Constructor, &arg).pretty(arena)];
+                            }
+                        }
                     }
-                }
+                    ref typ => panic!("Unexpected type `{}` in variant", typ),
+                };
+
                 enclose(p, Prec::Constructor, arena, doc).group()
             }
             Type::Builtin(ref t) => arena.text(t.to_str()),
@@ -902,7 +918,8 @@ pub fn walk_type_<I, T, F: ?Sized>(typ: &T, f: &mut F)
                 f.walk(a);
             }
         }
-        Type::Record(ref row) => f.walk(row),
+        Type::Record(ref row) |
+        Type::Variant(ref row) => f.walk(row),
         Type::ExtendRow { ref types, ref fields, ref rest } => {
             for field in types {
                 f.walk(&field.typ.typ);
@@ -912,11 +929,6 @@ pub fn walk_type_<I, T, F: ?Sized>(typ: &T, f: &mut F)
             }
             f.walk(rest);
         }
-        Type::Variants(ref variants) => {
-            for variant in variants {
-                f.walk(&variant.1);
-            }
-        }
         Type::Hole |
         Type::Opaque |
         Type::Builtin(_) |
@@ -1029,6 +1041,7 @@ pub fn walk_move_type_opt<F: ?Sized, I, T>(typ: &Type<I, T>, f: &mut F) -> Optio
             merge(id, f.visit(id), args, new_args, Type::app)
         }
         Type::Record(ref row) => f.visit(row).map(|row| T::from(Type::Record(row))),
+        Type::Variant(ref row) => f.visit(row).map(|row| T::from(Type::Variant(row))),
         Type::ExtendRow { ref types, ref fields, ref rest } => {
             let new_fields = walk_move_types(fields, |field| {
                 f.visit(&field.typ).map(|typ| {
@@ -1045,10 +1058,6 @@ pub fn walk_move_type_opt<F: ?Sized, I, T>(typ: &Type<I, T>, f: &mut F) -> Optio
                   new_rest,
                   |fields, rest| Type::extend_row(types.clone(), fields, rest))
         }
-        Type::Variants(ref variants) => {
-            walk_move_types(variants, |v| f.visit(&v.1).map(|t| (v.0.clone(), t)))
-                .map(Type::variants)
-        }
         Type::Hole |
         Type::Opaque |
         Type::Builtin(_) |
diff --git a/check/src/completion.rs b/check/src/completion.rs
index 1022a120..1732bb29 100644
--- a/check/src/completion.rs
+++ b/check/src/completion.rs
@@ -101,7 +101,7 @@ impl<E: TypeEnv> OnFound for Suggest<E> {
         if let Expr::Projection(ref expr, _, _) = context.value {
             let typ = instantiate::remove_aliases(&self.env, expr.env_type_of(&self.env));
             let id = ident.as_ref();
-            for field in typ.field_iter() {
+            for field in typ.row_iter() {
                 if field.name.as_ref().starts_with(id) {
                     self.result.push(Suggestion {
                         name: field.name.declared_name().into(),
diff --git a/check/src/kindcheck.rs b/check/src/kindcheck.rs
index 0cbc875a..96af6e58 100644
--- a/check/src/kindcheck.rs
+++ b/check/src/kindcheck.rs
@@ -3,7 +3,7 @@ use std::fmt;
 use base::ast;
 use base::kind::{self, ArcKind, Kind, KindEnv};
 use base::symbol::Symbol;
-use base::types::{self, ArcType, BuiltinType, Generic, Type, Walker};
+use base::types::{self, ArcType, BuiltinType, Field, Generic, Type, Walker};
 
 use substitution::{Substitution, Substitutable};
 use unify;
@@ -191,16 +191,19 @@ impl<'a> KindCheck<'a> {
                 }
                 Ok((kind, Type::app(ctor, new_args)))
             }
-            Type::Variants(ref variants) => {
-                let variants = try!(variants.iter()
-                    .map(|variant| {
-                        let (kind, typ) = try!(self.kindcheck(&variant.1));
+            Type::Variant(ref row) => {
+                let row = try!(row.row_iter()
+                    .map(|field| {
+                        let (kind, typ) = try!(self.kindcheck(&field.typ));
                         let type_kind = self.type_kind();
                         try!(self.unify(&type_kind, kind));
-                        Ok((variant.0.clone(), typ))
+                        Ok(Field {
+                            name: field.name.clone(),
+                            typ: typ,
+                        })
                     })
                     .collect());
-                Ok((self.type_kind(), Type::variants(variants)))
+                Ok((self.type_kind(), Type::variant(row)))
             }
             Type::Record(ref row) => {
                 let (kind, row) = try!(self.kindcheck(row));
diff --git a/check/src/rename.rs b/check/src/rename.rs
index 4ddeac7f..4e662def 100644
--- a/check/src/rename.rs
+++ b/check/src/rename.rs
@@ -87,7 +87,7 @@ pub fn rename(symbols: &mut SymbolModule,
         fn find_fields(&self, typ: &ArcType) -> Vec<types::Field<Symbol, ArcType>> {
             // Walk through all type aliases
             let record = instantiate::remove_aliases(&self.env, typ.clone());
-            record.field_iter().cloned().collect()
+            record.row_iter().cloned().collect()
         }
 
         fn new_pattern(&mut self, typ: &ArcType, pattern: &mut ast::SpannedPattern<Symbol>) {
@@ -143,9 +143,9 @@ pub fn rename(symbols: &mut SymbolModule,
 
         fn stack_type(&mut self, id: Symbol, span: Span<BytePos>, alias: &Alias<Symbol, ArcType>) {
             // Insert variant constructors into the local scope
-            if let Type::Variants(ref variants) = *alias.typ {
-                for &(ref name, ref typ) in variants {
-                    self.env.stack.insert(name.clone(), (name.clone(), span, typ.clone()));
+            if let Type::Variant(ref row) = *alias.typ {
+                for field in row.row_iter().cloned() {
+                    self.env.stack.insert(field.name.clone(), (field.name, span, field.typ));
                 }
             }
 
diff --git a/check/src/typecheck.rs b/check/src/typecheck.rs
index 175a8910..057b1ee2 100644
--- a/check/src/typecheck.rs
+++ b/check/src/typecheck.rs
@@ -174,7 +174,7 @@ impl<'a> TypeEnv for Environment<'a> {
                 match *alias.typ {
                     Type::Record(_) => {
                         fields.iter()
-                            .all(|name| alias.typ.field_iter().any(|f| f.name.name_eq(name)))
+                            .all(|name| alias.typ.row_iter().any(|f| f.name.name_eq(name)))
                     }
                     _ => false,
                 }
@@ -292,11 +292,11 @@ impl<'a> Typecheck<'a> {
 
     fn stack_type(&mut self, id: Symbol, alias: &Alias<Symbol, ArcType>) {
         // Insert variant constructors into the local scope
-        if let Type::Variants(ref variants) = *alias.typ {
-            for (name, typ) in variants.iter().cloned() {
-                let symbol = self.symbols.symbol(name.as_ref());
-                self.original_symbols.insert(symbol, name.clone());
-                self.stack_var(name, typ);
+        if let Type::Variant(ref row) = *alias.typ {
+            for field in row.row_iter().cloned() {
+                let symbol = self.symbols.symbol(field.name.as_ref());
+                self.original_symbols.insert(symbol, field.name.clone());
+                self.stack_var(field.name, field.typ);
             }
         }
 
@@ -594,7 +594,7 @@ impl<'a> Typecheck<'a> {
                 match *record {
                     Type::Variable(_) |
                     Type::Record(_) => {
-                        let field_type = record.field_iter()
+                        let field_type = record.row_iter()
                             .find(|field| field.name.name_eq(field_id))
                             .map(|field| field.typ.clone());
                         *ast_field_typ = match field_type {
@@ -792,7 +792,7 @@ impl<'a> Typecheck<'a> {
                 for field in fields {
                     let name = field.1.as_ref().unwrap_or(&field.0);
                     // The field should always exist since the type was constructed from the pattern
-                    let field_type = match_type.field_iter()
+                    let field_type = match_type.row_iter()
                         .find(|f| f.name.name_eq(&field.0))
                         .expect("ICE: Expected field to exist in type");
                     self.stack_var(name.clone(), field_type.typ.clone());
@@ -1194,19 +1194,24 @@ impl<'a> Typecheck<'a> {
                             Some(Type::ident(new_id.clone()))
                         })
                 }
-                Type::Variants(ref variants) => {
+                Type::Variant(ref row) => {
                     let iter = || {
-                        variants.iter()
-                            .map(|var| self.original_symbols.get(&var.0))
+                        row.row_iter()
+                            .map(|var| self.original_symbols.get(&var.name))
                     };
                     if iter().any(|opt| opt.is_some()) {
                         // If any of the variants requires a symbol replacement
                         // we create a new type
-                        Some(Type::variants(iter()
-                            .zip(variants.iter())
+                        Some(Type::variant(iter()
+                            .zip(row.row_iter())
                             .map(|(new, old)| {
                                 match new {
-                                    Some(new) => (new.clone(), old.1.clone()),
+                                    Some(new) => {
+                                        Field {
+                                            name: new.clone(),
+                                            typ: old.typ.clone(),
+                                        }
+                                    }
                                     None => old.clone(),
                                 }
                             })
@@ -1333,7 +1338,7 @@ fn with_pattern_types<F>(fields: &[(Symbol, Option<Symbol>)], typ: &ArcType, mut
     for field in fields {
         // If the field in the pattern does not exist (undefined field error) then skip it as
         // the error itself will already have been reported
-        if let Some(associated_type) = typ.field_iter()
+        if let Some(associated_type) = typ.row_iter()
             .find(|type_field| type_field.name.name_eq(&field.0)) {
             f(&field.0, &field.1, &associated_type.typ);
         }
diff --git a/check/src/unify_type.rs b/check/src/unify_type.rs
index 49e66faa..ad16fff2 100644
--- a/check/src/unify_type.rs
+++ b/check/src/unify_type.rs
@@ -322,8 +322,7 @@ fn unify_rows<'a, U>(unifier: &mut UnifierState<'a, U>,
         return Err(UnifyError::TypeMismatch(l.clone(), r.clone()));
     }
 
-    let (missing_from_left, both, missing_from_right) = gather_fields(l.field_iter(),
-                                                                      r.field_iter());
+    let (missing_from_left, both, missing_from_right) = gather_fields(l.row_iter(), r.row_iter());
 
     let mut types: Vec<_> = types_both.iter().map(|pair| pair.0.clone()).collect();
 
@@ -352,7 +351,7 @@ fn unify_rows<'a, U>(unifier: &mut UnifierState<'a, U>,
     // `Row (y : String | Fresh var 2) <=> $0`
 
     // This default `rest` value will only be used on errors, or if both fields has the same fields
-    let mut r_iter = r.field_iter();
+    let mut r_iter = r.row_iter();
     for _ in r_iter.by_ref() {
     }
     let mut rest = r_iter.current_type().clone();
@@ -376,14 +375,14 @@ fn unify_rows<'a, U>(unifier: &mut UnifierState<'a, U>,
                     Type::extend_row(types_missing_from_right, missing_from_right, rest.clone());
                 unifier.try_match(&l_rest, r_iter.current_type());
                 types.extend(l_rest.type_field_iter().cloned());
-                fields.extend(l_rest.field_iter().cloned());
+                fields.extend(l_rest.row_iter().cloned());
             }
         }
     }
 
     // No need to do anything of no fields are missing
     if !missing_from_left.is_empty() {
-        let mut l_iter = l.field_iter();
+        let mut l_iter = l.row_iter();
         for _ in l_iter.by_ref() {
         }
 
@@ -402,7 +401,7 @@ fn unify_rows<'a, U>(unifier: &mut UnifierState<'a, U>,
                     Type::extend_row(types_missing_from_left, missing_from_left, rest.clone());
                 unifier.try_match(&l_iter.current_type(), &r_rest);
                 types.extend(r_rest.type_field_iter().cloned());
-                fields.extend(r_rest.field_iter().cloned());
+                fields.extend(r_rest.row_iter().cloned());
             }
         }
     }
@@ -744,7 +743,7 @@ mod tests {
         match result {
             Ok(result) => {
                 // Get the row variable at the end of the resulting type so we can compare the types
-                let mut iter = result.field_iter();
+                let mut iter = result.row_iter();
                 for _ in iter.by_ref() {
                 }
                 let row_variable = iter.current_type().clone();
diff --git a/parser/src/lib.rs b/parser/src/lib.rs
index 8abdfea9..c4a4fc21 100644
--- a/parser/src/lib.rs
+++ b/parser/src/lib.rs
@@ -262,12 +262,17 @@ impl<'input, I, Id, F> ParserEnv<I, F>
     }
 
     fn parse_adt(&self, return_type: &ArcType<Id>, input: I) -> ParseResult<ArcType<Id>, I> {
-        let variant = (token(Token::Pipe),
-                       self.ident(),
-                       many(self.parser(ParserEnv::<I, F>::type_arg)))
-            .map(|(_, id, args): (_, _, Vec<_>)| (id, Type::function(args, return_type.clone())));
+        let variant =
+            (token(Token::Pipe), self.ident(), many(self.parser(ParserEnv::<I, F>::type_arg)))
+                .map(|(_, id, args): (_, _, Vec<_>)| {
+                    Field {
+                        name: id,
+                        typ: Type::function(args, return_type.clone()),
+                    }
+                });
+
         many1(variant)
-            .map(Type::variants)
+            .map(Type::variant)
             .parse_stream(input)
     }
 
diff --git a/vm/src/compiler.rs b/vm/src/compiler.rs
index 48ffcb57..313dcf79 100644
--- a/vm/src/compiler.rs
+++ b/vm/src/compiler.rs
@@ -256,17 +256,17 @@ impl CompilerEnv for TypeInfos {
             .iter()
             .filter_map(|(_, ref alias)| {
                 match *alias.typ {
-                    Type::Variants(ref variants) => {
-                        variants.iter()
+                    Type::Variant(ref row) => {
+                        row.row_iter()
                             .enumerate()
-                            .find(|&(_, v)| v.0 == *id)
+                            .find(|&(_, field)| field.name == *id)
                     }
                     _ => None,
                 }
             })
             .next()
-            .map(|(tag, &(_, ref typ))| {
-                Variable::Constructor(tag as VmTag, count_function_args(&typ))
+            .map(|(tag, field)| {
+                Variable::Constructor(tag as VmTag, count_function_args(&field.typ))
             })
     }
 }
@@ -329,17 +329,18 @@ impl<'a> Compiler<'a> {
             .iter()
             .filter_map(|(_, typ)| {
                 match **typ {
-                    Type::Variants(ref variants) => {
-                        variants.iter()
+                    Type::Variant(ref row) => {
+                        row.row_iter()
                             .enumerate()
-                            .find(|&(_, v)| v.0 == *id)
+                            .find(|&(_, field)| field.name == *id)
                     }
                     _ => None,
                 }
             })
             .next()
-            .map(|(tag, &(_, ref typ))| {
-                Constructor(tag as VmIndex, types::arg_iter(typ).count() as VmIndex)
+            .map(|(tag, field)| {
+                Constructor(tag as VmIndex,
+                            types::arg_iter(&field.typ).count() as VmIndex)
             })
             .or_else(|| {
                 current.stack
@@ -387,7 +388,7 @@ impl<'a> Compiler<'a> {
     fn find_field(&self, typ: &ArcType, field: &Symbol) -> Option<FieldAccess> {
         // Remove all type aliases to get the actual record type
         let typ = instantiate::remove_aliases_cow(self, typ);
-        let mut iter = typ.field_iter();
+        let mut iter = typ.row_iter();
         match iter.by_ref().position(|f| f.name.name_eq(field)) {
             Some(index) => {
                 for _ in iter.by_ref() {}
@@ -404,10 +405,10 @@ impl<'a> Compiler<'a> {
 
     fn find_tag(&self, typ: &ArcType, constructor: &Symbol) -> Option<VmTag> {
         match **instantiate::remove_aliases_cow(self, typ) {
-            Type::Variants(ref variants) => {
-                variants.iter()
+            Type::Variant(ref row) => {
+                row.row_iter()
                     .enumerate()
-                    .find(|&(_, v)| v.0 == *constructor)
+                    .find(|&(_, field)| field.name == *constructor)
                     .map(|(tag, _)| tag as VmTag)
             }
             _ => None,
@@ -793,7 +794,7 @@ impl<'a> Compiler<'a> {
                 });
                 match *typ {
                     Type::Record(_) => {
-                        let mut field_iter = typ.field_iter();
+                        let mut field_iter = typ.row_iter();
                         let number_of_fields = field_iter.by_ref().count();
                         let is_polymorphic = **field_iter.current_type() != Type::EmptyRow;
                         if fields.len() == 0 ||
@@ -815,7 +816,7 @@ impl<'a> Compiler<'a> {
                             }
                         } else {
                             function.emit(Split);
-                            for field in typ.field_iter() {
+                            for field in typ.row_iter() {
                                 let name = match fields.iter()
                                     .find(|tup| tup.0.name_eq(&field.name)) {
                                     Some(&(ref name, ref bind)) => {
diff --git a/vm/src/types.rs b/vm/src/types.rs
index 2beff072..a363aa1c 100644
--- a/vm/src/types.rs
+++ b/vm/src/types.rs
@@ -173,12 +173,14 @@ impl TypeEnv for TypeInfos {
             .iter()
             .filter_map(|(_, ref alias)| {
                 match *alias.typ {
-                    Type::Variants(ref variants) => variants.iter().find(|v| v.0.as_ref() == id),
+                    Type::Variant(ref row) => {
+                        row.row_iter().find(|field| field.name.as_ref() == id)
+                    }
                     _ => None,
                 }
             })
             .next()
-            .map(|x| &x.1)
+            .map(|field| &field.typ)
     }
 
     fn find_type_info(&self, id: &SymbolRef) -> Option<&Alias<Symbol, ArcType>> {
@@ -194,7 +196,7 @@ impl TypeEnv for TypeInfos {
                 match *alias.typ {
                     Type::Record(_) => {
                         fields.iter().all(|name| {
-                            alias.typ.field_iter().any(|f| f.name.as_ref() == name.as_ref())
+                            alias.typ.row_iter().any(|f| f.name.as_ref() == name.as_ref())
                         })
                     }
                     _ => false,
diff --git a/vm/src/vm.rs b/vm/src/vm.rs
index 1ab0ce3d..4114d6c9 100644
--- a/vm/src/vm.rs
+++ b/vm/src/vm.rs
@@ -140,8 +140,10 @@ impl TypeEnv for VmEnv {
                     .values()
                     .filter_map(|alias| {
                         match *alias.typ {
-                            Type::Variants(ref ctors) => {
-                                ctors.iter().find(|ctor| *ctor.0 == *id).map(|t| &t.1)
+                            Type::Variant(ref row) => {
+                                row.row_iter()
+                                    .find(|field| *field.name == *id)
+                                    .map(|field| &field.typ)
                             }
                             _ => None,
                         }
@@ -258,7 +260,7 @@ impl VmEnv {
             };
             // HACK Can't return the data directly due to the use of cow on the type
             let next_type = map_cow_option(typ.clone(), |typ| {
-                typ.field_iter()
+                typ.row_iter()
                     .enumerate()
                     .find(|&(_, field)| field.name.as_ref() == field_name)
                     .map(|(index, field)| {


Test patch:
diff --git a/base/tests/types.rs b/base/tests/types.rs
index 7599009c..adb7c9e5 100644
--- a/base/tests/types.rs
+++ b/base/tests/types.rs
@@ -174,10 +174,16 @@ fn show_record_multi_line() {
 }
 
 #[test]
-fn variants() {
-    let typ: ArcType<&str> =
-        Type::variants(vec![("A", Type::function(vec![Type::int()], Type::ident("A"))),
-                            ("B", Type::ident("A"))]);
+fn show_variant() {
+    let typ: ArcType<&str> = Type::variant(vec![Field {
+                                                    name: "A",
+                                                    typ: Type::function(vec![Type::int()],
+                                                                        Type::ident("A")),
+                                                },
+                                                Field {
+                                                    name: "B",
+                                                    typ: Type::ident("A"),
+                                                }]);
     assert_eq_display!(format!("{}", typ), "| A Int | B");
 }
 
diff --git a/check/tests/pass.rs b/check/tests/pass.rs
index cc4335df..90024b40 100644
--- a/check/tests/pass.rs
+++ b/check/tests/pass.rs
@@ -309,10 +309,16 @@ let option_Functor: Functor Option = {
 in option_Functor.map (\x -> x #Int- 1) (Some 2)
 ";
     let result = support::typecheck(text);
-    let variants = Type::variants(vec![(support::intern_unscoped("None"), support::typ_a("Option", vec![typ("a")])),
-                                       (support::intern_unscoped("Some"),
-                                        Type::function(vec![typ("a")],
-                                                       support::typ_a("Option", vec![typ("a")])))]);
+    let variants = Type::variant(vec![Field {
+                                          name: support::intern_unscoped("None"),
+                                          typ: support::typ_a("Option", vec![typ("a")]),
+                                      },
+                                      Field {
+                                          name: support::intern_unscoped("Some"),
+                                          typ: Type::function(vec![typ("a")],
+                                                                   support::typ_a("Option",
+                                                                                  vec![typ("a")])),
+                                      }]);
     let option = alias("Option", &["a"], variants);
     let expected = Ok(Type::app(option, vec![typ("Int")]));
 
@@ -347,9 +353,12 @@ test
     let result = support::typecheck(text);
     assert!(result.is_ok(), "{}", result.unwrap_err());
 
-    let variants = Type::variants(vec![(support::intern_unscoped("T"),
-                                        Type::function(vec![typ("a")],
-                                                       support::typ_a("Test", vec![typ("a")])))]);
+    let variants = Type::variant(vec![Field {
+                                          name: support::intern_unscoped("T"),
+                                          typ: Type::function(vec![typ("a")],
+                                                                   support::typ_a("Test",
+                                                                                  vec![typ("a")])),
+                                      }]);
     let expected = Ok(Type::app(alias("Test", &["a"], variants), vec![Type::unit()]));
 
     assert_eq!(result, expected);
@@ -486,7 +495,7 @@ type Test = | Test String Int in { Test, x = 1 }
 "#;
     let result = support::typecheck(text);
     let variant = Type::function(vec![typ("String"), typ("Int")], typ("Test"));
-    let test = Type::variants(vec![(intern("Test"), variant)]);
+    let test = Type::variant(vec![Field { name: intern("Test"), typ: variant }]);
     let types = vec![Field {
         name: support::intern_unscoped("Test"),
         typ: types::Alias::new(intern("Test"), vec![], test),
@@ -527,11 +536,11 @@ return 1
 "#;
     let result = support::typecheck(text);
     let variant = |name| {
-        Type::variants(vec![(
-            intern(name),
-            Type::function(vec![typ("a")],
-            Type::app(typ(name), vec![typ("a")]),
-        ))])
+        Type::variant(vec![Field {
+                               name: intern(name),
+                               typ: Type::function(vec![typ("a")],
+                                                        Type::app(typ(name), vec![typ("a")])),
+                           }])
     };
     let test = alias("Test", &["a"], variant("Test"));
     let m = Generic {
diff --git a/parser/tests/basic.rs b/parser/tests/basic.rs
index cff669f3..6fb4401a 100644
--- a/parser/tests/basic.rs
+++ b/parser/tests/basic.rs
@@ -245,8 +245,7 @@ fn type_decl_record() {
 fn type_mutually_recursive() {
     let _ = ::env_logger::init();
     let e = parse_new!("type Test = | Test Int and Test2 = { x: Int, y: {} } in 1");
-    let test = Type::variants(vec![(intern("Test"),
-                                    Type::function(vec![typ("Int")], typ("Test")))]);
+    let test = Type::variant(vec![field("Test", Type::function(vec![typ("Int")], typ("Test")))]);
     let test2 = Type::record(Vec::new(),
                              vec![Field {
                                       name: intern("x"),
@@ -307,7 +306,7 @@ fn variant_type() {
     assert_eq!(e,
                type_decl(intern("Option"),
                          vec![generic("a")],
-                         Type::variants(vec![(intern("None"), none), (intern("Some"), some)]),
+                         Type::variant(vec![field("None", none), field("Some", some)]),
                          app(id("Some"), vec![int(1)])));
 }
 #[test]