Repository: gluon-lang/gluon
Pull request #296 (2017-06-14 20:36:55)

feat: Add a serializer and deserializer from/to gluon values
This makes it possible to easily marshal values to and from gluon by utilizing serde's serialization and deserialization.


Patch:
diff --git a/Cargo.toml b/Cargo.toml
index c6442ec4..1f422d7c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -46,6 +46,9 @@ skeptic = { version = "0.6", optional = true }
 collect-mac = "0.1.0"
 env_logger = "0.3.4"
 
+serde = "1.0.0"
+serde_derive = "1.0.0"
+
 hyper = "0.11.0"
 curl = "0.4.1"
 
diff --git a/base/src/resolve.rs b/base/src/resolve.rs
index bd37bbd7..61fd248e 100644
--- a/base/src/resolve.rs
+++ b/base/src/resolve.rs
@@ -29,9 +29,34 @@ pub fn remove_aliases_cow<'t>(env: &TypeEnv, typ: &'t ArcType) -> Cow<'t, ArcTyp
     }
 }
 
+pub fn canonical_alias<'t, F>(env: &TypeEnv, typ: &'t ArcType, canonical: F) -> Cow<'t, ArcType>
+where
+    F: Fn(&AliasData<Symbol, ArcType>) -> bool,
+{
+    match peek_alias(env, typ) {
+        Ok(Some(alias)) if !canonical(alias) => {
+            type_of_alias(env, alias, typ.unapplied_args())
+                .map(|typ| {
+                    Cow::Owned(canonical_alias(env, &typ, canonical).into_owned())
+                })
+                .unwrap_or(Cow::Borrowed(typ))
+        }
+        _ => Cow::Borrowed(typ),
+    }
+}
+
 /// Expand `typ` if it is an alias that can be expanded and return the expanded type.
 /// Returns `None` if the type is not an alias or the alias could not be expanded.
 pub fn remove_alias(env: &TypeEnv, typ: &ArcType) -> Result<Option<ArcType>, Error> {
+    Ok(peek_alias(env, typ)?.and_then(|alias| {
+        type_of_alias(env, alias, typ.unapplied_args())
+    }))
+}
+
+pub fn peek_alias<'t>(
+    env: &'t TypeEnv,
+    typ: &'t ArcType,
+) -> Result<Option<&'t AliasData<Symbol, ArcType>>, Error> {
     let maybe_alias = match **typ {
         Type::Alias(ref alias) if alias.args.is_empty() => Some(alias),
         Type::App(ref alias, ref args) => {
@@ -53,8 +78,7 @@ pub fn remove_alias(env: &TypeEnv, typ: &ArcType) -> Result<Option<ArcType>, Err
                     })?
                 }
             };
-
-            Ok(type_of_alias(env, alias, typ.unapplied_args()))
+            Ok(Some(alias))
         }
         None => Ok(None),
     }
diff --git a/base/src/types.rs b/base/src/types.rs
index 9d9eb82c..60614114 100644
--- a/base/src/types.rs
+++ b/base/src/types.rs
@@ -630,6 +630,15 @@ where
             _ => None,
         }
     }
+
+    pub fn is_non_polymorphic_record(&self) -> bool {
+        match *self {
+            Type::Record(ref row) |
+            Type::ExtendRow { rest: ref row, .. } => row.is_non_polymorphic_record(),
+            Type::EmptyRow => true,
+            _ => false,
+        }
+    }
 }
 
 impl<T> Type<Symbol, T>
diff --git a/check/src/completion.rs b/check/src/completion.rs
index 267ec8ea..c9179503 100644
--- a/check/src/completion.rs
+++ b/check/src/completion.rs
@@ -1,6 +1,5 @@
 //! Primitive auto completion and type quering on ASTs
 
-use std::borrow::Borrow;
 use std::iter::once;
 use std::cmp::Ordering;
 
@@ -265,7 +264,7 @@ impl<'a> OnFound for GetMetadata<'a> {
         }
     }
 
-    fn pattern(&mut self, pattern: &SpannedPattern<Symbol>) {}
+    fn pattern(&mut self, _pattern: &SpannedPattern<Symbol>) {}
 
 
     fn nothing(&mut self) {}
@@ -313,7 +312,7 @@ impl<'a, E: TypeEnv> OnFound for MetadataSuggest<'a, E> {
         }
     }
 
-    fn pattern(&mut self, pattern: &SpannedPattern<Symbol>) {}
+    fn pattern(&mut self, _pattern: &SpannedPattern<Symbol>) {}
 
     fn nothing(&mut self) {
         self.result = self.suggest
diff --git a/check/src/lib.rs b/check/src/lib.rs
index f4a8beca..063160b7 100644
--- a/check/src/lib.rs
+++ b/check/src/lib.rs
@@ -36,7 +36,12 @@ pub fn check_signature(env: &TypeEnv, signature: &ArcType, actual: &ArcType) ->
     let subs = Substitution::new(Kind::typ());
     let state = unify_type::State::new(env, &subs);
     let actual = unify_type::instantiate_generic_variables(&mut FnvMap::default(), &subs, actual);
-    unify_type::merge_signature(&subs, &mut ScopedMap::new(), 0, state, signature, &actual).is_ok()
+    let result =
+        unify_type::merge_signature(&subs, &mut ScopedMap::new(), 0, state, signature, &actual);
+    if let Err(ref err) = result {
+        debug!("Check signature error: {}", err);
+    }
+    result.is_ok()
 }
 
 #[cfg(test)]
diff --git a/examples/http.rs b/examples/http.rs
index 8809d4a3..8fca5925 100644
--- a/examples/http.rs
+++ b/examples/http.rs
@@ -113,8 +113,8 @@ impl<'vm> Getable<'vm> for Wrap<StatusCode> {
     fn from_value(_: &'vm Thread, value: Variants) -> Option<Self> {
         use hyper::StatusCode::*;
         match value.as_ref() {
-            ValueRef::Tag(tag) => {
-                Some(Wrap(match tag {
+            ValueRef::Data(data) => {
+                Some(Wrap(match data.tag() {
                     0 => Ok,
                     1 => NotFound,
                     2 => InternalServerError,
diff --git a/vm/Cargo.toml b/vm/Cargo.toml
index 9849544e..b88227ff 100644
--- a/vm/Cargo.toml
+++ b/vm/Cargo.toml
@@ -23,6 +23,7 @@ bitflags = "0.7.0"
 itertools = "0.6.0"
 futures = "0.1.0"
 typed-arena = "1.2.0"
+serde = { version = "1.0.0", optional = true }
 smallvec = "0.2.1"
 
 gluon_base = { path = "../base", version = "0.4.2" } # GLUON
@@ -37,6 +38,7 @@ gluon = { path = "..", version = "0.4.2" } # GLUON
 
 lalrpop-util = "0.13.1"
 regex = "0.2.0"
+serde_derive = "1.0.0"
 
 [features]
-test = ["env_logger", "lalrpop", "gluon_parser"]
+test = ["env_logger", "lalrpop", "gluon_parser", "serde"]
diff --git a/vm/src/api/de.rs b/vm/src/api/de.rs
new file mode 100644
index 00000000..effd7267
--- /dev/null
+++ b/vm/src/api/de.rs
@@ -0,0 +1,834 @@
+//! Gluon -> Rust value conversion via the `serde::Deserialize` trait
+
+use std::fmt;
+use std::marker::PhantomData;
+
+use base::resolve;
+use base::types::{ArcType, arg_iter, BuiltinType, Type, TypeEnv};
+use base::symbol::Symbol;
+
+use {Error as VmError, Result, Variants};
+use api::{Getable, ValueRef, VmType};
+use thread::{Thread, ThreadInternal};
+
+use serde::de::{self, DeserializeSeed, DeserializeOwned, Visitor, SeqAccess, MapAccess,
+                EnumAccess, VariantAccess, IntoDeserializer, Error};
+
+impl de::Error for VmError {
+    fn custom<T>(msg: T) -> Self
+    where
+        T: fmt::Display,
+    {
+        VmError::Message(format!("{}", msg))
+    }
+}
+
+/**
+`Getable` wrapper which extracts `T` by deserializing it into a rust value.
+
+## Struct
+
+```
+#[macro_use]
+extern crate serde_derive;
+
+extern crate gluon;
+#[macro_use]
+extern crate gluon_vm;
+
+use gluon::{Compiler, Thread, new_vm};
+use gluon::base::types::ArcType;
+use gluon::vm::api::VmType;
+use gluon::vm::api::de::De;
+# fn main() {
+
+#[derive(Debug, PartialEq, Deserialize)]
+struct Vec2 {
+    x: f64,
+    y: f64
+}
+
+impl VmType for Vec2 {
+    type Type = Self;
+
+    fn make_type(thread: &Thread) -> ArcType {
+        field_decl!{ x, y }
+        type T = record_type! {
+            x => f64,
+            y => f64
+        };
+        T::make_type(thread)
+    }
+}
+
+let thread = new_vm();
+let (De(vec), _) = Compiler::new()
+#   .implicit_prelude(false)
+    .run_expr::<De<Vec2>>(&thread, "test", "{ x = 1.0, y = 2.0 }")
+    .unwrap_or_else(|err| panic!("{}", err));
+assert_eq!(vec, Vec2 {
+        x: 1.0,
+        y: 2.0
+    });
+
+# }
+```
+
+## Enum
+
+```
+#[macro_use]
+extern crate serde_derive;
+
+extern crate gluon;
+#[macro_use]
+extern crate gluon_vm;
+
+use gluon::{Compiler, Thread, new_vm};
+use gluon::base::types::ArcType;
+use gluon::vm::api::VmType;
+use gluon::vm::api::de::De;
+# fn main() {
+
+#[derive(Debug, PartialEq, Deserialize)]
+enum Enum {
+    A(i32),
+    B { string: String, test: f64 },
+}
+
+impl VmType for Enum {
+    type Type = Self;
+
+    fn make_type(thread: &Thread) -> ArcType {
+        // Use the enum type declared in gluon
+        thread.find_type_info("test.Enum").unwrap().into_type()
+    }
+}
+
+let thread = new_vm();
+Compiler::new()
+#   .implicit_prelude(false)
+    .load_script(
+        &thread,
+        "test",
+        r#" type Enum = | A Int | B String Float in { Enum } "#,
+    )
+    .unwrap_or_else(|err| panic!("{}", err));
+
+let (De(enum_), _) = Compiler::new()
+#   .implicit_prelude(false)
+    .run_expr::<De<Enum>>(
+        &thread,
+        "test",
+        r#" let { Enum } = import! "test" in A 123 "#,
+    )
+    .unwrap_or_else(|err| panic!("{}", err));
+assert_eq!(enum_, Enum::A(123));
+
+// The field names of record variants are ignored so make sure the fields are declared correctly
+let (De(enum_), _) = Compiler::new()
+#   .implicit_prelude(false)
+    .run_expr::<De<Enum>>(
+        &thread,
+        "test",
+        r#" let { Enum } = import! "test" in B "abc" 3.14 "#,
+    )
+    .unwrap_or_else(|err| panic!("{}", err));
+assert_eq!(
+    enum_,
+    Enum::B {
+        string: "abc".to_string(),
+        test: 3.14,
+    }
+);
+
+# }
+```
+*/
+
+pub struct De<T>(pub T);
+
+impl<T> VmType for De<T>
+where
+    T: VmType,
+{
+    type Type = T::Type;
+
+    fn make_type(thread: &Thread) -> ArcType {
+        T::make_type(thread)
+    }
+}
+
+impl<'vm, T> Getable<'vm> for De<T>
+where
+    T: VmType,
+    T: DeserializeOwned,
+{
+    fn from_value(thread: &'vm Thread, value: Variants) -> Option<Self> {
+        let typ = T::make_type(thread);
+        from_value(thread, value, &typ)
+            .map(De)
+            .map_err(|err| {
+                debug!("{}", err);
+                err
+            })
+            .ok()
+    }
+}
+
+/// Deserializes `T` from a gluon value assuming that `value` is of type `typ`.
+pub fn from_value<T>(thread: &Thread, value: Variants, typ: &ArcType) -> Result<T>
+where
+    T: DeserializeOwned,
+{
+    let env = thread.global_env().get_env();
+    let mut deserializer = Deserializer {
+        state: State {
+            thread: thread,
+            env: &*env,
+        },
+        input: value,
+        typ: typ,
+    };
+    T::deserialize(&mut deserializer)
+}
+
+#[derive(Clone)]
+struct State<'de> {
+    thread: &'de Thread,
+    env: &'de TypeEnv,
+}
+
+#[derive(Clone)]
+struct Deserializer<'de, 't> {
+    state: State<'de>,
+    input: Variants<'de>,
+    typ: &'t ArcType,
+}
+
+impl<'de, 't> Deserializer<'de, 't> {
+    fn from_value(
+        thread: &'de Thread,
+        env: &'de TypeEnv,
+        input: Variants<'de>,
+        typ: &'t ArcType,
+    ) -> Self {
+        Deserializer {
+            state: State {
+                thread: thread,
+                env: env,
+            },
+            input: input,
+            typ: typ,
+        }
+    }
+
+    fn deserialize_builtin<T, F, R>(&self, expected: BuiltinType, visit: F) -> Result<R>
+    where
+        F: FnOnce(T) -> Result<R>,
+        T: Getable<'de>,
+    {
+        self.deserialize_leaf(|t| *t == Type::Builtin(expected), visit)
+    }
+
+    fn deserialize_leaf<T, E, F, R>(&self, expected: E, visit: F) -> Result<R>
+    where
+        E: FnOnce(&Type<Symbol, ArcType>) -> bool,
+        F: FnOnce(T) -> Result<R>,
+        T: Getable<'de>,
+    {
+        let typ = resolve::remove_aliases_cow(self.state.env, self.typ);
+        match T::from_value(self.state.thread, self.input) {
+            Some(c) => {
+                if expected(&typ) {
+                    visit(c)
+                } else {
+                    Err(VmError::Message(
+                        format!("Unable to deserialize `{}`", self.typ),
+                    ))
+                }
+            }
+            _ => Err(VmError::Message(
+                format!("Unable to deserialize `{}`", self.typ),
+            )),
+        }
+    }
+}
+
+impl<'de, 't, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de, 't> {
+    type Error = VmError;
+
+    fn deserialize_any<V>(self, visitor: V) -> Result<V::Value>
+    where
+        V: Visitor<'de>,
+    {
+        match self.input.as_ref() {
+            ValueRef::Array(ref array) => {
+                match array.as_slice::<u8>() {
+                    Some(_) => self.deserialize_bytes(visitor),
+                    _ => self.deserialize_seq(visitor),
+                }
+            }
+            ValueRef::Byte(_) => self.deserialize_u8(visitor),
+            ValueRef::Data(_) => {
+                let typ = resolve::remove_aliases_cow(self.state.env, self.typ);
+                let mut deserializer = Deserializer {
+                    typ: &typ,
+                    ..self.clone()
+                };
+                if let Type::Record(_) = **typ {
+                    deserializer.deserialize_enum("", &[], visitor)
+                } else {
+                    deserializer.deserialize_map(visitor)
+                }
+            }
+            ValueRef::Float(_) => self.deserialize_f64(visitor),
+            ValueRef::Int(_) => self.deserialize_i64(visitor),
+            ValueRef::String(ref s) => visitor.visit_borrowed_str(s),
+            ValueRef::Userdata(_) |
+            ValueRef::Internal => {
+                Err(VmError::Message(
+                    format!("Unable to deserialize `{}`", self.typ),
+                ))
+            }
+        }
+    }
+
+    fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value>
+    where
+        V: Visitor<'de>,
+    {
+        match self.input.as_ref() {
+            ValueRef::Data(data) => visitor.visit_bool(data.tag() != 0),
+            _ => Err(VmError::Message(format!("Unable to deserialize bool"))),
+        }
+    }
+
+    // The `parse_signed` function is generic over the integer type `T` so here
+    // it is invoked with `T=i8`. The next 8 methods are similar.
+    fn deserialize_i8<V>(self, visitor: V) -> Result<V::Value>
+    where
+        V: Visitor<'de>,
+    {
+        match self.input.as_ref() {
+            ValueRef::Byte(b) => visitor.visit_i8(b as i8),
+            _ => self.deserialize_any(visitor),
+        }
+    }
+
+    fn deserialize_i16<V>(self, visitor: V) -> Result<V::Value>
+    where
+        V: Visitor<'de>,
+    {
+        match self.input.as_ref() {
+            ValueRef::Byte(b) => visitor.visit_i16(b as i16),
+            _ => self.deserialize_any(visitor),
+        }
+    }
+
+    fn deserialize_i32<V>(self, visitor: V) -> Result<V::Value>
+    where
+        V: Visitor<'de>,
+    {
+        match self.input.as_ref() {
+            ValueRef::Int(b) => visitor.visit_i32(b as i32),
+            _ => self.deserialize_any(visitor),
+        }
+    }
+
+    fn deserialize_i64<V>(self, visitor: V) -> Result<V::Value>
+    where
+        V: Visitor<'de>,
+    {
+        match self.input.as_ref() {
+            ValueRef::Int(b) => visitor.visit_i64(b as i64),
+            _ => self.deserialize_any(visitor),
+        }
+    }
+
+    fn deserialize_u8<V>(self, visitor: V) -> Result<V::Value>
+    where
+        V: Visitor<'de>,
+    {
+        match self.input.as_ref() {
+            ValueRef::Byte(b) => visitor.visit_u8(b),
+            _ => self.deserialize_any(visitor),
+        }
+    }
+
+    fn deserialize_u16<V>(self, visitor: V) -> Result<V::Value>
+    where
+        V: Visitor<'de>,
+    {
+        match self.input.as_ref() {
+            ValueRef::Byte(b) => visitor.visit_u16(b as u16),
+            _ => self.deserialize_any(visitor),
+        }
+    }
+
+    fn deserialize_u32<V>(self, visitor: V) -> Result<V::Value>
+    where
+        V: Visitor<'de>,
+    {
+        match self.input.as_ref() {
+            ValueRef::Int(b) => visitor.visit_u32(b as u32),
+            _ => self.deserialize_any(visitor),
+        }
+    }
+
+    fn deserialize_u64<V>(self, visitor: V) -> Result<V::Value>
+    where
+        V: Visitor<'de>,
+    {
+        match self.input.as_ref() {
+            ValueRef::Int(b) => visitor.visit_u64(b as u64),
+            _ => self.deserialize_any(visitor),
+        }
+    }
+
+    fn deserialize_f32<V>(self, visitor: V) -> Result<V::Value>
+    where
+        V: Visitor<'de>,
+    {
+        match self.input.as_ref() {
+            ValueRef::Float(f) => visitor.visit_f32(f as f32),
+            _ => self.deserialize_any(visitor),
+        }
+    }
+
+    fn deserialize_f64<V>(self, visitor: V) -> Result<V::Value>
+    where
+        V: Visitor<'de>,
+    {
+        match self.input.as_ref() {
+            ValueRef::Float(f) => visitor.visit_f64(f),
+            _ => self.deserialize_any(visitor),
+        }
+    }
+
+    fn deserialize_char<V>(self, visitor: V) -> Result<V::Value>
+    where
+        V: Visitor<'de>,
+    {
+        use std::char::from_u32;
+        let typ = resolve::remove_aliases_cow(self.state.env, self.typ);
+        match (self.input.as_ref(), &**typ) {
+            (ValueRef::Int(c), &Type::Builtin(BuiltinType::Char)) => {
+                match from_u32(c as u32) {
+                    Some(c) => visitor.visit_char(c),
+                    None => self.deserialize_any(visitor),
+                }
+            }
+            _ => self.deserialize_any(visitor),
+        }
+    }
+
+    fn deserialize_str<V>(self, visitor: V) -> Result<V::Value>
+    where
+        V: Visitor<'de>,
+    {
+        self.deserialize_builtin(BuiltinType::String, |s| visitor.visit_borrowed_str(s))
+    }
+
+    fn deserialize_string<V>(self, visitor: V) -> Result<V::Value>
+    where
+        V: Visitor<'de>,
+    {
+        self.deserialize_str(visitor)
+    }
+
+    fn deserialize_bytes<V>(self, visitor: V) -> Result<V::Value>
+    where
+        V: Visitor<'de>,
+    {
+        self.deserialize_leaf(
+            |typ| match *typ {
+                Type::App(ref func, ref args) if args.len() == 1 => {
+                    **func == Type::Builtin(BuiltinType::Array) &&
+                        *args[0] == Type::Builtin(BuiltinType::Byte)
+                }
+                _ => false,
+            },
+            |s| visitor.visit_bytes(s),
+        )
+    }
+
+    fn deserialize_byte_buf<V>(self, visitor: V) -> Result<V::Value>
+    where
+        V: Visitor<'de>,
+    {
+        self.deserialize_bytes(visitor)
+    }
+
+    fn deserialize_option<V>(self, visitor: V) -> Result<V::Value>
+    where
+        V: Visitor<'de>,
+    {
+        let typ = resolve::canonical_alias(self.state.env, self.typ, |alias| {
+            alias.name.declared_name() == "std.types.Option"
+        });
+        let option_inner_typ = match **typ {
+            Type::App(ref func, ref args) if args.len() == 1 => {
+                match **func {
+                    Type::Alias(ref alias) if alias.name.declared_name() == "std.types.Option" => {
+                        Some(&args[0])
+                    }
+                    _ => None,
+                }
+            }
+            _ => None,
+        };
+        // If the type is not `Option` we just visit the value itself
+        match option_inner_typ {
+            Some(typ) => {
+                match self.input.as_ref() {
+                    ValueRef::Data(data) if data.tag() == 0 => visitor.visit_none(),
+                    ValueRef::Data(data) if data.tag() == 1 => {
+                        visitor.visit_some(&mut Deserializer {
+                            state: self.state.clone(),
+                            typ: typ,
+                            input: data.get_variants(0).unwrap(),
+                        })
+                    }
+                    _ => self.deserialize_any(visitor),
+                }
+            }
+            None => visitor.visit_some(self),
+        }
+    }
+
+    fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value>
+    where
+        V: Visitor<'de>,
+    {
+        match self.input.as_ref() {
+            ValueRef::Data(data) if data.tag() == 0 => visitor.visit_unit(),
+            _ => self.deserialize_any(visitor),
+        }
+    }
+
+    fn deserialize_unit_struct<V>(self, _name: &'static str, visitor: V) -> Result<V::Value>
+    where
+        V: Visitor<'de>,
+    {
+        self.deserialize_unit(visitor)
+    }
+
+    fn deserialize_newtype_struct<V>(self, _name: &'static str, visitor: V) -> Result<V::Value>
+    where
+        V: Visitor<'de>,
+    {
+        visitor.visit_newtype_struct(self)
+    }
+
+    fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value>
+    where
+        V: Visitor<'de>,
+    {
+        let typ = resolve::remove_aliases_cow(self.state.env, self.typ);
+        match (self.input.as_ref(), &**typ) {
+            (ValueRef::Array(values), &Type::App(_, ref args)) if args.len() == 1 => {
+                visitor.visit_seq(SeqDeserializer::new(
+                    self.state.clone(),
+                    values.iter().map(|variant| (variant, &args[0])),
+                ))
+            }
+            (ValueRef::Data(data), &Type::Variant(ref row)) => {
+                match row.row_iter().nth(data.tag() as usize) {
+                    Some(field) => {
+                        let iter = (0..data.len()).map(|i| data.get_variants(i).unwrap()).zip(
+                            arg_iter(&field.typ),
+                        );
+                        visitor.visit_seq(SeqDeserializer::new(self.state.clone(), iter))
+                    }
+                    None => self.deserialize_any(visitor),
+                }
+            }
+            _ => self.deserialize_any(visitor),
+        }
+    }
+
+    fn deserialize_tuple<V>(self, _len: usize, visitor: V) -> Result<V::Value>
+    where
+        V: Visitor<'de>,
+    {
+        self.deserialize_seq(visitor)
+    }
+
+    fn deserialize_tuple_struct<V>(
+        self,
+        _name: &'static str,
+        _len: usize,
+        visitor: V,
+    ) -> Result<V::Value>
+    where
+        V: Visitor<'de>,
+    {
+        self.deserialize_seq(visitor)
+    }
+
+    fn deserialize_map<V>(self, visitor: V) -> Result<V::Value>
+    where
+        V: Visitor<'de>,
+    {
+        let typ = resolve::remove_aliases_cow(self.state.env, self.typ);
+        match (self.input.as_ref(), &**typ) {
+            (ValueRef::Data(ref data), &Type::Record { .. }) => {
+                let iter = typ.row_iter().flat_map(|field| {
+                    data.lookup_field(self.state.thread, field.name.as_ref())
+                        .map(|variant| (variant, &field.name, &field.typ))
+                });
+                visitor.visit_map(MapDeserializer::new(self.state.clone(), iter))
+            }
+            _ => self.deserialize_any(visitor),
+        }
+    }
+
+    fn deserialize_struct<V>(
+        self,
+        _name: &'static str,
+        _fields: &'static [&'static str],
+        visitor: V,
+    ) -> Result<V::Value>
+    where
+        V: Visitor<'de>,
+    {
+        self.deserialize_map(visitor)
+    }
+
+    fn deserialize_enum<V>(
+        self,
+        _name: &'static str,
+        _variants: &'static [&'static str],
+        visitor: V,
+    ) -> Result<V::Value>
+    where
+        V: Visitor<'de>,
+    {
+        visitor.visit_enum(Enum::new(self))
+    }
+
+    fn deserialize_identifier<V>(self, visitor: V) -> Result<V::Value>
+    where
+        V: Visitor<'de>,
+    {
+        self.deserialize_str(visitor)
+    }
+
+    fn deserialize_ignored_any<V>(self, visitor: V) -> Result<V::Value>
+    where
+        V: Visitor<'de>,
+    {
+        self.deserialize_any(visitor)
+    }
+}
+
+struct SeqDeserializer<'de, 't, I> {
+    state: State<'de>,
+    iter: I,
+    _marker: PhantomData<&'t ()>,
+}
+
+impl<'de, 't, I> SeqDeserializer<'de, 't, I> {
+    fn new(state: State<'de>, iter: I) -> Self {
+        SeqDeserializer {
+            state: state,
+            iter: iter,
+            _marker: PhantomData,
+        }
+    }
+}
+
+impl<'de, 't, I> SeqAccess<'de> for SeqDeserializer<'de, 't, I>
+where
+    I: Iterator<
+        Item = (
+            Variants<'de>,
+            &'t ArcType,
+        ),
+    >,
+{
+    type Error = VmError;
+
+    fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>>
+    where
+        T: DeserializeSeed<'de>,
+    {
+        match self.iter.next() {
+            Some((value, typ)) => {
+                seed.deserialize(&mut Deserializer {
+                    state: self.state.clone(),
+                    input: value,
+                    typ: typ,
+                }).map(Some)
+            }
+            None => Ok(None),
+        }
+    }
+}
+
+struct MapDeserializer<'de, 't, I> {
+    state: State<'de>,
+    iter: I,
+    value: Option<(Variants<'de>, &'t ArcType)>,
+}
+
+impl<'de, 't, I> MapDeserializer<'de, 't, I> {
+    fn new(state: State<'de>, iter: I) -> Self {
+        MapDeserializer {
+            state: state,
+            iter: iter,
+            value: None,
+        }
+    }
+}
+
+impl<'de, 'a, 't, I> MapAccess<'de> for MapDeserializer<'de, 't, I>
+where
+    I: Iterator<
+        Item = (
+            Variants<'de>,
+            &'t Symbol,
+            &'t ArcType,
+        ),
+    >,
+{
+    type Error = VmError;
+
+    fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>>
+    where
+        K: DeserializeSeed<'de>,
+    {
+        match self.iter.next() {
+            Some((value, field, typ)) => {
+                self.value = Some((value, typ));
+                seed.deserialize(field.as_ref().into_deserializer()).map(
+                    Some,
+                )
+            }
+            None => Ok(None),
+        }
+    }
+
+    fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value>
+    where
+        V: DeserializeSeed<'de>,
+    {
+        match self.value.take() {
+            Some((value, typ)) => {
+                seed.deserialize(&mut Deserializer {
+                    state: self.state.clone(),
+                    input: value,
+                    typ: typ,
+                })
+            }
+            None => Err(Self::Error::custom("Unable to deserialize value")),
+        }
+    }
+}
+
+struct Enum<'a, 'de: 'a, 't: 'a> {
+    de: &'a mut Deserializer<'de, 't>,
+}
+
+impl<'a, 'de, 't> Enum<'a, 'de, 't> {
+    fn new(de: &'a mut Deserializer<'de, 't>) -> Self {
+        Enum { de: de }
+    }
+}
+
+impl<'a, 'b, 'de, 't> de::Deserializer<'de> for &'b mut Enum<'a, 'de, 't> {
+    type Error = VmError;
+
+    fn deserialize_any<V>(self, visitor: V) -> Result<V::Value>
+    where
+        V: Visitor<'de>,
+    {
+        match self.de.input.as_ref() {
+            ValueRef::Data(data) => visitor.visit_u32(data.tag()),
+            _ => Err(Self::Error::custom("Unable to deserialize tag")),
+        }
+    }
+
+    fn deserialize_str<V>(self, visitor: V) -> Result<V::Value>
+    where
+        V: Visitor<'de>,
+    {
+        let tag = match self.de.input.as_ref() {
+            ValueRef::Data(data) => data.tag(),
+            _ => return Err(Self::Error::custom("Unable to deserialize tag")),
+        };
+        let typ = resolve::remove_aliases_cow(self.de.state.env, self.de.typ);
+        match **typ {
+            Type::Variant(ref variants) => {
+                let variant = variants.row_iter().nth(tag as usize).ok_or_else(|| {
+                    Self::Error::custom("Unable to deserialize tag")
+                })?;
+                visitor.visit_str(variant.name.as_ref())
+            }
+            _ => return Err(Self::Error::custom("Unable to deserialize tag")),
+        }
+    }
+
+    fn deserialize_string<V>(self, visitor: V) -> Result<V::Value>
+    where
+        V: Visitor<'de>,
+    {
+        self.deserialize_str(visitor)
+    }
+
+    forward_to_deserialize_any! {
+        bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 char bytes
+        byte_buf option unit unit_struct newtype_struct seq tuple
+        tuple_struct map struct enum identifier ignored_any
+    }
+}
+
+impl<'a, 'de, 't> EnumAccess<'de> for Enum<'a, 'de, 't> {
+    type Error = VmError;
+    type Variant = Self;
+
+    fn variant_seed<V>(mut self, seed: V) -> Result<(V::Value, Self::Variant)>
+    where
+        V: DeserializeSeed<'de>,
+    {
+        seed.deserialize(&mut self).map(|value| (value, self))
+    }
+}
+
+impl<'de, 'a, 't> VariantAccess<'de> for Enum<'a, 'de, 't> {
+    type Error = VmError;
+
+    fn unit_variant(self) -> Result<()> {
+        Ok(())
+    }
+
+    fn newtype_variant_seed<T>(self, seed: T) -> Result<T::Value>
+    where
+        T: DeserializeSeed<'de>,
+    {
+        match self.de.input.as_ref() {
+            ValueRef::Data(data) if data.len() == 1 => {
+                seed.deserialize(&mut Deserializer {
+                    input: data.get_variants(0).unwrap(),
+                    ..self.de.clone()
+                })
+            }
+            _ => Err(VmError::Message(format!("Unable to deserialize bool"))),
+        }
+    }
+
+    fn tuple_variant<V>(self, _len: usize, visitor: V) -> Result<V::Value>
+    where
+        V: Visitor<'de>,
+    {
+        de::Deserializer::deserialize_seq(self.de, visitor)
+    }
+
+    fn struct_variant<V>(self, _fields: &'static [&'static str], visitor: V) -> Result<V::Value>
+    where
+        V: Visitor<'de>,
+    {
+        de::Deserializer::deserialize_seq(self.de, visitor)
+    }
+}
diff --git a/vm/src/api/mac.rs b/vm/src/api/mac.rs
new file mode 100644
index 00000000..af5982e3
--- /dev/null
+++ b/vm/src/api/mac.rs
@@ -0,0 +1,219 @@
+
+/// Creates a `GluonFunction` from a function implementing `VMFunction`
+///
+/// ```rust
+/// #[macro_use]
+/// extern crate gluon_vm;
+/// fn test(_x: i32, _y: String) -> f64 {
+///     panic!()
+/// }
+///
+/// fn main() {
+///     primitive!(2 test);
+/// }
+/// ```
+#[macro_export]
+macro_rules! primitive {
+    (0 $name: expr) => {
+        unsafe {
+            extern "C" fn wrapper(thread: &$crate::thread::Thread) -> $crate::thread::Status {
+                 $crate::api::VmFunction::unpack_and_call(
+                     &($name as fn () -> _), thread)
+            }
+            $crate::api::primitive_f(stringify!($name), wrapper, $name as fn () -> _)
+        }
+    };
+    (1 $name: expr) => {
+        unsafe {
+            extern "C" fn wrapper(thread: &$crate::thread::Thread) -> $crate::thread::Status {
+                 $crate::api::VmFunction::unpack_and_call(
+                     &($name as fn (_) -> _), thread)
+            }
+            $crate::api::primitive_f(stringify!($name), wrapper, $name as fn (_) -> _)
+        }
+    };
+    (2 $name: expr) => {
+        unsafe {
+            extern "C" fn wrapper(thread: &$crate::thread::Thread) -> $crate::thread::Status {
+                 $crate::api::VmFunction::unpack_and_call(
+                     &($name as fn (_, _) -> _), thread)
+            }
+            $crate::api::primitive_f(stringify!($name), wrapper, $name as fn (_, _) -> _)
+        }
+    };
+    (3 $name: expr) => {
+        unsafe {
+            extern "C" fn wrapper(thread: &$crate::thread::Thread) -> $crate::thread::Status {
+                 $crate::api::VmFunction::unpack_and_call(
+                     &($name as fn (_, _, _) -> _), thread)
+            }
+            $crate::api::primitive_f(stringify!($name), wrapper, $name as fn (_, _, _) -> _)
+        }
+    };
+    (4 $name: expr) => {
+        unsafe {
+            extern "C" fn wrapper(thread: &$crate::thread::Thread) -> $crate::thread::Status {
+                 $crate::api::VmFunction::unpack_and_call(
+                     &($name as fn (_, _, _, _) -> _), thread)
+            }
+            $crate::api::primitive_f(stringify!($name), wrapper, $name as fn (_, _, _, _) -> _)
+        }
+    };
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! field_decl_inner {
+    ($($field: ident),*) => {
+        $(
+        #[allow(non_camel_case_types)]
+        #[derive(Default)]
+        pub struct $field;
+        impl $crate::api::record::Field for $field {
+            fn name() -> &'static str {
+                stringify!($field)
+            }
+        }
+        )*
+    }
+}
+
+/// Declares fields useable by the record macros
+///
+/// ```rust
+/// #[macro_use]
+/// extern crate gluon_vm;
+/// # fn main() { }
+///
+/// field_decl! {x, y}
+/// ```
+#[macro_export]
+macro_rules! field_decl {
+    ($($field: ident),*) => {
+        mod _field { field_decl_inner!($($field),*); }
+    }
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! record_no_decl_inner {
+    () => { () };
+    ($field: ident => $value: expr) => {
+        $crate::api::record::HList((_field::$field, $value), ())
+    };
+    ($field: ident => $value: expr, $($field_tail: ident => $value_tail: expr),*) => {
+        $crate::api::record::HList((_field::$field, $value),
+                                   record_no_decl_inner!($($field_tail => $value_tail),*))
+    };
+}
+
+/// Macro that creates a record that can be passed to gluon. Reuses already declared fields
+/// instead of generating unique ones.
+///
+/// ```rust
+/// #[macro_use]
+/// extern crate gluon_vm;
+///
+/// field_decl! {x, y, name}
+///
+/// fn main() {
+///     record_no_decl!(x => 1, y => 2, name => "Gluon");
+/// }
+/// ```
+#[macro_export]
+macro_rules! record_no_decl {
+    ($($field: ident => $value: expr),*) => {
+        {
+            $crate::api::Record {
+                fields: record_no_decl_inner!($($field => $value),*)
+            }
+        }
+    }
+}
+
+/// Macro that creates a record that can be passed to gluon
+///
+/// ```rust
+/// #[macro_use]
+/// extern crate gluon_vm;
+/// fn main() {
+///     record!(x => 1, y => 2, name => "Gluon");
+/// }
+/// ```
+#[macro_export]
+macro_rules! record {
+    ($($field: ident => $value: expr),*) => {
+        {
+            field_decl!($($field),*);
+            record_no_decl!($($field => $value),*)
+        }
+    }
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! record_type_inner {
+    () => { () };
+    ($field: ident => $value: ty) => {
+        $crate::api::record::HList<(_field::$field, $value), ()>
+    };
+    ($field: ident => $value: ty, $($field_tail: ident => $value_tail: ty),*) => {
+        $crate::api::record::HList<(_field::$field, $value),
+                                record_type_inner!( $($field_tail => $value_tail),*)>
+    }
+}
+
+/// Creates a Rust type compatible with the type of `record_no_decl!`
+///
+/// ```rust
+/// #[macro_use]
+/// extern crate gluon_vm;
+/// # fn main() { }
+/// // Fields used in `record_type!` needs to be forward declared
+/// field_decl! {x, y}
+/// fn new_vec(x: f64, y: f64) -> record_type!(x => f64, y => f64) {
+///     record_no_decl!(x => y, y => y)
+/// }
+/// ```
+#[macro_export]
+macro_rules! record_type {
+    ($($field: ident => $value: ty),*) => {
+        $crate::api::Record<
+            record_type_inner!($($field => $value),*)
+            >
+    }
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! record_p_impl {
+    () => { () };
+    ($field: pat) => {
+        $crate::api::record::HList((_, $field), ())
+    };
+    ($field: pat, $($field_tail: pat),*) => {
+        $crate::api::record::HList((_, $field),
+                                record_p_impl!($($field_tail),*))
+    }
+}
+
+/// Creates a pattern which matches on marshalled gluon records
+///
+/// ```rust
+/// #[macro_use]
+/// extern crate gluon_vm;
+/// fn main() {
+///     match record!(x => 1, y => "y") {
+///         record_p!(a, "y") => assert_eq!(a, 1),
+///         _ => assert!(false),
+///     }
+/// }
+/// ```
+#[macro_export]
+macro_rules! record_p {
+    ($($field: pat),*) => {
+        $crate::api::Record {
+            fields: record_p_impl!($($field),*)
+        }
+    }
+}
diff --git a/vm/src/api.rs b/vm/src/api/mod.rs
similarity index 85%
rename from vm/src/api.rs
rename to vm/src/api/mod.rs
index b6bfe045..cae7b7e9 100644
--- a/vm/src/api.rs
+++ b/vm/src/api/mod.rs
@@ -1,5 +1,5 @@
 //! The marshalling api
-use {Variants, Error, Result};
+use {Variants, Error, Result, forget_lifetime};
 use gc::{DataDef, Gc, Traverseable, Move};
 use base::symbol::Symbol;
 use stack::StackFrame;
@@ -22,6 +22,13 @@ use futures::{Async, BoxFuture, Future};
 
 pub use value::Userdata;
 
+#[macro_use]
+pub mod mac;
+#[cfg(feature = "serde")]
+pub mod ser;
+#[cfg(feature = "serde")]
+pub mod de;
+
 macro_rules! count {
     () => { 0 };
     ($_e: ident) => { 1 };
@@ -35,7 +42,7 @@ pub enum ValueRef<'a> {
     Float(f64),
     String(&'a str),
     Data(Data<'a>),
-    Tag(VmTag),
+    Array(ArrayRef<'a>),
     Userdata(&'a vm::Userdata),
     Internal,
 }
@@ -51,7 +58,6 @@ impl<'a, 'b> PartialEq<ValueRef<'b>> for ValueRef<'a> {
             (&Float(l), &Float(r)) => l == r,
             (&String(l), &String(r)) => l == r,
             (&Data(l), &Data(r)) => l == r,
-            (&Tag(l), &Tag(r)) => l == r,
             _ => false,
         }
     }
@@ -65,37 +71,75 @@ impl<'a> PartialEq<Value> for ValueRef<'a> {
 
 impl<'a> ValueRef<'a> {
     pub fn new(value: &'a Value) -> ValueRef<'a> {
-        match *value {
+        unsafe { ValueRef::rooted_new(*value) }
+    }
+
+    pub unsafe fn rooted_new(value: Value) -> ValueRef<'a> {
+        match value {
             Value::Byte(i) => ValueRef::Byte(i),
             Value::Int(i) => ValueRef::Int(i),
             Value::Float(f) => ValueRef::Float(f),
-            Value::String(ref s) => ValueRef::String(s),
-            Value::Data(ref data) => ValueRef::Data(Data(data)),
-            Value::Userdata(ref data) => ValueRef::Userdata(&***data),
-            Value::Tag(tag) => ValueRef::Tag(tag),
+            Value::String(s) => ValueRef::String(forget_lifetime(&*s)),
+            Value::Data(data) => ValueRef::Data(Data(DataInner::Data(forget_lifetime(&*data)))),
+            Value::Tag(tag) => ValueRef::Data(Data(DataInner::Tag(tag))),
+            Value::Array(array) => ValueRef::Array(ArrayRef(forget_lifetime(&*array))),
+            Value::Userdata(data) => ValueRef::Userdata(forget_lifetime(&**data)),
             Value::Thread(_) |
             Value::Function(_) |
             Value::Closure(_) |
-            Value::Array(_) | // FIXME Expose arrays safely
             Value::PartialApplication(_) => ValueRef::Internal,
         }
     }
 }
 
 #[derive(Copy, Clone, PartialEq, Debug)]
-pub struct Data<'a>(&'a DataStruct);
+enum DataInner<'a> {
+    Tag(VmTag),
+    Data(&'a DataStruct),
+}
+
+#[derive(Copy, Clone, PartialEq, Debug)]
+pub struct Data<'a>(DataInner<'a>);
 
 impl<'a> Data<'a> {
     pub fn tag(&self) -> VmTag {
-        self.0.tag
+        match self.0 {
+            DataInner::Tag(tag) => tag,
+            DataInner::Data(data) => data.tag,
+        }
     }
 
     pub fn len(&self) -> usize {
-        self.0.fields.len()
+        match self.0 {
+            DataInner::Tag(_) => 0,
+            DataInner::Data(data) => data.fields.len(),
+        }
     }
 
     pub fn get(&self, index: usize) -> Option<ValueRef<'a>> {
-        self.0.fields.get(index).map(ValueRef::new)
+        match self.0 {
+            DataInner::Tag(_) => None,
+            DataInner::Data(data) => data.fields.get(index).map(ValueRef::new),
+        }
+    }
+
+    pub fn get_variants(&self, index: usize) -> Option<Variants<'a>> {
+        match self.0 {
+            DataInner::Tag(_) => None,
+            DataInner::Data(data) => unsafe { data.fields.get(index).map(|v| Variants::new(v)) },
+        }
+    }
+
+    // Retrieves the field `name` from this record
+    pub fn lookup_field(&self, thread: &Thread, name: &str) -> Option<Variants<'a>> {
+        match self.0 {
+            DataInner::Tag(_) => None,
+            DataInner::Data(data) => unsafe {
+                thread.lookup_field(data, name).ok().map(|v| {
+                    Variants::with_root(v, data)
+                })
+            },
+        }
     }
 }
 
@@ -159,68 +203,6 @@ where
     }
 }
 
-/// Creates a `GluonFunction` from a function implementing `VMFunction`
-///
-/// ```rust
-/// #[macro_use]
-/// extern crate gluon_vm;
-/// fn test(_x: i32, _y: String) -> f64 {
-///     panic!()
-/// }
-///
-/// fn main() {
-///     primitive!(2 test);
-/// }
-/// ```
-#[macro_export]
-macro_rules! primitive {
-    (0 $name: expr) => {
-        unsafe {
-            extern "C" fn wrapper(thread: &$crate::thread::Thread) -> $crate::thread::Status {
-                 $crate::api::VmFunction::unpack_and_call(
-                     &($name as fn () -> _), thread)
-            }
-            $crate::api::primitive_f(stringify!($name), wrapper, $name as fn () -> _)
-        }
-    };
-    (1 $name: expr) => {
-        unsafe {
-            extern "C" fn wrapper(thread: &$crate::thread::Thread) -> $crate::thread::Status {
-                 $crate::api::VmFunction::unpack_and_call(
-                     &($name as fn (_) -> _), thread)
-            }
-            $crate::api::primitive_f(stringify!($name), wrapper, $name as fn (_) -> _)
-        }
-    };
-    (2 $name: expr) => {
-        unsafe {
-            extern "C" fn wrapper(thread: &$crate::thread::Thread) -> $crate::thread::Status {
-                 $crate::api::VmFunction::unpack_and_call(
-                     &($name as fn (_, _) -> _), thread)
-            }
-            $crate::api::primitive_f(stringify!($name), wrapper, $name as fn (_, _) -> _)
-        }
-    };
-    (3 $name: expr) => {
-        unsafe {
-            extern "C" fn wrapper(thread: &$crate::thread::Thread) -> $crate::thread::Status {
-                 $crate::api::VmFunction::unpack_and_call(
-                     &($name as fn (_, _, _) -> _), thread)
-            }
-            $crate::api::primitive_f(stringify!($name), wrapper, $name as fn (_, _, _) -> _)
-        }
-    };
-    (4 $name: expr) => {
-        unsafe {
-            extern "C" fn wrapper(thread: &$crate::thread::Thread) -> $crate::thread::Status {
-                 $crate::api::VmFunction::unpack_and_call(
-                     &($name as fn (_, _, _, _) -> _), thread)
-            }
-            $crate::api::primitive_f(stringify!($name), wrapper, $name as fn (_, _, _, _) -> _)
-        }
-    };
-}
-
 #[derive(PartialEq)]
 pub struct Generic<T>(pub Value, PhantomData<T>);
 
@@ -255,7 +237,7 @@ impl<'vm, T: VmType> Pushable<'vm> for Generic<T> {
 }
 impl<'vm, T> Getable<'vm> for Generic<T> {
     fn from_value(_: &'vm Thread, value: Variants) -> Option<Generic<T>> {
-        Some(Generic::from(*value.0))
+        Some(Generic::from(value.0))
     }
 }
 
@@ -378,7 +360,7 @@ impl<'vm, T: vm::Userdata> Pushable<'vm> for T {
 
 impl<'vm> Getable<'vm> for Value {
     fn from_value(_vm: &'vm Thread, value: Variants) -> Option<Self> {
-        Some(*value.0)
+        Some(value.0)
     }
 }
 
@@ -402,10 +384,6 @@ where
     }
 }
 
-unsafe fn forget_lifetime<'a, 'b, T: ?Sized>(x: &'a T) -> &'b T {
-    ::std::mem::transmute(x)
-}
-
 impl<'vm> Getable<'vm> for &'vm str {
     fn from_value(_vm: &'vm Thread, value: Variants) -> Option<Self> {
         unsafe {
@@ -598,8 +576,7 @@ impl<'vm> Pushable<'vm> for bool {
 impl<'vm> Getable<'vm> for bool {
     fn from_value(_: &'vm Thread, value: Variants) -> Option<bool> {
         match value.as_ref() {
-            ValueRef::Tag(1) => Some(true),
-            ValueRef::Tag(0) => Some(false),
+            ValueRef::Data(data) => Some(data.tag() == 1),
             _ => None,
         }
     }
@@ -629,7 +606,6 @@ impl<'vm> Getable<'vm> for Ordering {
     fn from_value(_: &'vm Thread, value: Variants) -> Option<Ordering> {
         let tag = match value.as_ref() {
             ValueRef::Data(data) => data.tag(),
-            ValueRef::Tag(tag) => tag,
             _ => return None,
         };
         match tag {
@@ -732,8 +708,8 @@ where
 }
 impl<'s, 'vm, T: Copy + ArrayRepr> Getable<'vm> for &'s [T] {
     unsafe fn from_value_unsafe(_: &'vm Thread, value: Variants) -> Option<Self> {
-        match *value.0 {
-            Value::Array(ptr) => ptr.as_slice().map(|s| &*(s as *const _)),
+        match value.as_ref() {
+            ValueRef::Array(ptr) => ptr.0.as_slice().map(|s| &*(s as *const _)),
             _ => None,
         }
     }
@@ -838,15 +814,14 @@ impl<'vm, T: Pushable<'vm>> Pushable<'vm> for Option<T> {
 }
 impl<'vm, T: Getable<'vm>> Getable<'vm> for Option<T> {
     fn from_value(vm: &'vm Thread, value: Variants) -> Option<Option<T>> {
-        match *value.0 {
-            Value::Data(data) => {
-                if data.tag == 0 {
+        match value.as_ref() {
+            ValueRef::Data(data) => {
+                if data.tag() == 0 {
                     Some(None)
                 } else {
-                    T::from_value(vm, Variants(&data.fields[0])).map(Some)
+                    T::from_value(vm, data.get_variants(0).unwrap()).map(Some)
                 }
             }
-            Value::Tag(0) => Some(None),
             _ => None,
         }
     }
@@ -894,11 +869,11 @@ impl<'vm, T: Pushable<'vm>, E: Pushable<'vm>> Pushable<'vm> for StdResult<T, E>
 
 impl<'vm, T: Getable<'vm>, E: Getable<'vm>> Getable<'vm> for StdResult<T, E> {
     fn from_value(vm: &'vm Thread, value: Variants) -> Option<StdResult<T, E>> {
-        match *value.0 {
-            Value::Data(data) => {
-                match data.tag {
-                    0 => E::from_value(vm, Variants(&data.fields[0])).map(Err),
-                    1 => T::from_value(vm, Variants(&data.fields[0])).map(Ok),
+        match value.as_ref() {
+            ValueRef::Data(data) => {
+                match data.tag() {
+                    0 => E::from_value(vm, data.get_variants(0).unwrap()).map(Err),
+                    1 => T::from_value(vm, data.get_variants(0).unwrap()).map(Ok),
                     _ => None,
                 }
             }
@@ -1037,6 +1012,10 @@ where
         *self.0
     }
 
+    pub fn get_variants(&self) -> Variants {
+        unsafe { Variants::new(&self.0) }
+    }
+
     pub fn get_ref(&self) -> ValueRef {
         ValueRef::new(&self.0)
     }
@@ -1073,13 +1052,37 @@ where
 
 impl<'vm, V> Getable<'vm> for OpaqueValue<&'vm Thread, V> {
     fn from_value(vm: &'vm Thread, value: Variants) -> Option<OpaqueValue<&'vm Thread, V>> {
-        Some(OpaqueValue(vm.root_value(*value.0), PhantomData))
+        Some(OpaqueValue(vm.root_value(value.0), PhantomData))
     }
 }
 
 impl<'vm, V> Getable<'vm> for OpaqueValue<RootedThread, V> {
     fn from_value(vm: &'vm Thread, value: Variants) -> Option<OpaqueValue<RootedThread, V>> {
-        Some(OpaqueValue(vm.root_value(*value.0), PhantomData))
+        Some(OpaqueValue(vm.root_value(value.0), PhantomData))
+    }
+}
+
+#[derive(Copy, Clone, Debug)]
+pub struct ArrayRef<'vm>(&'vm ValueArray);
+
+impl<'vm> ArrayRef<'vm> {
+    pub fn get(&self, index: usize) -> Option<Variants> {
+        if index < self.0.len() {
+            unsafe { Some(Variants::with_root(self.0.get(index), self)) }
+        } else {
+            None
+        }
+    }
+
+    pub fn as_slice<T>(&self) -> Option<&[T]>
+    where
+        T: ArrayRepr + Copy,
+    {
+        self.0.as_slice()
+    }
+
+    pub fn iter(&self) -> ::value::VariantIter<'vm> {
+        self.0.variant_iter()
     }
 }
 
@@ -1104,12 +1107,14 @@ impl<'vm, T> Array<'vm, T> {
 
 impl<'vm, T: for<'vm2> Getable<'vm2>> Array<'vm, T> {
     pub fn get(&self, index: VmInt) -> Option<T> {
-        match *self.0 {
-            Value::Array(data) => {
-                let v = data.get(index as usize);
-                T::from_value(self.0.vm(), Variants(&v))
+        unsafe {
+            match Variants::new(&self.0).as_ref() {
+                ValueRef::Array(data) => {
+                    let v = data.get(index as usize).unwrap();
+                    T::from_value(self.0.vm(), v)
+                }
+                _ => None,
             }
-            _ => None,
         }
     }
 }
@@ -1137,7 +1142,7 @@ where
 
 impl<'vm, T> Getable<'vm> for Array<'vm, T> {
     fn from_value(vm: &'vm Thread, value: Variants) -> Option<Array<'vm, T>> {
-        Some(Array(vm.root_value(*value.0), PhantomData))
+        Some(Array(vm.root_value(value.0), PhantomData))
     }
 }
 
@@ -1146,7 +1151,7 @@ impl<'vm, T: Any> VmType for Root<'vm, T> {
 }
 impl<'vm, T: vm::Userdata> Getable<'vm> for Root<'vm, T> {
     fn from_value(vm: &'vm Thread, value: Variants) -> Option<Root<'vm, T>> {
-        match *value.0 {
+        match value.0 {
             Value::Userdata(data) => vm.root::<T>(data).map(From::from),
             _ => None,
         }
@@ -1158,7 +1163,7 @@ impl<'vm> VmType for RootStr<'vm> {
 }
 impl<'vm> Getable<'vm> for RootStr<'vm> {
     fn from_value(vm: &'vm Thread, value: Variants) -> Option<RootStr<'vm>> {
-        match *value.0 {
+        match value.0 {
             Value::String(v) => Some(vm.root_string(v)),
             _ => None,
         }
@@ -1217,9 +1222,10 @@ macro_rules! define_tuple {
             fn from_value(vm: &'vm Thread, value: Variants) -> Option<($($id),+)> {
                 match value.as_ref() {
                     ValueRef::Data(v) => {
+                        assert!(v.len() == count!($($id),+));
                         let mut i = 0;
                         let x = ( $(
-                            { let a = $id::from_value(vm, Variants(&v.0.fields[i])); i += 1; a }
+                            { let a = $id::from_value(vm, v.get_variants(i).unwrap()); i += 1; a }
                         ),+ );
                         match x {
                             ($(Some($id)),+) => Some(( $($id),+ )),
@@ -1424,7 +1430,7 @@ pub mod record {
         T: GetableFieldList<'vm>,
     {
         fn from_value(vm: &'vm Thread, value: Variants) -> Option<Self> {
-            match *value.0 {
+            match value.0 {
                 Value::Data(ref data) => {
                     HList::<(F, A), T>::from_value(vm, &data.fields).map(
                         |fields| {
@@ -1438,163 +1444,6 @@ pub mod record {
     }
 }
 
-#[doc(hidden)]
-#[macro_export]
-macro_rules! field_decl_inner {
-    ($($field: ident),*) => {
-        $(
-        #[allow(non_camel_case_types)]
-        #[derive(Default)]
-        pub struct $field;
-        impl $crate::api::record::Field for $field {
-            fn name() -> &'static str {
-                stringify!($field)
-            }
-        }
-        )*
-    }
-}
-
-/// Declares fields useable by the record macros
-///
-/// ```rust
-/// #[macro_use]
-/// extern crate gluon_vm;
-/// # fn main() { }
-///
-/// field_decl! {x, y}
-/// ```
-#[macro_export]
-macro_rules! field_decl {
-    ($($field: ident),*) => {
-        mod _field { field_decl_inner!($($field),*); }
-    }
-}
-
-#[doc(hidden)]
-#[macro_export]
-macro_rules! record_no_decl_inner {
-    () => { () };
-    ($field: ident => $value: expr) => {
-        $crate::api::record::HList((_field::$field, $value), ())
-    };
-    ($field: ident => $value: expr, $($field_tail: ident => $value_tail: expr),*) => {
-        $crate::api::record::HList((_field::$field, $value),
-                                   record_no_decl_inner!($($field_tail => $value_tail),*))
-    };
-}
-
-/// Macro that creates a record that can be passed to gluon. Reuses already declared fields
-/// instead of generating unique ones.
-///
-/// ```rust
-/// #[macro_use]
-/// extern crate gluon_vm;
-///
-/// field_decl! {x, y, name}
-///
-/// fn main() {
-///     record_no_decl!(x => 1, y => 2, name => "Gluon");
-/// }
-/// ```
-#[macro_export]
-macro_rules! record_no_decl {
-    ($($field: ident => $value: expr),*) => {
-        {
-            $crate::api::Record {
-                fields: record_no_decl_inner!($($field => $value),*)
-            }
-        }
-    }
-}
-
-/// Macro that creates a record that can be passed to gluon
-///
-/// ```rust
-/// #[macro_use]
-/// extern crate gluon_vm;
-/// fn main() {
-///     record!(x => 1, y => 2, name => "Gluon");
-/// }
-/// ```
-#[macro_export]
-macro_rules! record {
-    ($($field: ident => $value: expr),*) => {
-        {
-            field_decl!($($field),*);
-            record_no_decl!($($field => $value),*)
-        }
-    }
-}
-
-#[doc(hidden)]
-#[macro_export]
-macro_rules! record_type_inner {
-    () => { () };
-    ($field: ident => $value: ty) => {
-        $crate::api::record::HList<(_field::$field, $value), ()>
-    };
-    ($field: ident => $value: ty, $($field_tail: ident => $value_tail: ty),*) => {
-        $crate::api::record::HList<(_field::$field, $value),
-                                record_type_inner!( $($field_tail => $value_tail),*)>
-    }
-}
-
-/// Creates a Rust type compatible with the type of `record_no_decl!`
-///
-/// ```rust
-/// #[macro_use]
-/// extern crate gluon_vm;
-/// # fn main() { }
-/// // Fields used in `record_type!` needs to be forward declared
-/// field_decl! {x, y}
-/// fn new_vec(x: f64, y: f64) -> record_type!(x => f64, y => f64) {
-///     record_no_decl!(x => y, y => y)
-/// }
-/// ```
-#[macro_export]
-macro_rules! record_type {
-    ($($field: ident => $value: ty),*) => {
-        $crate::api::Record<
-            record_type_inner!($($field => $value),*)
-            >
-    }
-}
-
-#[doc(hidden)]
-#[macro_export]
-macro_rules! record_p_impl {
-    () => { () };
-    ($field: pat) => {
-        $crate::api::record::HList((_, $field), ())
-    };
-    ($field: pat, $($field_tail: pat),*) => {
-        $crate::api::record::HList((_, $field),
-                                record_p_impl!($($field_tail),*))
-    }
-}
-
-/// Creates a pattern which matches on marshalled gluon records
-///
-/// ```rust
-/// #[macro_use]
-/// extern crate gluon_vm;
-/// fn main() {
-///     match record!(x => 1, y => "y") {
-///         record_p!(a, "y") => assert_eq!(a, 1),
-///         _ => assert!(false),
-///     }
-/// }
-/// ```
-#[macro_export]
-macro_rules! record_p {
-    ($($field: pat),*) => {
-        $crate::api::Record {
-            fields: record_p_impl!($($field),*)
-        }
-    }
-}
-
 impl<'vm, F: VmType> VmType for Primitive<F> {
     type Type = F::Type;
     fn make_type(vm: &Thread) -> ArcType {
@@ -1753,7 +1602,7 @@ where
 impl<'vm, F> Getable<'vm> for Function<&'vm Thread, F> {
     fn from_value(vm: &'vm Thread, value: Variants) -> Option<Function<&'vm Thread, F>> {
         Some(Function {
-            value: vm.root_value(*value.0),
+            value: vm.root_value(value.0),
             _marker: PhantomData,
         }) //TODO not type safe
     }
@@ -1762,7 +1611,7 @@ impl<'vm, F> Getable<'vm> for Function<&'vm Thread, F> {
 impl<'vm, F> Getable<'vm> for Function<RootedThread, F> {
     fn from_value(vm: &'vm Thread, value: Variants) -> Option<Self> {
         Some(Function {
-            value: vm.root_value(*value.0),
+            value: vm.root_value(value.0),
             _marker: PhantomData,
         }) //TODO not type safe
     }
@@ -1844,7 +1693,7 @@ where $($args: Getable<'vm> + 'vm,)*
             let (lock, ($($args,)*)) = {
                 let stack = StackFrame::current(&mut context.stack);
                 $(let $args = {
-                    let x = $args::from_value_unsafe(vm, Variants(&stack[i]))
+                    let x = $args::from_value_unsafe(vm, Variants::new(&stack[i]))
                         .expect(stringify!(Argument $args));
                     i += 1;
                     x
@@ -1892,7 +1741,7 @@ where $($args: Getable<'vm> + 'vm,)*
             let (lock, ($($args,)*)) = {
                 let stack = StackFrame::current(&mut context.stack);
                 $(let $args = {
-                    let x = $args::from_value_unsafe(vm, Variants(&stack[i]))
+                    let x = $args::from_value_unsafe(vm, Variants::new(&stack[i]))
                         .expect(stringify!(Argument $args));
                     i += 1;
                     x
@@ -1946,13 +1795,15 @@ impl<'vm, T, $($args,)* R> Function<T, fn($($args),*) -> R>
             }
         })
     }
-
+    
     fn return_value(vm: &Thread, value: Value) -> Result<R> {
-        R::from_value(vm, Variants(&value))
-            .ok_or_else(|| {
-                error!("Wrong type {:?}", value);
-                Error::Message("Wrong type".to_string())
-            })
+        unsafe {
+            R::from_value(vm, Variants::new(&value))
+                .ok_or_else(|| {
+                    error!("Wrong type {:?}", value);
+                    Error::Message("Wrong type".to_string())
+                })
+        }
     }
 }
 
diff --git a/vm/src/api/ser.rs b/vm/src/api/ser.rs
new file mode 100644
index 00000000..1e896c74
--- /dev/null
+++ b/vm/src/api/ser.rs
@@ -0,0 +1,577 @@
+//! Rust -> Gluon value conversion via the `serde::Serialize` trait
+
+use std::fmt;
+use std::ops::{Deref, DerefMut};
+
+use base::types::ArcType;
+use {Error, Result};
+use api::{Pushable, VmType};
+use interner::InternedStr;
+use thread::{Context, Thread, ThreadInternal};
+use types::{VmTag, VmIndex};
+use value::{Def, Value};
+use serde::ser::{self, Serialize};
+
+/**
+`Pushable` wrapper which pushes `T` by serializing it.
+
+## Struct
+
+```
+#[macro_use]
+extern crate serde_derive;
+
+extern crate gluon;
+#[macro_use]
+extern crate gluon_vm;
+
+use gluon::{Compiler, Thread, new_vm};
+use gluon::base::types::ArcType;
+use gluon::vm::api::{FunctionRef, VmType};
+use gluon::vm::api::ser::Ser;
+# fn main() {
+
+#[derive(Serialize)]
+struct Vec2 {
+    x: i32,
+    y: i32,
+}
+
+impl VmType for Vec2 {
+    type Type = Self;
+
+    fn make_type(thread: &Thread) -> ArcType {
+        field_decl!{ x, y }
+        type T = record_type! {
+            x => i32,
+            y => i32
+        };
+        T::make_type(thread)
+    }
+}
+
+let thread = new_vm();
+let (mut f, _): (FunctionRef<fn (Ser<Vec2>) -> i32>, _) = Compiler::new()
+    .run_expr(&thread, "", r#"let f v: _ -> Int = v.x + v.y in f"#)
+    .unwrap_or_else(|err| panic!("{}", err));
+let vec = Vec2 {
+    x: 3,
+    y: 10
+};
+let result = f.call(Ser(vec)).unwrap_or_else(|err| panic!("{}", err));
+assert_eq!(result, 13);
+# }
+```
+
+## Enum
+
+```
+#[macro_use]
+extern crate serde_derive;
+
+extern crate gluon;
+#[macro_use]
+extern crate gluon_vm;
+
+use gluon::{Compiler, Thread, new_vm};
+use gluon::base::types::ArcType;
+use gluon::vm::api::{FunctionRef, VmType};
+use gluon::vm::api::ser::Ser;
+# fn main() {
+
+
+#[derive(Serialize)]
+enum Enum {
+    A(i32),
+    B(String, i32),
+}
+
+impl VmType for Enum {
+    type Type = Self;
+
+    fn make_type(thread: &Thread) -> ArcType {
+        // Use the enum type declared in gluon
+        thread.find_type_info("test.Enum").unwrap().into_type()
+    }
+}
+
+let thread = new_vm();
+
+let expr = r#"
+type Enum = | A Int | B String Int
+
+let f e =
+    match e with
+    | A a -> a
+    | B b c -> c
+
+{ Enum, f }
+"#;
+Compiler::new()
+#   .implicit_prelude(false)
+    .load_script(&thread, "test", expr)
+    .unwrap_or_else(|err| panic!("{}", err));
+
+let mut f: FunctionRef<fn (Ser<Enum>) -> i32> = thread
+    .get_global("test.f")
+    .unwrap_or_else(|err| panic!("{}", err));
+
+let result = f.call(Ser(Enum::B("".to_string(), 4))).unwrap_or_else(|err| panic!("{}", err));
+assert_eq!(result, 4);
+
+# }
+```
+*/
+pub struct Ser<T>(pub T);
+
+impl<T> VmType for Ser<T>
+where
+    T: VmType,
+{
+    type Type = T::Type;
+
+    fn make_type(thread: &Thread) -> ArcType {
+        T::make_type(thread)
+    }
+}
+
+
+impl<'vm, T> Pushable<'vm> for Ser<T>
+where
+    T: Serialize,
+{
+    fn push(self, thread: &'vm Thread, context: &mut Context) -> Result<()> {
+        let mut serializer = Serializer {
+            thread: thread,
+            context: context,
+        };
+        self.0.serialize(&mut serializer)
+    }
+}
+
+impl ser::Error for Error {
+    fn custom<T>(msg: T) -> Self
+    where
+        T: fmt::Display,
+    {
+        Error::Message(format!("{}", msg))
+    }
+}
+
+struct Serializer<'t> {
+    thread: &'t Thread,
+    context: &'t mut Context,
+}
+
+impl<'t> Serializer<'t> {
+    fn to_value<T>(&mut self, value: T) -> Result<()>
+    where
+        T: Pushable<'t>,
+    {
+        value.push(self.thread, self.context)
+    }
+
+    fn alloc(&mut self, tag: VmTag, values: VmIndex) -> Result<()> {
+        let value = self.context.gc.alloc(Def {
+            tag: tag,
+            elems: &self.context.stack[self.context.stack.len() - values..],
+        })?;
+        for _ in 0..values {
+            self.context.stack.pop();
+        }
+        self.context.stack.push(Value::Data(value));
+        Ok(())
+    }
+}
+
+struct RecordSerializer<'s, 'vm: 's> {
+    serializer: &'s mut Serializer<'vm>,
+    variant_index: VmTag,
+    values: VmIndex,
+    fields: Vec<InternedStr>,
+}
+
+impl<'s, 'vm> Deref for RecordSerializer<'s, 'vm> {
+    type Target = Serializer<'vm>;
+    fn deref(&self) -> &Self::Target {
+        self.serializer
+    }
+}
+
+impl<'s, 'vm> DerefMut for RecordSerializer<'s, 'vm> {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        self.serializer
+    }
+}
+
+impl<'s, 'vm> RecordSerializer<'s, 'vm> {
+    fn new(serializer: &'s mut Serializer<'vm>, variant_index: u32) -> Self {
+        RecordSerializer {
+            serializer: serializer,
+            variant_index: variant_index,
+            values: 0,
+            fields: Vec::new(),
+        }
+    }
+}
+
+impl<'a, 'vm> ser::Serializer for &'a mut Serializer<'vm> {
+    type Ok = ();
+
+    // The error type when some error occurs during serialization.
+    type Error = Error;
+
+    // Associated types for keeping track of additional state while serializing
+    // compound data structures like sequences and maps. In this case no
+    // additional state is required beyond what is already stored in the
+    // Serializer struct.
+    type SerializeSeq = RecordSerializer<'a, 'vm>;
+    type SerializeTuple = RecordSerializer<'a, 'vm>;
+    type SerializeTupleStruct = RecordSerializer<'a, 'vm>;
+    type SerializeTupleVariant = RecordSerializer<'a, 'vm>;
+    type SerializeMap = RecordSerializer<'a, 'vm>;
+    type SerializeStruct = RecordSerializer<'a, 'vm>;
+    type SerializeStructVariant = RecordSerializer<'a, 'vm>;
+
+    // Here we go with the simple methods. The following 12 methods receive one
+    // of the primitive types of the data model and map it to JSON by appending
+    // into the output string.
+    fn serialize_bool(self, v: bool) -> Result<Self::Ok> {
+        self.to_value(v)
+    }
+
+    // JSON does not distinguish between different sizes of integers, so all
+    // signed integers will be serialized the same and all unsigned integers
+    // will be serialized the same. Other formats, especially compact binary
+    // formats, may need independent logic for the different sizes.
+    fn serialize_i8(self, v: i8) -> Result<Self::Ok> {
+        self.serialize_i64(v as i64)
+    }
+
+    fn serialize_i16(self, v: i16) -> Result<Self::Ok> {
+        self.serialize_i64(v as i64)
+    }
+
+    fn serialize_i32(self, v: i32) -> Result<Self::Ok> {
+        self.serialize_i64(v as i64)
+    }
+
+    // Not particularly efficient but this is example code anyway. A more
+    // performant approach would be to use the `itoa` crate.
+    fn serialize_i64(self, v: i64) -> Result<Self::Ok> {
+        self.to_value(v as isize)
+    }
+
+    fn serialize_u8(self, v: u8) -> Result<Self::Ok> {
+        self.serialize_u64(v as u64)
+    }
+
+    fn serialize_u16(self, v: u16) -> Result<Self::Ok> {
+        self.serialize_u64(v as u64)
+    }
+
+    fn serialize_u32(self, v: u32) -> Result<Self::Ok> {
+        self.serialize_u64(v as u64)
+    }
+
+    fn serialize_u64(self, v: u64) -> Result<Self::Ok> {
+        self.to_value(v as isize)
+    }
+
+    fn serialize_f32(self, v: f32) -> Result<Self::Ok> {
+        self.serialize_f64(v as f64)
+    }
+
+    fn serialize_f64(self, v: f64) -> Result<Self::Ok> {
+        self.to_value(v)
+    }
+
+    fn serialize_char(self, v: char) -> Result<Self::Ok> {
+        self.serialize_str(&v.to_string())
+    }
+
+    fn serialize_str(self, v: &str) -> Result<Self::Ok> {
+        self.to_value(v)
+    }
+
+    fn serialize_bytes(self, v: &[u8]) -> Result<Self::Ok> {
+        self.to_value(v)
+    }
+
+    // An absent optional is represented as the JSON `null`.
+    fn serialize_none(self) -> Result<Self::Ok> {
+        self.serialize_unit()
+    }
+
+    // A present optional is represented as just the contained value. Note that
+    // this is a lossy representation. For example the values `Some(())` and
+    // `None` both serialize as just `null`. Unfortunately this is typically
+    // what people expect when working with JSON. Other formats are encouraged
+    // to behave more intelligently if possible.
+    fn serialize_some<T>(self, value: &T) -> Result<Self::Ok>
+    where
+        T: ?Sized + Serialize,
+    {
+        value.serialize(self)
+    }
+
+    fn serialize_unit(self) -> Result<Self::Ok> {
+        self.context.stack.push(Value::Tag(0));
+        Ok(())
+    }
+
+    fn serialize_unit_struct(self, _name: &'static str) -> Result<Self::Ok> {
+        self.serialize_unit()
+    }
+
+    fn serialize_unit_variant(
+        self,
+        _name: &'static str,
+        variant_index: u32,
+        _variant: &'static str,
+    ) -> Result<Self::Ok> {
+        self.context.stack.push(Value::Tag(variant_index));
+        Ok(())
+    }
+
+    // As is done here, serializers are encouraged to treat newtype structs as
+    // insignificant wrappers around the data they contain.
+    fn serialize_newtype_struct<T>(self, _name: &'static str, value: &T) -> Result<Self::Ok>
+    where
+        T: ?Sized + Serialize,
+    {
+        value.serialize(self)
+    }
+
+    // Note that newtype variant (and all of the other variant serialization
+    // methods) refer exclusively to the "externally tagged" enum
+    // representation.
+    //
+    // Serialize this to JSON in externally tagged form as `{ NAME: VALUE }`.
+    fn serialize_newtype_variant<T>(
+        self,
+        _name: &'static str,
+        variant_index: u32,
+        _variant: &'static str,
+        value: &T,
+    ) -> Result<Self::Ok>
+    where
+        T: ?Sized + Serialize,
+    {
+        value.serialize(&mut *self)?;
+        self.alloc(variant_index, 1)
+    }
+
+    fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq> {
+        Ok(RecordSerializer::new(self, 0))
+    }
+
+    fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple> {
+        self.serialize_seq(Some(len))
+    }
+
+    fn serialize_tuple_struct(
+        self,
+        _name: &'static str,
+        len: usize,
+    ) -> Result<Self::SerializeTupleStruct> {
+        self.serialize_seq(Some(len))
+    }
+
+    fn serialize_tuple_variant(
+        self,
+        _name: &'static str,
+        variant_index: u32,
+        _variant: &'static str,
+        _len: usize,
+    ) -> Result<Self::SerializeTupleVariant> {
+        Ok(RecordSerializer::new(self, variant_index))
+    }
+
+    fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap> {
+        Ok(RecordSerializer::new(self, 0))
+    }
+
+    fn serialize_struct(self, _name: &'static str, len: usize) -> Result<Self::SerializeStruct> {
+        self.serialize_map(Some(len))
+    }
+
+    fn serialize_struct_variant(
+        self,
+        _name: &'static str,
+        variant_index: u32,
+        _variant: &'static str,
+        _len: usize,
+    ) -> Result<Self::SerializeStructVariant> {
+        Ok(RecordSerializer::new(self, variant_index))
+    }
+}
+
+impl<'a, 'vm> ser::SerializeSeq for RecordSerializer<'a, 'vm> {
+    type Ok = ();
+    type Error = Error;
+
+    fn serialize_element<T>(&mut self, value: &T) -> Result<()>
+    where
+        T: ?Sized + Serialize,
+    {
+        value.serialize(&mut **self)?;
+        self.values += 1;
+        Ok(())
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        self.serializer.alloc(self.variant_index, self.values)
+    }
+}
+
+impl<'a, 'vm> ser::SerializeTuple for RecordSerializer<'a, 'vm> {
+    type Ok = ();
+    type Error = Error;
+
+    fn serialize_element<T>(&mut self, value: &T) -> Result<()>
+    where
+        T: ?Sized + Serialize,
+    {
+        value.serialize(&mut **self)?;
+        self.values += 1;
+        Ok(())
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        self.serializer.alloc(self.variant_index, self.values)
+    }
+}
+
+impl<'a, 'vm> ser::SerializeTupleStruct for RecordSerializer<'a, 'vm> {
+    type Ok = ();
+    type Error = Error;
+
+    fn serialize_field<T>(&mut self, value: &T) -> Result<()>
+    where
+        T: ?Sized + Serialize,
+    {
+        value.serialize(&mut **self)?;
+        self.values += 1;
+        Ok(())
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        self.serializer.alloc(self.variant_index, self.values)
+    }
+}
+
+impl<'a, 'vm> ser::SerializeTupleVariant for RecordSerializer<'a, 'vm> {
+    type Ok = ();
+    type Error = Error;
+
+    fn serialize_field<T>(&mut self, value: &T) -> Result<()>
+    where
+        T: ?Sized + Serialize,
+    {
+        value.serialize(&mut **self)?;
+        self.values += 1;
+        Ok(())
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        self.serializer.alloc(self.variant_index, self.values)
+    }
+}
+
+impl<'a, 'vm> ser::SerializeMap for RecordSerializer<'a, 'vm> {
+    type Ok = ();
+    type Error = Error;
+
+    fn serialize_key<T>(&mut self, _key: &T) -> Result<()>
+    where
+        T: ?Sized + Serialize,
+    {
+        Ok(())
+    }
+
+    fn serialize_value<T>(&mut self, value: &T) -> Result<()>
+    where
+        T: ?Sized + Serialize,
+    {
+        value.serialize(&mut **self)?;
+        self.values += 1;
+        Ok(())
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        self.serializer.alloc(self.variant_index, self.values)
+    }
+}
+
+impl<'a, 'vm> ser::SerializeStruct for RecordSerializer<'a, 'vm> {
+    type Ok = ();
+    type Error = Error;
+
+    fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
+    where
+        T: ?Sized + Serialize,
+    {
+        let field = self.thread.global_env().intern(key)?;
+        self.fields.push(field);
+        value.serialize(&mut **self)?;
+        self.values += 1;
+        Ok(())
+    }
+
+    fn end(mut self) -> Result<Self::Ok> {
+        let tag = self.serializer.context.get_map(&self.fields);
+        self.serializer.alloc(tag, self.values)
+    }
+}
+
+// Similar to `SerializeTupleVariant`, here the `end` method is responsible for
+// closing both of the curly braces opened by `serialize_struct_variant`.
+impl<'a, 'vm> ser::SerializeStructVariant for RecordSerializer<'a, 'vm> {
+    type Ok = ();
+    type Error = Error;
+
+    fn serialize_field<T>(&mut self, _key: &'static str, value: &T) -> Result<()>
+    where
+        T: ?Sized + Serialize,
+    {
+        value.serialize(&mut **self)?;
+        self.values += 1;
+        Ok(())
+    }
+
+    fn end(self) -> Result<Self::Ok> {
+        self.serializer.alloc(self.variant_index, self.values)
+    }
+}
+
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use thread::{RootedThread, RootedValue, ThreadInternal};
+
+    fn to_value<T>(thread: &Thread, value: &T) -> Result<Value>
+    where
+        T: ?Sized + Serialize,
+    {
+        let mut context = thread.context();
+        Ser(value).push(thread, &mut context)?;
+        Ok(context.stack.pop())
+    }
+
+    fn make_value<'vm, T>(thread: &'vm Thread, value: T) -> RootedValue<&'vm Thread>
+    where
+        T: Pushable<'vm>,
+    {
+        let mut context = thread.context();
+        value.push(thread, &mut context).unwrap();
+        thread.root_value(context.stack.pop())
+    }
+
+    #[test]
+    fn bool() {
+        let thread = RootedThread::new();
+        assert_eq!(to_value(&thread, &true).unwrap(), Value::Tag(1));
+    }
+}
diff --git a/vm/src/core/mod.rs b/vm/src/core/mod.rs
index 6df5a0be..c18b2476 100644
--- a/vm/src/core/mod.rs
+++ b/vm/src/core/mod.rs
@@ -377,7 +377,8 @@ impl<'a, 'e> Translator<'a, 'e> {
                 }
             }
             ast::Expr::IfElse(ref pred, ref if_true, ref if_false) => {
-                let alts: SmallVec<[_; 2]> = collect![
+                let alts: SmallVec<[_; 2]> =
+                    collect![
                     Alternative {
                         pattern: Pattern::Constructor(self.bool_constructor(true), vec![]),
                         expr: self.translate_alloc(if_true),
@@ -1037,7 +1038,7 @@ impl<'a, 'e> PatternTranslator<'a, 'e> {
                 ast::Pattern::Record { .. } |
                 ast::Pattern::Tuple { .. } => CType::Record,
                 ast::Pattern::Constructor(_, _) => CType::Constructor,
-                ast::Pattern::Error => panic!("ICE: Error pattern survived typechecking")
+                ast::Pattern::Error => panic!("ICE: Error pattern survived typechecking"),
             }
         }
 
@@ -1144,7 +1145,7 @@ impl<'a, 'e> PatternTranslator<'a, 'e> {
                         }
                     }
                 }
-                ast::Pattern::Error => ()
+                ast::Pattern::Error => (),
             }
         }
         if record_fields.is_empty() {
diff --git a/vm/src/field_map.rs b/vm/src/field_map.rs
index da7a2b49..c4a63c9d 100644
--- a/vm/src/field_map.rs
+++ b/vm/src/field_map.rs
@@ -3,6 +3,7 @@ use base::fnv::FnvMap;
 use interner::InternedStr;
 use types::{VmTag, VmIndex};
 
+#[derive(Debug)]
 pub struct FieldMap {
     /// Maps fields into a tag
     tags: FnvMap<Vec<InternedStr>, VmTag>,
diff --git a/vm/src/lib.rs b/vm/src/lib.rs
index 31b2c860..8b78ae9e 100644
--- a/vm/src/lib.rs
+++ b/vm/src/lib.rs
@@ -18,6 +18,13 @@ extern crate pretty;
 #[macro_use]
 extern crate futures;
 
+#[cfg(feature = "serde")]
+#[macro_use]
+extern crate serde;
+#[cfg(all(feature = "serde", test))]
+#[macro_use]
+extern crate serde_derive;
+
 #[macro_use]
 extern crate gluon_base as base;
 extern crate gluon_check as check;
@@ -47,26 +54,36 @@ mod source_map;
 mod value;
 mod vm;
 
+use std::marker::PhantomData;
+
 use api::ValueRef;
 use value::Value;
 use types::VmIndex;
 use base::types::ArcType;
 use base::symbol::Symbol;
 
-#[derive(Debug)]
-pub struct Variants<'a>(&'a Value);
+unsafe fn forget_lifetime<'a, 'b, T: ?Sized>(x: &'a T) -> &'b T {
+    ::std::mem::transmute(x)
+}
+
+#[derive(Copy, Clone, Debug)]
+pub struct Variants<'a>(Value, PhantomData<&'a Value>);
 
 impl<'a> Variants<'a> {
-    /// Creates a new `Variants` value which assumes that `value` is alive for the lifetime of the
+    /// Creates a new `Variants` value which assumes that `value` is rooted for the lifetime of the
     /// value
     pub unsafe fn new(value: &Value) -> Variants {
-        Variants(value)
+        Variants::with_root(*value, value)
+    }
+
+    pub unsafe fn with_root<T: ?Sized>(value: Value, _root: &T) -> Variants {
+        Variants(value, PhantomData)
     }
 
     /// Returns an instance of `ValueRef` which allows users to safely retrieve the interals of a
     /// value
-    pub fn as_ref(&self) -> ValueRef {
-        ValueRef::new(self.0)
+    pub fn as_ref(&self) -> ValueRef<'a> {
+        unsafe { ValueRef::rooted_new(self.0) }
     }
 }
 
diff --git a/vm/src/primitives.rs b/vm/src/primitives.rs
index 65a229b6..3ad888f8 100644
--- a/vm/src/primitives.rs
+++ b/vm/src/primitives.rs
@@ -79,9 +79,11 @@ fn array_append<'vm>(
             Err(err) => return RuntimeResult::Panic(err),
         }
     };
-    RuntimeResult::Return(
-        Getable::from_value(lhs.vm(), Variants(&Value::Array(value))).expect("Array"),
-    )
+    unsafe {
+        RuntimeResult::Return(
+            Getable::from_value(lhs.vm(), Variants::new(&Value::Array(value))).expect("Array"),
+        )
+    }
 }
 
 fn string_append(lhs: WithVM<&str>, rhs: &str) -> RuntimeResult<String, Error> {
@@ -117,17 +119,17 @@ fn string_append(lhs: WithVM<&str>, rhs: &str) -> RuntimeResult<String, Error> {
 
     let vm = lhs.vm;
     let lhs = lhs.value;
-    let value = unsafe {
+    unsafe {
         let mut context = vm.context();
         let result = context.alloc(StrAppend { lhs: lhs, rhs: rhs });
-        match result {
+        let value = match result {
             Ok(x) => GcStr::from_utf8_unchecked(x),
             Err(err) => return RuntimeResult::Panic(err),
-        }
-    };
-    RuntimeResult::Return(
-        Getable::from_value(vm, Variants(&Value::String(value))).expect("Array"),
-    )
+        };
+        RuntimeResult::Return(
+            Getable::from_value(vm, Variants::new(&Value::String(value))).expect("Array"),
+        )
+    }
 }
 
 fn string_slice(s: &str, start: usize, end: usize) -> RuntimeResult<&str, String> {
diff --git a/vm/src/stack.rs b/vm/src/stack.rs
index cd4094e2..7402c090 100644
--- a/vm/src/stack.rs
+++ b/vm/src/stack.rs
@@ -213,10 +213,12 @@ impl<'a: 'b, 'b> StackFrame<'b> {
     }
 
     pub fn get_variants(&self, index: VmIndex) -> Option<Variants> {
-        if index < self.len() {
-            Some(Variants(&self[index]))
-        } else {
-            None
+        unsafe {
+            if index < self.len() {
+                Some(Variants::new(&self[index]))
+            } else {
+                None
+            }
         }
     }
 
diff --git a/vm/src/thread.rs b/vm/src/thread.rs
index 7a4215eb..16c437e1 100644
--- a/vm/src/thread.rs
+++ b/vm/src/thread.rs
@@ -30,8 +30,8 @@ use source_map::LocalIter;
 use stack::{Frame, Stack, StackFrame, State};
 use types::*;
 use vm::{GlobalVmState, VmEnv};
-use value::{Value, ClosureData, ClosureInitDef, ClosureDataDef, Def, ExternFunction, GcStr,
-            BytecodeFunction, Callable, PartialApplicationDataDef, Userdata};
+use value::{Value, ClosureData, ClosureInitDef, ClosureDataDef, DataStruct, Def, ExternFunction,
+            GcStr, BytecodeFunction, Callable, PartialApplicationDataDef, Userdata};
 
 use value::Value::{Int, Float, String, Data, Function, PartialApplication, Closure};
 
@@ -461,9 +461,11 @@ impl Thread {
         // Finally check that type of the returned value is correct
         let expected = T::make_type(self);
         if check_signature(&*env, &expected, &actual) {
-            T::from_value(self, Variants(&value)).ok_or_else(|| {
-                Error::UndefinedBinding(name.into())
-            })
+            unsafe {
+                T::from_value(self, Variants::new(&value)).ok_or_else(|| {
+                    Error::UndefinedBinding(name.into())
+                })
+            }
         } else {
             Err(Error::WrongType(expected, actual.into_owned()))
         }
@@ -641,7 +643,7 @@ where
 
     fn can_share_values_with(&self, gc: &mut Gc, other: &Thread) -> bool;
 
-    fn lookup_field(&self, value: Value, field: &str) -> Result<Value>;
+    fn lookup_field(&self, data: &DataStruct, field: &str) -> Result<Value>;
 }
 
 impl ThreadInternal for Thread {
@@ -821,16 +823,13 @@ impl ThreadInternal for Thread {
         false
     }
 
-    fn lookup_field(&self, value: Value, field: &str) -> Result<Value> {
+    fn lookup_field(&self, data: &DataStruct, field: &str) -> Result<Value> {
         let context = self.context.lock().unwrap();
         let str_index = self.global_env().intern(field)?;
         context
             .record_map
-            .get_offset(0, str_index)
-            .and_then(|index| match value {
-                Data(data) => data.fields.get(index as usize).cloned(),
-                _ => None,
-            })
+            .get_offset(data.tag, str_index)
+            .and_then(|index| data.fields.get(index as usize).cloned())
             .ok_or_else(|| {
                 Error::Message(format!(
                     "Internal error: Undefined record field {}",
@@ -1024,6 +1023,10 @@ impl Context {
         self.max_stack_size = limit;
     }
 
+    pub fn get_map(&mut self, fields: &[InternedStr]) -> VmTag {
+        self.record_map.get_map(fields)
+    }
+
     /// "Returns a future", letting the virtual machine know that `future` must be resolved to
     /// produce the actual value.
     ///
@@ -1834,19 +1837,18 @@ where
     F: FnOnce(T, T) -> Value,
     T: Getable<'b> + fmt::Debug,
 {
-    let r = stack.pop();
-    let l = stack.pop();
-    match (
-        T::from_value(vm, Variants(&l)),
-        T::from_value(vm, Variants(&r)),
-    ) {
-        (Some(l), Some(r)) => {
-            let result = f(l, r);
-            // pushing numbers should never return an error so unwrap
-            stack.stack.push(result);
+    let (l, r) = {
+        let r = stack.get_variants(stack.len() - 1).unwrap();
+        let l = stack.get_variants(stack.len() - 2).unwrap();
+        match (T::from_value(vm, l), T::from_value(vm, r)) {
+            (Some(l), Some(r)) => (l, r),
+            _ => panic!("{:?} `op` {:?}", l, r),
         }
-        _ => panic!("{:?} `op` {:?}", l, r),
-    }
+    };
+    let result = f(l, r);
+    stack.pop();
+    stack.pop();
+    stack.stack.push(result);
 }
 
 fn debug_instruction(
diff --git a/vm/src/value.rs b/vm/src/value.rs
index 38987528..985addc4 100644
--- a/vm/src/value.rs
+++ b/vm/src/value.rs
@@ -16,7 +16,7 @@ use compiler::DebugInfo;
 use gc::{Gc, GcPtr, Generation, Traverseable, DataDef, Move, WriteOnly};
 use array::Array;
 use thread::{Thread, Status};
-use {Error, Result};
+use {Error, Result, Variants};
 
 use self::Value::{Int, Float, String, Function, PartialApplication, Closure};
 
@@ -175,12 +175,19 @@ mod gc_str {
     use super::ValueArray;
     use gc::{Gc, GcPtr, Generation, Traverseable};
 
+    use std::fmt;
     use std::str;
     use std::ops::Deref;
 
-    #[derive(Copy, Clone, Debug, PartialEq)]
+    #[derive(Copy, Clone, PartialEq)]
     pub struct GcStr(GcPtr<ValueArray>);
 
+    impl fmt::Debug for GcStr {
+        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+            f.debug_tuple("GcStr").field(&&**self).finish()
+        }
+    }
+
     impl Eq for GcStr {}
 
     impl GcStr {
@@ -768,12 +775,20 @@ macro_rules! on_array {
 }
 
 
-#[derive(Debug)]
 pub struct ValueArray {
     repr: Repr,
     array: Array<()>,
 }
 
+impl fmt::Debug for ValueArray {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        f.debug_struct("ValueArray")
+            .field("repr", &self.repr)
+            .field("array", on_array!(self, |x| x as &fmt::Debug))
+            .finish()
+    }
+}
+
 impl PartialEq for ValueArray {
     fn eq(&self, other: &ValueArray) -> bool {
         self.repr == other.repr && self.iter().zip(other.iter()).all(|(l, r)| l == r)
@@ -802,6 +817,29 @@ impl<'a> Iterator for Iter<'a> {
     }
 }
 
+pub struct VariantIter<'a> {
+    array: &'a ValueArray,
+    index: usize,
+}
+
+impl<'a> Iterator for VariantIter<'a> {
+    type Item = Variants<'a>;
+    fn next(&mut self) -> Option<Self::Item> {
+        if self.index < self.array.len() {
+            let value = self.array.get(self.index);
+            self.index += 1;
+            Some(unsafe { Variants::with_root(value, self.array) })
+        } else {
+            None
+        }
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        let i = self.array.len() - self.index;
+        (i, Some(i))
+    }
+}
+
 impl Traverseable for ValueArray {
     fn traverse(&self, gc: &mut Gc) {
         on_array!(*self, |array: &Array<_>| array.traverse(gc))
@@ -839,6 +877,13 @@ impl ValueArray {
         }
     }
 
+    pub fn variant_iter(&self) -> VariantIter {
+        VariantIter {
+            array: self,
+            index: 0,
+        }
+    }
+
     pub fn size_of(repr: Repr, len: usize) -> usize {
         ::std::mem::size_of::<ValueArray>() + repr.size_of() * len
     }


Test patch:
diff --git a/tests/de.rs b/tests/de.rs
new file mode 100644
index 00000000..ef1dcbc0
--- /dev/null
+++ b/tests/de.rs
@@ -0,0 +1,188 @@
+extern crate env_logger;
+#[macro_use]
+extern crate serde_derive;
+
+extern crate gluon;
+
+use gluon::base::types::{ArcType, Field, Type};
+use gluon::base::symbol::Symbol;
+use gluon::vm::api::{Getable, Hole, OpaqueValue, VmType};
+use gluon::vm::api::de::{self, De};
+use gluon::vm::thread::Thread;
+use gluon::{Compiler, new_vm};
+
+#[test]
+fn bool() {
+    let _ = env_logger::init();
+
+    let thread = new_vm();
+    let (De(b), _) = Compiler::new()
+        .run_expr::<De<bool>>(&thread, "test", "True")
+        .unwrap_or_else(|err| panic!("{}", err));
+    assert_eq!(b, true);
+}
+
+#[derive(Debug, PartialEq, Deserialize)]
+struct Record {
+    test: i32,
+    string: String,
+}
+
+impl VmType for Record {
+    type Type = Self;
+
+    fn make_type(thread: &Thread) -> ArcType {
+        Type::poly_record(
+            vec![],
+            vec![
+                Field {
+                    name: Symbol::from("test"),
+                    typ: i32::make_type(thread),
+                },
+                Field {
+                    name: Symbol::from("string"),
+                    typ: str::make_type(thread),
+                },
+            ],
+            Type::hole(),
+        )
+    }
+}
+
+#[test]
+fn option() {
+    let _ = env_logger::init();
+
+    let thread = new_vm();
+    let (De(opt), _) = Compiler::new()
+        .run_expr::<De<Option<f64>>>(&thread, "test", r#" Some 1.0 "#)
+        .unwrap_or_else(|err| panic!("{}", err));
+    assert_eq!(opt, Some(1.0));
+}
+
+#[test]
+fn partial_record() {
+    let _ = env_logger::init();
+
+    let thread = new_vm();
+    let (De(record), _) = Compiler::new()
+        .run_expr::<De<Record>>(
+            &thread,
+            "test",
+            r#" { test = 1, extra = 1.0, string = "test", } "#,
+        )
+        .unwrap_or_else(|err| panic!("{}", err));
+    assert_eq!(
+        record,
+        Record {
+            test: 1,
+            string: "test".to_string(),
+        }
+    );
+}
+
+
+#[derive(Debug, PartialEq, Deserialize)]
+struct OptionalFieldRecord {
+    test: Option<i32>,
+}
+
+impl VmType for OptionalFieldRecord {
+    type Type = Self;
+
+    fn make_type(thread: &Thread) -> ArcType {
+        Type::poly_record(
+            vec![],
+            vec![
+                Field {
+                    name: Symbol::from("test"),
+                    typ: Option::<i32>::make_type(thread),
+                },
+            ],
+            Type::hole(),
+        )
+    }
+}
+
+#[test]
+fn optional_field() {
+    let _ = env_logger::init();
+
+    let thread = new_vm();
+
+    let (value, _) = Compiler::new()
+        .run_expr::<OpaqueValue<&Thread, Hole>>(&thread, "test", r#" { } "#)
+        .unwrap_or_else(|err| panic!("{}", err));
+    assert_eq!(
+        De::<OptionalFieldRecord>::from_value(&thread, value.get_variants()).map(|x| x.0),
+        Some(OptionalFieldRecord { test: None })
+    );
+
+    let (value, _) = Compiler::new()
+        .run_expr::<OpaqueValue<&Thread, Hole>>(&thread, "test", r#" { test = Some 2 } "#)
+        .unwrap_or_else(|err| panic!("{}", err));
+    assert_eq!(
+        De::<OptionalFieldRecord>::from_value(&thread, value.get_variants()).map(|x| x.0),
+        Some(OptionalFieldRecord { test: Some(2) })
+    );
+
+    let (value, _) = Compiler::new()
+        .run_expr::<OpaqueValue<&Thread, Hole>>(&thread, "test", r#" { test = 1 } "#)
+        .unwrap_or_else(|err| panic!("{}", err));
+
+    let typ = Type::poly_record(
+        vec![],
+        vec![
+            Field {
+                name: Symbol::from("test"),
+                typ: i32::make_type(&thread),
+            },
+        ],
+        Type::hole(),
+    );
+    assert_eq!(
+        de::from_value(&thread, value.get_variants(), &typ).ok(),
+        Some(OptionalFieldRecord { test: Some(1) })
+    );
+
+}
+
+#[derive(Debug, PartialEq, Deserialize)]
+enum Enum {
+    A(i32),
+    B { string: String, test: f64 },
+    C(i32, i32),
+}
+
+impl VmType for Enum {
+    type Type = Self;
+
+    fn make_type(thread: &Thread) -> ArcType {
+        thread.find_type_info("test.Enum").unwrap().into_type()
+    }
+}
+
+#[test]
+fn enum_() {
+    let _ = env_logger::init();
+
+    let thread = new_vm();
+    Compiler::new()
+        .implicit_prelude(false)
+        .load_script(
+            &thread,
+            "test",
+            r#" type Enum = | A Int | B String Float | C Int Int in { Enum } "#,
+        )
+        .unwrap_or_else(|err| panic!("{}", err));
+
+    let (De(enum_), _) = Compiler::new()
+        .implicit_prelude(false)
+        .run_expr::<De<Enum>>(
+            &thread,
+            "test",
+            r#" let { Enum } = import! "test" in C 0 1 "#,
+        )
+        .unwrap_or_else(|err| panic!("{}", err));
+    assert_eq!(enum_, Enum::C(0, 1));
+}