Repository: gluon-lang/gluon
Pull request #639 (2018-09-23 16:26:18)

 refactor(vm): Split up the derive implementations 


Patch:
diff --git a/repl/src/main.rs b/repl/src/main.rs
index 0408d6e3..b1f16cfb 100644
--- a/repl/src/main.rs
+++ b/repl/src/main.rs
@@ -14,7 +14,6 @@ extern crate log;
 #[macro_use]
 extern crate serde_derive;
 extern crate structopt;
-#[allow(unused_imports)]
 #[macro_use]
 extern crate structopt_derive;
 extern crate tokio;
@@ -142,28 +141,17 @@ const LONG_VERSION: &str = concat!(crate_version!(), "\n", "commit: ", env!("GIT
 pub struct Opt {
     #[structopt(short = "i", long = "interactive", help = "Starts the repl")]
     interactive: bool,
-
     #[structopt(
         long = "color",
         default_value = "auto",
         help = "Coloring: auto, always, always-ansi, never"
     )]
     color: Color,
-
-    #[structopt(
-        long = "prompt",
-        short = "p",
-        default_value = "> ",
-        help = "String printed as the prompt for the repl"
-    )]
-    prompt: String,
-
     #[structopt(
         name = "FILE",
         help = "Executes each file as a gluon program"
     )]
     input: Vec<String>,
-
     #[structopt(subcommand)]
     subcommand_opt: Option<SubOpt>,
 }
@@ -282,8 +270,7 @@ fn run(
         }
         None => if opt.interactive {
             let mut runtime = Runtime::new()?;
-            let prompt = opt.prompt.clone();
-            runtime.block_on(future::lazy(move || repl::run(color, &prompt)))?;
+            runtime.block_on(future::lazy(move || repl::run(color)))?;
         } else if !opt.input.is_empty() {
             run_files(compiler, &vm, &opt.input)?;
         } else {
diff --git a/repl/src/repl.glu b/repl/src/repl.glu
index 9b576cd9..4e501ad0 100644
--- a/repl/src/repl.glu
+++ b/repl/src/repl.glu
@@ -167,13 +167,7 @@ let store line : String -> IO ReplAction =
         io.load_script binding expr *> wrap Continue
     | None -> io.println "Expected binding in definition" *> wrap Continue
 
-type Repl = {
-    commands : Commands,
-    editor : Editor,
-    cpu_pool : CpuPool,
-    color : Color,
-    prompt : String
-}
+type Repl = { commands : Commands, editor : Editor, cpu_pool : CpuPool, color : Color }
 
 let loop repl : Repl -> IO () =
     let run_line line =
@@ -188,7 +182,7 @@ let loop repl : Repl -> IO () =
                 repl_prim.finish_or_interrupt repl.cpu_pool eval_thread eval_action
             io.catch action io.println *> wrap Continue
 
-    do line_result = rustyline.readline repl.editor repl.prompt
+    do line_result = rustyline.readline repl.editor "> "
     match line_result with
     | Err Eof -> wrap ()
     | Err Interrupted -> loop repl
@@ -200,12 +194,12 @@ let loop repl : Repl -> IO () =
             do _ = rustyline.save_history repl.editor
             wrap ()
 
-let run color prompt : Color -> String -> IO () =
+let run color : Color -> IO () =
     do _ = io.println "gluon (:h for help, :q to quit)"
     do editor = rustyline.new_editor ()
     do cpu_pool = repl_prim.new_cpu_pool 1
     let commands = make_commands cpu_pool
-    let repl = { commands, editor, cpu_pool, color, prompt }
+    let repl = { commands, editor, cpu_pool, color }
     loop repl
 
 run
diff --git a/repl/src/repl.rs b/repl/src/repl.rs
index 648a0482..9bdfaf7b 100644
--- a/repl/src/repl.rs
+++ b/repl/src/repl.rs
@@ -537,7 +537,7 @@ fn compile_repl(compiler: &mut Compiler, vm: &Thread) -> Result<(), GluonError>
 }
 
 #[allow(dead_code)]
-pub fn run(color: Color, prompt: &str) -> impl Future<Item = (), Error = Box<StdError + Send + Sync + 'static>> {
+pub fn run(color: Color) -> impl Future<Item = (), Error = Box<StdError + Send + Sync + 'static>> {
     let vm = ::gluon::VmBuilder::new().build();
 
     let mut compiler = Compiler::new();
@@ -547,12 +547,12 @@ pub fn run(color: Color, prompt: &str) -> impl Future<Item = (), Error = Box<Std
         Either::A
     );
 
-    let mut repl: OwnedFunction<fn(_, _) -> _> =
+    let mut repl: OwnedFunction<fn(Ser<Color>) -> IO<()>> =
         try_future!(vm.get_global("repl"), Either::A);
     debug!("Starting repl");
     Either::B(
-        repl.call_async(Ser(color), prompt)
-            .map(|_: IO<()>| ())
+        repl.call_async(Ser(color))
+            .map(|_| ())
             .map_err(|err| err.into()),
     )
 }
@@ -584,7 +584,7 @@ mod tests {
         let _ = ::env_logger::try_init();
         let vm = new_vm();
         compile_repl(&mut Compiler::new(), &vm).unwrap_or_else(|err| panic!("{}", err));
-        let repl: Result<FunctionRef<fn(Color, String) -> IO<()>>, _> = vm.get_global("repl");
+        let repl: Result<FunctionRef<fn(Color) -> IO<()>>, _> = vm.get_global("repl");
         assert!(repl.is_ok(), "{}", repl.err().unwrap());
     }
 
diff --git a/src/import.rs b/src/import.rs
index 2d7023cc..a19459c0 100644
--- a/src/import.rs
+++ b/src/import.rs
@@ -23,12 +23,9 @@ use base::pos::{self, BytePos, Span};
 use base::symbol::Symbol;
 use base::types::ArcType;
 
-use vm::{
-    self,
-    macros::{Error as MacroError, Macro, MacroExpander, MacroFuture},
-    thread::{Thread, ThreadInternal},
-    ExternLoader, ExternModule,
-};
+use vm::macros::{Error as MacroError, Macro, MacroExpander, MacroFuture};
+use vm::thread::{Thread, ThreadInternal};
+use vm::{ExternLoader, ExternModule};
 
 use super::Compiler;
 
@@ -180,8 +177,8 @@ impl<I> Import<I> {
             Some(tup) => UnloadedModule::Source(Cow::Borrowed(tup.1)),
             None => {
                 {
-                    let mut loaders = self.loaders.write().unwrap();
-                    if let Some(loader) = loaders.get_mut(module) {
+                    let loaders = self.loaders.read().unwrap();
+                    if let Some(loader) = loaders.get(module) {
                         let value = loader(vm)?;
                         return Ok(UnloadedModule::Extern(value));
                     }
@@ -195,7 +192,8 @@ impl<I> Import<I> {
                             Ok(file) => Some(file),
                             Err(_) => None,
                         }
-                    }).next();
+                    })
+                    .next();
                 let mut file = file.ok_or_else(|| {
                     Error::String(format!(
                         "Could not find module '{}'. Searched {}.",
@@ -394,14 +392,7 @@ impl<I> Import<I> {
 ///     }
 /// }
 /// ```
-pub fn add_extern_module<F>(thread: &Thread, name: &str, loader: F)
-where
-    F: FnMut(&Thread) -> vm::Result<ExternModule> + Send + Sync + 'static,
-{
-    add_extern_module_(thread, name, Box::new(loader))
-}
-
-fn add_extern_module_(thread: &Thread, name: &str, loader: ExternLoader) {
+pub fn add_extern_module(thread: &Thread, name: &str, loader: ExternLoader) {
     let opt_macro = thread.get_macros().get("import");
     let import = opt_macro
         .as_ref()
@@ -417,7 +408,7 @@ fn add_extern_module_(thread: &Thread, name: &str, loader: ExternLoader) {
 
 macro_rules! add_extern_module_if {
     (
-        #[cfg($($features: tt)*)],
+        #[cfg($($features: tt)*)], 
         available_if = $msg: expr,
         args($vm: expr, $mod_name: expr, $loader: path)
     ) => {{
@@ -446,7 +437,8 @@ fn get_state<'m>(macros: &'m mut MacroExpander) -> &'m mut State {
                 visited: Vec::new(),
                 modules_with_errors: FnvMap::default(),
             })
-        }).downcast_mut::<State>()
+        })
+        .downcast_mut::<State>()
         .unwrap()
 }
 
diff --git a/vm/src/derive.rs b/vm/src/derive.rs
deleted file mode 100644
index 51b7c436..00000000
--- a/vm/src/derive.rs
+++ /dev/null
@@ -1,966 +0,0 @@
-use base::ast::{
-    Alternative, Argument, AstType, Expr, ExprField, Lambda, Literal, Pattern, PatternField,
-    SpannedExpr, SpannedPattern, TypeBinding, TypedIdent, ValueBinding,
-};
-use base::metadata::Attribute;
-use base::pos::{self, BytePos, Span};
-use base::symbol::{Symbol, Symbols};
-use base::types::{arg_iter, remove_forall, row_iter, Type};
-
-use macros::Error;
-
-pub fn generate(
-    symbols: &mut Symbols,
-    derive: &Attribute,
-    bind: &TypeBinding<Symbol>,
-) -> Result<Vec<ValueBinding<Symbol>>, Error> {
-    match derive.arguments {
-        Some(ref args) => args
-            .split(',')
-            .map(|s| s.trim())
-            .map(|arg| {
-                Ok(match arg {
-                    "Eq" => generate_eq(symbols, bind),
-                    "Show" => generate_show(symbols, bind),
-                    "Deserialize" => generate_deserialize(symbols, bind),
-                    "Serialize" => generate_serialize(symbols, bind),
-                    _ => return Err(format!("`{}` is not a type that can be derived", arg)),
-                })
-            }).collect::<Result<_, _>>()?,
-        _ => Err("Invalid `derive` attribute".into()),
-    }
-}
-
-fn sequence_actions(
-    symbols: &mut Symbols,
-    span: Span<BytePos>,
-    field_symbols: &[TypedIdent<Symbol>],
-    packed_expr: SpannedExpr<Symbol>,
-    action: &mut FnMut(&Symbol) -> SpannedExpr<Symbol>,
-) -> SpannedExpr<Symbol> {
-    let pack_record = pos::spanned(
-        span,
-        Expr::Lambda(Lambda {
-            args: field_symbols
-                .iter()
-                .cloned()
-                .map(|id| Argument::explicit(pos::spanned(span, id)))
-                .collect(),
-            body: Box::new(packed_expr),
-            id: TypedIdent::new(symbols.symbol("pack_record")),
-        }),
-    );
-    let map_expr = app(
-        span,
-        symbols.symbol("map"),
-        vec![
-            paren(span, pack_record),
-            paren(span, action(&field_symbols.first().expect("FIXME").name)),
-        ],
-    );
-
-    field_symbols.iter().skip(1).fold(map_expr, |prev, symbol| {
-        let deserialize_field = action(&symbol.name);
-        infix(span, prev, symbols.symbol("<*>"), deserialize_field)
-    })
-}
-
-fn generate_record_pattern<I>(
-    span: Span<BytePos>,
-    row: &AstType<Symbol>,
-    symbols: I,
-) -> SpannedPattern<Symbol>
-where
-    I: IntoIterator<Item = TypedIdent<Symbol>>,
-{
-    pos::spanned(
-        span,
-        Pattern::Record {
-            implicit_import: None,
-            typ: Type::hole(),
-            types: Vec::new(),
-            fields: row_iter(row)
-                .zip(symbols)
-                .map(|(field, bind)| PatternField {
-                    name: pos::spanned(span, field.name.clone()),
-                    value: Some(pos::spanned(span, Pattern::Ident(bind))),
-                }).collect(),
-        },
-    )
-}
-
-fn generate_import(
-    span: Span<BytePos>,
-    symbols: &mut Symbols,
-    type_fields: &[&str],
-    fields: &[&str],
-    import: &str,
-) -> ValueBinding<Symbol> {
-    generate_import_(span, symbols, type_fields, fields, false, import)
-}
-
-fn generate_import_(
-    span: Span<BytePos>,
-    symbols: &mut Symbols,
-    type_fields: &[&str],
-    fields: &[&str],
-    implicit_import: bool,
-    import: &str,
-) -> ValueBinding<Symbol> {
-    ValueBinding {
-        name: pos::spanned(
-            span,
-            Pattern::Record {
-                implicit_import: if implicit_import {
-                    Some(pos::spanned(span, Symbol::from("implicit_import")))
-                } else {
-                    None
-                },
-                typ: Type::hole(),
-                types: type_fields
-                    .iter()
-                    .map(|f| PatternField {
-                        name: pos::spanned(span, symbols.symbol(*f)),
-                        value: None,
-                    }).collect(),
-                fields: fields
-                    .iter()
-                    .map(|f| PatternField {
-                        name: pos::spanned(span, symbols.symbol(*f)),
-                        value: None,
-                    }).collect(),
-            },
-        ),
-        args: Vec::new(),
-        expr: app(
-            span,
-            symbols.symbol("import!"),
-            vec![project(span, symbols, import)],
-        ),
-        metadata: Default::default(),
-        typ: None,
-        resolved_type: Type::hole(),
-    }
-}
-
-fn project(span: Span<BytePos>, symbols: &mut Symbols, p: &str) -> SpannedExpr<Symbol> {
-    p.split('.')
-        .fold(None, |acc, name| {
-            let symbol = symbols.symbol(name);
-            Some(match acc {
-                Some(expr) => {
-                    pos::spanned(span, Expr::Projection(Box::new(expr), symbol, Type::hole()))
-                }
-                None => ident(span, symbol),
-            })
-        }).unwrap()
-}
-
-fn ident(span: Span<BytePos>, s: Symbol) -> SpannedExpr<Symbol> {
-    pos::spanned(span, Expr::Ident(TypedIdent::new(s)))
-}
-
-fn literal(span: Span<BytePos>, s: &str) -> SpannedExpr<Symbol> {
-    pos::spanned(span, Expr::Literal(Literal::String(s.to_string())))
-}
-fn paren(span: Span<BytePos>, expr: SpannedExpr<Symbol>) -> SpannedExpr<Symbol> {
-    pos::spanned(
-        span,
-        Expr::Tuple {
-            elems: vec![expr],
-            typ: Type::hole(),
-        },
-    )
-}
-
-fn app(span: Span<BytePos>, func: Symbol, args: Vec<SpannedExpr<Symbol>>) -> SpannedExpr<Symbol> {
-    pos::spanned(
-        span,
-        Expr::App {
-            func: Box::new(pos::spanned(span, Expr::Ident(TypedIdent::new(func)))),
-            implicit_args: Vec::new(),
-            args,
-        },
-    )
-}
-
-fn infix(
-    span: Span<BytePos>,
-    lhs: SpannedExpr<Symbol>,
-    op: Symbol,
-    rhs: SpannedExpr<Symbol>,
-) -> SpannedExpr<Symbol> {
-    pos::spanned(
-        span,
-        Expr::Infix {
-            lhs: lhs.into(),
-            op: pos::spanned(span, TypedIdent::new(op)),
-            rhs: rhs.into(),
-            implicit_args: Vec::new(),
-        },
-    )
-}
-
-fn is_self_type(self_: &Symbol, typ: &AstType<Symbol>) -> bool {
-    match **typ {
-        Type::App(ref f, _) => is_self_type(self_, f),
-        Type::Alias(ref alias) => alias.name == *self_,
-        Type::Ident(ref id) => id == self_,
-        _ => false,
-    }
-}
-
-fn binding_type(
-    symbols: &mut Symbols,
-    derive_type_name: &str,
-    self_type: AstType<Symbol>,
-    bind: &TypeBinding<Symbol>,
-) -> AstType<Symbol> {
-    let derive_type: AstType<_> = Type::ident(symbols.symbol(derive_type_name));
-    Type::function_implicit(
-        bind.alias
-            .value
-            .params()
-            .iter()
-            .cloned()
-            .map(|g| Type::app(derive_type.clone(), collect![Type::generic(g)])),
-        Type::app(derive_type.clone(), collect![self_type]),
-    )
-}
-
-fn generate_show(
-    symbols: &mut Symbols,
-    bind: &TypeBinding<Symbol>,
-) -> Result<ValueBinding<Symbol>, Error> {
-    let span = bind.name.span;
-
-    let x = Symbol::from("x");
-    let show_fn = TypedIdent::new(symbols.symbol("show_"));
-
-    let show_expr = match **remove_forall(bind.alias.value.unresolved_type()) {
-        Type::Variant(ref variants) => {
-            let alts = row_iter(variants)
-                .map(|variant| {
-                    let pattern_args: Vec<_> = arg_iter(&variant.typ)
-                        .enumerate()
-                        .map(|(i, typ)| {
-                            (
-                                is_self_type(&bind.alias.value.name, typ),
-                                TypedIdent::new(Symbol::from(format!("arg_{}", i))),
-                            )
-                        }).collect();
-
-                    let expr = {
-                        let open_brace = literal(span, variant.name.declared_name());
-
-                        pattern_args
-                            .iter()
-                            .fold(open_brace, |acc, &(self_type, ref x)| {
-                                let show_function = if self_type {
-                                    show_fn.name.clone()
-                                } else {
-                                    symbols.symbol("show")
-                                };
-                                let show = infix(
-                                    span,
-                                    literal(span, "("),
-                                    symbols.symbol("++"),
-                                    infix(
-                                        span,
-                                        app(span, show_function, vec![ident(span, x.name.clone())]),
-                                        symbols.symbol("++"),
-                                        literal(span, ")"),
-                                    ),
-                                );
-                                infix(
-                                    span,
-                                    acc,
-                                    symbols.symbol("++"),
-                                    infix(span, literal(span, " "), symbols.symbol("++"), show),
-                                )
-                            })
-                    };
-
-                    let ctor_pattern = |pattern_args: Vec<_>| {
-                        pos::spanned(
-                            span,
-                            Pattern::Constructor(
-                                TypedIdent::new(variant.name.clone()),
-                                pattern_args
-                                    .into_iter()
-                                    .map(|arg| pos::spanned(span, Pattern::Ident(arg)))
-                                    .collect(),
-                            ),
-                        )
-                    };
-                    Alternative {
-                        pattern: ctor_pattern(pattern_args.into_iter().map(|t| t.1).collect()),
-                        expr,
-                    }
-                }).collect();
-            Expr::Match(Box::new(ident(span, x.clone())), alts)
-        }
-        Type::Record(ref row) => {
-            let field_symbols: Vec<_> = row_iter(row)
-                .map(|field| {
-                    TypedIdent::new(Symbol::from(format!("{}", field.name.declared_name())))
-                }).collect();
-
-            let expr = {
-                let open_brace = literal(span, "{ ");
-
-                let show_expr = field_symbols
-                    .iter()
-                    .enumerate()
-                    .fold(open_brace, |acc, (i, x)| {
-                        let show = app(
-                            span,
-                            symbols.symbol("show"),
-                            vec![ident(span, x.name.clone())],
-                        );
-
-                        let show_field = infix(
-                            span,
-                            acc,
-                            symbols.symbol("++"),
-                            infix(
-                                span,
-                                literal(span, &format!("{} = ", x.name.declared_name())),
-                                symbols.symbol("++"),
-                                show,
-                            ),
-                        );
-
-                        let last = i == field_symbols.len() - 1;
-                        let suffix = if last { " " } else { ", " };
-                        infix(
-                            span,
-                            show_field,
-                            symbols.symbol("++"),
-                            literal(span, suffix),
-                        )
-                    });
-
-                infix(span, show_expr, symbols.symbol("++"), literal(span, "}"))
-            };
-            Expr::Match(
-                Box::new(ident(span, x.clone())),
-                vec![Alternative {
-                    pattern: generate_record_pattern(span, row, field_symbols),
-                    expr,
-                }],
-            )
-        }
-        _ => return Err("Unable to derive Show for this type".into()),
-    };
-
-    let self_type: AstType<_> = Type::app(
-        Type::ident(bind.alias.value.name.clone()),
-        bind.alias
-            .value
-            .params()
-            .iter()
-            .cloned()
-            .map(Type::generic)
-            .collect(),
-    );
-
-    let show_record_expr = Expr::rec_let_bindings(
-        vec![ValueBinding {
-            name: pos::spanned(span, Pattern::Ident(show_fn.clone())),
-            args: vec![Argument::explicit(pos::spanned(
-                span,
-                TypedIdent::new(x.clone()),
-            ))],
-            expr: pos::spanned(span, show_expr),
-            metadata: Default::default(),
-            typ: Some(Type::function(vec![self_type.clone()], Type::string())),
-            resolved_type: Type::hole(),
-        }],
-        pos::spanned(
-            span,
-            Expr::Record {
-                typ: Type::hole(),
-                types: Vec::new(),
-                exprs: vec![ExprField {
-                    metadata: Default::default(),
-                    name: pos::spanned(span, symbols.symbol("show")),
-                    value: Some(ident(span, show_fn.name.clone())),
-                }],
-                base: None,
-            },
-        ),
-    );
-
-    Ok(ValueBinding {
-        name: pos::spanned(
-            span,
-            Pattern::Ident(TypedIdent::new(
-                symbols.symbol(format!("show_{}", bind.alias.value.name.declared_name())),
-            )),
-        ),
-        args: Vec::new(),
-        expr: pos::spanned(span, show_record_expr),
-        metadata: Default::default(),
-        typ: Some(binding_type(symbols, "Show", self_type, bind)),
-        resolved_type: Type::hole(),
-    })
-}
-
-fn generate_eq(
-    symbols: &mut Symbols,
-    bind: &TypeBinding<Symbol>,
-) -> Result<ValueBinding<Symbol>, Error> {
-    let span = bind.name.span;
-
-    let eq = TypedIdent::new(symbols.symbol("eq"));
-    let l = Symbol::from("l");
-    let r = Symbol::from("r");
-
-    let matcher = Box::new(pos::spanned(
-        span,
-        Expr::Tuple {
-            typ: Type::hole(),
-            elems: vec![ident(span, l.clone()), ident(span, r.clone())],
-        },
-    ));
-
-    let generate_and_chain =
-        |symbols: &mut Symbols,
-         iter: &mut Iterator<Item = (&(bool, TypedIdent<Symbol>), &TypedIdent<Symbol>)>| {
-            iter.fold(None, |acc, (&(self_type, ref l), r)| {
-                let equal_symbol = if self_type {
-                    eq.name.clone()
-                } else {
-                    symbols.symbol("==")
-                };
-
-                let eq_check = app(
-                    span,
-                    equal_symbol,
-                    vec![
-                        ident(span, l.name.clone()).into(),
-                        ident(span, r.name.clone()),
-                    ],
-                );
-
-                Some(match acc {
-                    Some(acc) => infix(span, acc, symbols.symbol("&&"), eq_check),
-                    None => eq_check,
-                })
-            }).unwrap_or_else(|| ident(span, symbols.symbol("True")))
-        };
-
-    let comparison_expr = match **remove_forall(bind.alias.value.unresolved_type()) {
-        Type::Variant(ref variants) => {
-            let catch_all_alternative = Alternative {
-                pattern: pos::spanned(span, Pattern::Ident(TypedIdent::new(symbols.symbol("_")))),
-                expr: ident(span, symbols.symbol("False")),
-            };
-
-            let alts = row_iter(variants)
-                .map(|variant| {
-                    let l_pattern_args: Vec<_> = arg_iter(&variant.typ)
-                        .map(|typ| {
-                            (
-                                is_self_type(&bind.alias.value.name, typ),
-                                TypedIdent::new(Symbol::from("arg_l")),
-                            )
-                        }).collect();
-                    let r_pattern_args: Vec<_> = arg_iter(&variant.typ)
-                        .map(|_| TypedIdent::new(Symbol::from("arg_r")))
-                        .collect();
-
-                    let expr = generate_and_chain(
-                        symbols,
-                        &mut l_pattern_args.iter().zip(&r_pattern_args),
-                    );
-
-                    let ctor_pattern = |pattern_args: Vec<_>| {
-                        pos::spanned(
-                            span,
-                            Pattern::Constructor(
-                                TypedIdent::new(variant.name.clone()),
-                                pattern_args
-                                    .into_iter()
-                                    .map(|arg| pos::spanned(span, Pattern::Ident(arg)))
-                                    .collect(),
-                            ),
-                        )
-                    };
-                    Alternative {
-                        pattern: pos::spanned(
-                            span,
-                            Pattern::Tuple {
-                                typ: Type::hole(),
-                                elems: vec![
-                                    ctor_pattern(l_pattern_args.into_iter().map(|t| t.1).collect()),
-                                    ctor_pattern(r_pattern_args),
-                                ],
-                            },
-                        ),
-                        expr,
-                    }
-                }).chain(Some(catch_all_alternative))
-                .collect();
-            Expr::Match(matcher, alts)
-        }
-        Type::Record(ref row) => {
-            let l_symbols: Vec<_> = row_iter(row)
-                .map(|field| {
-                    (
-                        is_self_type(&bind.alias.value.name, &field.typ),
-                        TypedIdent::new(Symbol::from(format!("{}_l", field.name.declared_name()))),
-                    )
-                }).collect();
-            let r_symbols: Vec<_> = row_iter(row)
-                .map(|field| {
-                    TypedIdent::new(Symbol::from(format!("{}_r", field.name.declared_name())))
-                }).collect();
-
-            let expr = generate_and_chain(symbols, &mut l_symbols.iter().zip(&r_symbols));
-            let generate_record_pattern = |symbols| {
-                pos::spanned(
-                    span,
-                    Pattern::Record {
-                        implicit_import: None,
-                        typ: Type::hole(),
-                        types: Vec::new(),
-                        fields: row_iter(row)
-                            .zip(symbols)
-                            .map(|(field, bind)| PatternField {
-                                name: pos::spanned(span, field.name.clone()),
-                                value: Some(pos::spanned(span, Pattern::Ident(bind))),
-                            }).collect(),
-                    },
-                )
-            };
-            Expr::Match(
-                matcher,
-                vec![Alternative {
-                    pattern: pos::spanned(
-                        span,
-                        Pattern::Tuple {
-                            elems: vec![
-                                generate_record_pattern(
-                                    l_symbols.into_iter().map(|t| t.1).collect(),
-                                ),
-                                generate_record_pattern(r_symbols),
-                            ],
-                            typ: Type::hole(),
-                        },
-                    ),
-                    expr,
-                }],
-            )
-        }
-        _ => return Err("Unable to derive eq for this type".into()),
-    };
-
-    let self_type: AstType<_> = Type::app(
-        Type::ident(bind.alias.value.name.clone()),
-        bind.alias
-            .value
-            .params()
-            .iter()
-            .cloned()
-            .map(Type::generic)
-            .collect(),
-    );
-
-    let eq_record_expr = Expr::rec_let_bindings(
-        vec![ValueBinding {
-            name: pos::spanned(span, Pattern::Ident(eq.clone())),
-            args: [l, r]
-                .iter()
-                .map(|arg| Argument::explicit(pos::spanned(span, TypedIdent::new(arg.clone()))))
-                .collect(),
-            expr: pos::spanned(span, comparison_expr),
-            metadata: Default::default(),
-            typ: Some(Type::function(
-                vec![self_type.clone(), self_type.clone()],
-                Type::hole(),
-            )),
-            resolved_type: Type::hole(),
-        }],
-        pos::spanned(
-            span,
-            Expr::Record {
-                typ: Type::hole(),
-                types: Vec::new(),
-                exprs: vec![ExprField {
-                    metadata: Default::default(),
-                    name: pos::spanned(span, symbols.symbol("==")),
-                    value: Some(ident(span, eq.name.clone())),
-                }],
-                base: None,
-            },
-        ),
-    );
-
-    Ok(ValueBinding {
-        name: pos::spanned(
-            span,
-            Pattern::Ident(TypedIdent::new(
-                symbols.symbol(format!("eq_{}", bind.alias.value.name.declared_name())),
-            )),
-        ),
-        args: Vec::new(),
-        expr: pos::spanned(span, eq_record_expr),
-        metadata: Default::default(),
-        typ: Some(binding_type(symbols, "Eq", self_type, bind)),
-        resolved_type: Type::hole(),
-    })
-}
-
-fn generate_deserialize(
-    symbols: &mut Symbols,
-    bind: &TypeBinding<Symbol>,
-) -> Result<ValueBinding<Symbol>, Error> {
-    let span = bind.name.span;
-
-    let deserializer_fn = TypedIdent::new(symbols.symbol("deserializer"));
-
-    let field_deserialize = symbols.symbol("field");
-    let deserializer_ident = ident(span, symbols.symbol("deserializer"));
-
-    let self_type: AstType<_> = Type::app(
-        Type::ident(bind.alias.value.name.clone()),
-        bind.alias
-            .value
-            .params()
-            .iter()
-            .cloned()
-            .map(Type::generic)
-            .collect(),
-    );
-
-    let deserializer_expr = match **remove_forall(bind.alias.value.unresolved_type()) {
-        Type::Record(ref row) => {
-            let field_symbols: Vec<_> = row_iter(row)
-                .map(|field| {
-                    TypedIdent::new(Symbol::from(format!("{}", field.name.declared_name())))
-                }).collect();
-
-            sequence_actions(
-                symbols,
-                span,
-                &field_symbols,
-                pos::spanned(
-                    span,
-                    Expr::Record {
-                        exprs: field_symbols
-                            .iter()
-                            .map(|id| ExprField {
-                                name: pos::spanned(span, id.name.clone()),
-                                value: None,
-                                metadata: Default::default(),
-                            }).collect(),
-                        types: Vec::new(),
-                        typ: Type::hole(),
-                        base: None,
-                    },
-                ),
-                &mut |field| {
-                    app(
-                        span,
-                        field_deserialize.clone(),
-                        vec![
-                            literal(span, field.declared_name()),
-                            deserializer_ident.clone(),
-                        ],
-                    )
-                },
-            )
-        }
-        Type::Variant(ref row) => row_iter(row)
-            .fold(None, |acc, variant| {
-                let deserialize_variant = app(
-                    span,
-                    symbols.symbol("map"),
-                    vec![
-                        ident(span, variant.name.clone()),
-                        deserializer_ident.clone(),
-                    ],
-                );
-                Some(match acc {
-                    Some(prev) => infix(span, prev, symbols.symbol("<|>"), deserialize_variant),
-                    None => deserialize_variant,
-                })
-            }).unwrap(),
-        _ => return Err("Unable to derive Deserialize for this type".into()),
-    };
-
-    let serialization_import = generate_import_(
-        span,
-        symbols,
-        &["ValueDeserializer"],
-        &["deserializer", "field"],
-        true,
-        "std.json.de",
-    );
-    let functor_import = generate_import(span, symbols, &[], &["map"], "std.functor");
-    let applicative_import = generate_import(span, symbols, &[], &["<*>"], "std.applicative");
-    let alternative_import = generate_import(span, symbols, &[], &["<|>"], "std.alternative");
-
-    let deserializer_binding = ValueBinding {
-        name: pos::spanned(span, Pattern::Ident(deserializer_fn.clone())),
-        args: Vec::new(),
-        expr: deserializer_expr,
-        metadata: Default::default(),
-        typ: Some(Type::app(
-            Type::ident(symbols.symbol("ValueDeserializer")),
-            collect![self_type.clone()],
-        )),
-        resolved_type: Type::hole(),
-    };
-
-    let export_record_expr = pos::spanned(
-        span,
-        Expr::Record {
-            typ: Type::hole(),
-            types: Vec::new(),
-            exprs: vec![ExprField {
-                metadata: Default::default(),
-                name: pos::spanned(span, symbols.symbol("deserializer")),
-                value: Some(ident(span, deserializer_fn.name.clone())),
-            }],
-            base: None,
-        },
-    );
-
-    let deserializer_record_expr = vec![
-        serialization_import,
-        functor_import,
-        applicative_import,
-        alternative_import,
-        deserializer_binding,
-    ].into_iter()
-    .rev()
-    .fold(export_record_expr, |expr, bind| {
-        pos::spanned(span, Expr::let_binding(bind, expr))
-    });
-
-    Ok(ValueBinding {
-        name: pos::spanned(
-            span,
-            Pattern::Ident(TypedIdent::new(symbols.symbol(format!(
-                "deserialize_{}",
-                bind.alias.value.name.declared_name()
-            )))),
-        ),
-        args: Vec::new(),
-        expr: deserializer_record_expr,
-        metadata: Default::default(),
-        typ: Some(binding_type(symbols, "Deserialize", self_type, bind)),
-        resolved_type: Type::hole(),
-    })
-}
-
-fn generate_serialize(
-    symbols: &mut Symbols,
-    bind: &TypeBinding<Symbol>,
-) -> Result<ValueBinding<Symbol>, Error> {
-    let span = bind.name.span;
-
-    let x = Symbol::from("x");
-
-    let serialize_fn = TypedIdent::new(symbols.symbol("serialize"));
-
-    let self_type: AstType<_> = Type::app(
-        Type::ident(bind.alias.value.name.clone()),
-        bind.alias
-            .value
-            .params()
-            .iter()
-            .cloned()
-            .map(Type::generic)
-            .collect(),
-    );
-
-    let serializer_expr = match **remove_forall(bind.alias.value.unresolved_type()) {
-        Type::Record(ref row) => {
-            let field_symbols: Vec<_> = row_iter(row)
-                .map(|field| {
-                    TypedIdent::new(Symbol::from(format!("{}", field.name.declared_name())))
-                }).collect();
-
-            let construct_map_expr = field_symbols
-                .iter()
-                .fold(None, |prev, symbol| {
-                    let serialize_field = ident(span, symbol.name.clone());
-
-                    let map = app(
-                        span,
-                        symbols.symbol("singleton"),
-                        vec![literal(span, symbol.name.declared_name()), serialize_field],
-                    );
-
-                    Some(match prev {
-                        Some(prev) => infix(span, prev, symbols.symbol("<>"), map),
-                        None => map,
-                    })
-                }).unwrap_or_else(|| ident(span, symbols.symbol("empty")));
-
-            let construct_object_expr = app(
-                span,
-                symbols.symbol("Object"),
-                vec![paren(span, construct_map_expr)],
-            );
-
-            let expr = sequence_actions(
-                symbols,
-                span,
-                &field_symbols,
-                construct_object_expr,
-                &mut |symbol| {
-                    app(
-                        span,
-                        serialize_fn.name.clone(),
-                        vec![ident(span, symbol.clone())],
-                    )
-                },
-            );
-
-            pos::spanned(
-                span,
-                Expr::Match(
-                    Box::new(ident(span, x.clone())),
-                    vec![Alternative {
-                        pattern: generate_record_pattern(span, row, field_symbols),
-                        expr,
-                    }],
-                ),
-            )
-        }
-        Type::Variant(ref row) => {
-            let alts = row_iter(row)
-                .map(|variant| {
-                    let pattern_args: Vec<_> = arg_iter(&variant.typ)
-                        .enumerate()
-                        .map(|(i, _typ)| TypedIdent::new(Symbol::from(format!("arg_{}", i))))
-                        .collect();
-
-                    let expr = {
-                        if pattern_args.len() > 1 {
-                            return Err(format!(
-                                "Variants with more than 1 argument is not yet supported"
-                            ).into());
-                        }
-
-                        if pattern_args.is_empty() {
-                            app(
-                                span,
-                                serialize_fn.name.clone(),
-                                vec![pos::spanned(
-                                    span,
-                                    Expr::Tuple {
-                                        elems: vec![],
-                                        typ: Type::hole(),
-                                    },
-                                )],
-                            )
-                        } else {
-                            let arg = &pattern_args[0];
-                            app(
-                                span,
-                                serialize_fn.name.clone(),
-                                vec![ident(span, arg.name.clone())],
-                            )
-                        }
-                    };
-
-                    let ctor_pattern = |pattern_args: Vec<_>| {
-                        pos::spanned(
-                            span,
-                            Pattern::Constructor(
-                                TypedIdent::new(variant.name.clone()),
-                                pattern_args
-                                    .into_iter()
-                                    .map(|arg| pos::spanned(span, Pattern::Ident(arg)))
-                                    .collect(),
-                            ),
-                        )
-                    };
-                    Ok(Alternative {
-                        pattern: ctor_pattern(pattern_args),
-                        expr,
-                    })
-                }).collect::<Result<_, Error>>()?;
-
-            pos::spanned(span, Expr::Match(Box::new(ident(span, x.clone())), alts))
-        }
-
-        _ => return Err("Unable to derive Deserialize for this type".into()),
-    };
-
-    let serialization_import = generate_import_(
-        span,
-        symbols,
-        &["ValueSerializer", "Value"],
-        &["serialize"],
-        true,
-        "std.json.ser",
-    );
-    let functor_import = generate_import(span, symbols, &[], &["map"], "std.functor");
-    let applicative_import = generate_import(span, symbols, &[], &["<*>"], "std.applicative");
-    let map_import = generate_import_(span, symbols, &[], &["singleton", "empty"], true, "std.map");
-    let semigroup_import = generate_import(span, symbols, &[], &["<>"], "std.semigroup");
-    let result_import = generate_import_(span, symbols, &[], &[], true, "std.result");
-
-    let serialize_ = TypedIdent::new(symbols.symbol("serialize_"));
-    let serializer_binding = ValueBinding {
-        name: pos::spanned(span, Pattern::Ident(serialize_.clone())),
-        args: vec![Argument::explicit(pos::spanned(
-            span,
-            TypedIdent::new(x.clone()),
-        ))],
-        expr: serializer_expr,
-        metadata: Default::default(),
-        typ: Some(Type::function(collect![self_type.clone()], Type::hole())),
-        resolved_type: Type::hole(),
-    };
-
-    let export_expr = pos::spanned(
-        span,
-        Expr::Record {
-            typ: Type::hole(),
-            types: Vec::new(),
-            exprs: vec![ExprField {
-                metadata: Default::default(),
-                name: pos::spanned(span, symbols.symbol("serialize")),
-                value: Some(ident(span, serialize_.name.clone())),
-            }],
-            base: None,
-        },
-    );
-
-    let serializer_record_expr = vec![
-        result_import,
-        serialization_import,
-        functor_import,
-        applicative_import,
-        map_import,
-        semigroup_import,
-        serializer_binding,
-    ].into_iter()
-    .rev()
-    .fold(export_expr, |expr, bind| {
-        pos::spanned(span, Expr::let_binding(bind, expr))
-    });
-
-    Ok(ValueBinding {
-        name: pos::spanned(
-            span,
-            Pattern::Ident(TypedIdent::new(symbols.symbol(format!(
-                "serialize_{}",
-                bind.alias.value.name.declared_name()
-            )))),
-        ),
-        args: Vec::new(),
-        expr: serializer_record_expr,
-        metadata: Default::default(),
-        typ: Some(binding_type(symbols, "Serialize", self_type, bind)),
-        resolved_type: Type::hole(),
-    })
-}
diff --git a/vm/src/derive/deserialize.rs b/vm/src/derive/deserialize.rs
new file mode 100644
index 00000000..1c8aea55
--- /dev/null
+++ b/vm/src/derive/deserialize.rs
@@ -0,0 +1,152 @@
+use base::ast::{AstType, Expr, ExprField, Pattern, TypeBinding, TypedIdent, ValueBinding};
+use base::pos;
+use base::symbol::{Symbol, Symbols};
+use base::types::{remove_forall, row_iter, Type};
+
+use macros::Error;
+
+use derive::*;
+
+pub fn generate(
+    symbols: &mut Symbols,
+    bind: &TypeBinding<Symbol>,
+) -> Result<ValueBinding<Symbol>, Error> {
+    let span = bind.name.span;
+
+    let deserializer_fn = TypedIdent::new(symbols.symbol("deserializer"));
+
+    let field_deserialize = symbols.symbol("field");
+    let deserializer_ident = ident(span, symbols.symbol("deserializer"));
+
+    let self_type: AstType<_> = Type::app(
+        Type::ident(bind.alias.value.name.clone()),
+        bind.alias
+            .value
+            .params()
+            .iter()
+            .cloned()
+            .map(Type::generic)
+            .collect(),
+    );
+
+    let deserializer_expr = match **remove_forall(bind.alias.value.unresolved_type()) {
+        Type::Record(ref row) => {
+            let field_symbols: Vec<_> = row_iter(row)
+                .map(|field| {
+                    TypedIdent::new(Symbol::from(format!("{}", field.name.declared_name())))
+                }).collect();
+
+            sequence_actions(
+                symbols,
+                span,
+                &field_symbols,
+                pos::spanned(
+                    span,
+                    Expr::Record {
+                        exprs: field_symbols
+                            .iter()
+                            .map(|id| ExprField {
+                                name: pos::spanned(span, id.name.clone()),
+                                value: None,
+                                metadata: Default::default(),
+                            }).collect(),
+                        types: Vec::new(),
+                        typ: Type::hole(),
+                        base: None,
+                    },
+                ),
+                &mut |field| {
+                    app(
+                        span,
+                        field_deserialize.clone(),
+                        vec![
+                            literal(span, field.declared_name()),
+                            deserializer_ident.clone(),
+                        ],
+                    )
+                },
+            )
+        }
+        Type::Variant(ref row) => row_iter(row)
+            .fold(None, |acc, variant| {
+                let deserialize_variant = app(
+                    span,
+                    symbols.symbol("map"),
+                    vec![
+                        ident(span, variant.name.clone()),
+                        deserializer_ident.clone(),
+                    ],
+                );
+                Some(match acc {
+                    Some(prev) => infix(span, prev, symbols.symbol("<|>"), deserialize_variant),
+                    None => deserialize_variant,
+                })
+            }).unwrap(),
+        _ => return Err("Unable to derive Deserialize for this type".into()),
+    };
+
+    let serialization_import = generate_import_(
+        span,
+        symbols,
+        &["ValueDeserializer"],
+        &["deserializer", "field"],
+        true,
+        "std.json.de",
+    );
+    let functor_import = generate_import(span, symbols, &[], &["map"], "std.functor");
+    let applicative_import = generate_import(span, symbols, &[], &["<*>"], "std.applicative");
+    let alternative_import = generate_import(span, symbols, &[], &["<|>"], "std.alternative");
+
+    let deserializer_binding = ValueBinding {
+        name: pos::spanned(span, Pattern::Ident(deserializer_fn.clone())),
+        args: Vec::new(),
+        expr: deserializer_expr,
+        metadata: Default::default(),
+        typ: Some(Type::app(
+            Type::ident(symbols.symbol("ValueDeserializer")),
+            collect![self_type.clone()],
+        )),
+        resolved_type: Type::hole(),
+    };
+
+    let export_record_expr = pos::spanned(
+        span,
+        Expr::Record {
+            typ: Type::hole(),
+            types: Vec::new(),
+            exprs: vec![ExprField {
+                metadata: Default::default(),
+                name: pos::spanned(span, symbols.symbol("deserializer")),
+                value: Some(ident(span, deserializer_fn.name.clone())),
+            }],
+            base: None,
+        },
+    );
+
+    let deserializer_record_expr = vec![
+        serialization_import,
+        functor_import,
+        applicative_import,
+        alternative_import,
+        deserializer_binding,
+    ].into_iter()
+    .rev()
+    .fold(export_record_expr, |expr, bind| {
+        pos::spanned(span, Expr::let_binding(bind, expr))
+    });
+
+    Ok(ValueBinding {
+        name: pos::spanned(
+            span,
+            Pattern::Ident(TypedIdent::new(symbols.symbol(format!(
+                "deserialize_{}",
+                bind.alias.value.name.declared_name()
+            )))),
+        ),
+        args: Vec::new(),
+        expr: deserializer_record_expr,
+        metadata: Default::default(),
+        typ: Some(binding_type(symbols, "Deserialize", self_type, bind)),
+        resolved_type: Type::hole(),
+    })
+}
diff --git a/vm/src/derive/eq.rs b/vm/src/derive/eq.rs
new file mode 100644
index 00000000..27c5d939
--- /dev/null
+++ b/vm/src/derive/eq.rs
@@ -0,0 +1,216 @@
+use base::ast::{
+    Alternative, Argument, AstType, Expr, ExprField, Pattern, TypeBinding, TypedIdent, ValueBinding,
+};
+use base::pos;
+use base::symbol::{Symbol, Symbols};
+use base::types::{arg_iter, remove_forall, row_iter, Type};
+
+use macros::Error;
+
+use derive::*;
+
+pub fn generate(
+    symbols: &mut Symbols,
+    bind: &TypeBinding<Symbol>,
+) -> Result<ValueBinding<Symbol>, Error> {
+    let span = bind.name.span;
+
+    let eq = TypedIdent::new(symbols.symbol("eq"));
+    let l = Symbol::from("l");
+    let r = Symbol::from("r");
+
+    let matcher = Box::new(pos::spanned(
+        span,
+        Expr::Tuple {
+            typ: Type::hole(),
+            elems: vec![ident(span, l.clone()), ident(span, r.clone())],
+        },
+    ));
+
+    let generate_and_chain =
+        |symbols: &mut Symbols,
+         iter: &mut Iterator<Item = (&(bool, TypedIdent<Symbol>), &TypedIdent<Symbol>)>| {
+            iter.fold(None, |acc, (&(self_type, ref l), r)| {
+                let equal_symbol = if self_type {
+                    eq.name.clone()
+                } else {
+                    symbols.symbol("==")
+                };
+
+                let eq_check = app(
+                    span,
+                    equal_symbol,
+                    vec![
+                        ident(span, l.name.clone()).into(),
+                        ident(span, r.name.clone()),
+                    ],
+                );
+
+                Some(match acc {
+                    Some(acc) => infix(span, acc, symbols.symbol("&&"), eq_check),
+                    None => eq_check,
+                })
+            }).unwrap_or_else(|| ident(span, symbols.symbol("True")))
+        };
+
+    let comparison_expr = match **remove_forall(bind.alias.value.unresolved_type()) {
+        Type::Variant(ref variants) => {
+            let catch_all_alternative = Alternative {
+                pattern: pos::spanned(span, Pattern::Ident(TypedIdent::new(symbols.symbol("_")))),
+                expr: ident(span, symbols.symbol("False")),
+            };
+
+            let alts = row_iter(variants)
+                .map(|variant| {
+                    let l_pattern_args: Vec<_> = arg_iter(&variant.typ)
+                        .map(|typ| {
+                            (
+                                is_self_type(&bind.alias.value.name, typ),
+                                TypedIdent::new(Symbol::from("arg_l")),
+                            )
+                        }).collect();
+                    let r_pattern_args: Vec<_> = arg_iter(&variant.typ)
+                        .map(|_| TypedIdent::new(Symbol::from("arg_r")))
+                        .collect();
+
+                    let expr = generate_and_chain(
+                        symbols,
+                        &mut l_pattern_args.iter().zip(&r_pattern_args),
+                    );
+
+                    let ctor_pattern = |pattern_args: Vec<_>| {
+                        pos::spanned(
+                            span,
+                            Pattern::Constructor(
+                                TypedIdent::new(variant.name.clone()),
+                                pattern_args
+                                    .into_iter()
+                                    .map(|arg| pos::spanned(span, Pattern::Ident(arg)))
+                                    .collect(),
+                            ),
+                        )
+                    };
+                    Alternative {
+                        pattern: pos::spanned(
+                            span,
+                            Pattern::Tuple {
+                                typ: Type::hole(),
+                                elems: vec![
+                                    ctor_pattern(l_pattern_args.into_iter().map(|t| t.1).collect()),
+                                    ctor_pattern(r_pattern_args),
+                                ],
+                            },
+                        ),
+                        expr,
+                    }
+                }).chain(Some(catch_all_alternative))
+                .collect();
+            Expr::Match(matcher, alts)
+        }
+        Type::Record(ref row) => {
+            let l_symbols: Vec<_> = row_iter(row)
+                .map(|field| {
+                    (
+                        is_self_type(&bind.alias.value.name, &field.typ),
+                        TypedIdent::new(Symbol::from(format!("{}_l", field.name.declared_name()))),
+                    )
+                }).collect();
+            let r_symbols: Vec<_> = row_iter(row)
+                .map(|field| {
+                    TypedIdent::new(Symbol::from(format!("{}_r", field.name.declared_name())))
+                }).collect();
+
+            let expr = generate_and_chain(symbols, &mut l_symbols.iter().zip(&r_symbols));
+            let generate_record_pattern = |symbols| {
+                pos::spanned(
+                    span,
+                    Pattern::Record {
+                        implicit_import: None,
+                        typ: Type::hole(),
+                        types: Vec::new(),
+                        fields: row_iter(row)
+                            .zip(symbols)
+                            .map(|(field, bind)| PatternField {
+                                name: pos::spanned(span, field.name.clone()),
+                                value: Some(pos::spanned(span, Pattern::Ident(bind))),
+                            }).collect(),
+                    },
+                )
+            };
+            Expr::Match(
+                matcher,
+                vec![Alternative {
+                    pattern: pos::spanned(
+                        span,
+                        Pattern::Tuple {
+                            elems: vec![
+                                generate_record_pattern(
+                                    l_symbols.into_iter().map(|t| t.1).collect(),
+                                ),
+                                generate_record_pattern(r_symbols),
+                            ],
+                            typ: Type::hole(),
+                        },
+                    ),
+                    expr,
+                }],
+            )
+        }
+        _ => return Err("Unable to derive eq for this type".into()),
+    };
+
+    let self_type: AstType<_> = Type::app(
+        Type::ident(bind.alias.value.name.clone()),
+        bind.alias
+            .value
+            .params()
+            .iter()
+            .cloned()
+            .map(Type::generic)
+            .collect(),
+    );
+
+    let eq_record_expr = Expr::rec_let_bindings(
+        vec![ValueBinding {
+            name: pos::spanned(span, Pattern::Ident(eq.clone())),
+            args: [l, r]
+                .iter()
+                .map(|arg| Argument::explicit(pos::spanned(span, TypedIdent::new(arg.clone()))))
+                .collect(),
+            expr: pos::spanned(span, comparison_expr),
+            metadata: Default::default(),
+            typ: Some(Type::function(
+                vec![self_type.clone(), self_type.clone()],
+                Type::hole(),
+            )),
+            resolved_type: Type::hole(),
+        }],
+        pos::spanned(
+            span,
+            Expr::Record {
+                typ: Type::hole(),
+                types: Vec::new(),
+                exprs: vec![ExprField {
+                    metadata: Default::default(),
+                    name: pos::spanned(span, symbols.symbol("==")),
+                    value: Some(ident(span, eq.name.clone())),
+                }],
+                base: None,
+            },
+        ),
+    );
+
+    Ok(ValueBinding {
+        name: pos::spanned(
+            span,
+            Pattern::Ident(TypedIdent::new(
+                symbols.symbol(format!("eq_{}", bind.alias.value.name.declared_name())),
+            )),
+        ),
+        args: Vec::new(),
+        expr: pos::spanned(span, eq_record_expr),
+        metadata: Default::default(),
+        typ: Some(binding_type(symbols, "Eq", self_type, bind)),
+        resolved_type: Type::hole(),
+    })
+}
diff --git a/vm/src/derive/mod.rs b/vm/src/derive/mod.rs
new file mode 100644
index 00000000..b0832b7c
--- /dev/null
+++ b/vm/src/derive/mod.rs
@@ -0,0 +1,234 @@
+use base::ast::{
+    Argument, AstType, Expr, Lambda, Literal, Pattern, PatternField, SpannedExpr, SpannedPattern,
+    TypeBinding, TypedIdent, ValueBinding,
+};
+use base::metadata::Attribute;
+use base::pos::{self, BytePos, Span};
+use base::symbol::{Symbol, Symbols};
+use base::types::{row_iter, Type};
+
+use macros::Error;
+
+mod deserialize;
+mod eq;
+mod serialize;
+mod show;
+
+pub fn generate(
+    symbols: &mut Symbols,
+    derive: &Attribute,
+    bind: &TypeBinding<Symbol>,
+) -> Result<Vec<ValueBinding<Symbol>>, Error> {
+    match derive.arguments {
+        Some(ref args) => args
+            .split(',')
+            .map(|s| s.trim())
+            .map(|arg| {
+                Ok(match arg {
+                    "Eq" => eq::generate(symbols, bind),
+                    "Show" => show::generate(symbols, bind),
+                    "Deserialize" => deserialize::generate(symbols, bind),
+                    "Serialize" => serialize::generate(symbols, bind),
+                    _ => return Err(format!("`{}` is not a type that can be derived", arg)),
+                })
+            }).collect::<Result<_, _>>()?,
+        _ => Err("Invalid `derive` attribute".into()),
+    }
+}
+
+fn sequence_actions(
+    symbols: &mut Symbols,
+    span: Span<BytePos>,
+    field_symbols: &[TypedIdent<Symbol>],
+    packed_expr: SpannedExpr<Symbol>,
+    action: &mut FnMut(&Symbol) -> SpannedExpr<Symbol>,
+) -> SpannedExpr<Symbol> {
+    let pack_record = pos::spanned(
+        span,
+        Expr::Lambda(Lambda {
+            args: field_symbols
+                .iter()
+                .cloned()
+                .map(|id| Argument::explicit(pos::spanned(span, id)))
+                .collect(),
+            body: Box::new(packed_expr),
+            id: TypedIdent::new(symbols.symbol("pack_record")),
+        }),
+    );
+    let map_expr = app(
+        span,
+        symbols.symbol("map"),
+        vec![
+            paren(span, pack_record),
+            paren(span, action(&field_symbols.first().expect("FIXME").name)),
+        ],
+    );
+
+    field_symbols.iter().skip(1).fold(map_expr, |prev, symbol| {
+        let deserialize_field = action(&symbol.name);
+        infix(span, prev, symbols.symbol("<*>"), deserialize_field)
+    })
+}
+
+fn generate_record_pattern<I>(
+    span: Span<BytePos>,
+    row: &AstType<Symbol>,
+    symbols: I,
+) -> SpannedPattern<Symbol>
+where
+    I: IntoIterator<Item = TypedIdent<Symbol>>,
+{
+    pos::spanned(
+        span,
+        Pattern::Record {
+            implicit_import: None,
+            typ: Type::hole(),
+            types: Vec::new(),
+            fields: row_iter(row)
+                .zip(symbols)
+                .map(|(field, bind)| PatternField {
+                    name: pos::spanned(span, field.name.clone()),
+                    value: Some(pos::spanned(span, Pattern::Ident(bind))),
+                }).collect(),
+        },
+    )
+}
+
+fn generate_import(
+    span: Span<BytePos>,
+    symbols: &mut Symbols,
+    type_fields: &[&str],
+    fields: &[&str],
+    import: &str,
+) -> ValueBinding<Symbol> {
+    generate_import_(span, symbols, type_fields, fields, false, import)
+}
+
+fn generate_import_(
+    span: Span<BytePos>,
+    symbols: &mut Symbols,
+    type_fields: &[&str],
+    fields: &[&str],
+    implicit_import: bool,
+    import: &str,
+) -> ValueBinding<Symbol> {
+    ValueBinding {
+        name: pos::spanned(
+            span,
+            Pattern::Record {
+                implicit_import: if implicit_import {
+                    Some(pos::spanned(span, Symbol::from("implicit_import")))
+                } else {
+                    None
+                },
+                typ: Type::hole(),
+                types: type_fields
+                    .iter()
+                    .map(|f| PatternField {
+                        name: pos::spanned(span, symbols.symbol(*f)),
+                        value: None,
+                    }).collect(),
+                fields: fields
+                    .iter()
+                    .map(|f| PatternField {
+                        name: pos::spanned(span, symbols.symbol(*f)),
+                        value: None,
+                    }).collect(),
+            },
+        ),
+        args: Vec::new(),
+        expr: app(
+            span,
+            symbols.symbol("import!"),
+            vec![project(span, symbols, import)],
+        ),
+        metadata: Default::default(),
+        typ: None,
+        resolved_type: Type::hole(),
+    }
+}
+
+fn project(span: Span<BytePos>, symbols: &mut Symbols, p: &str) -> SpannedExpr<Symbol> {
+    p.split('.')
+        .fold(None, |acc, name| {
+            let symbol = symbols.symbol(name);
+            Some(match acc {
+                Some(expr) => {
+                    pos::spanned(span, Expr::Projection(Box::new(expr), symbol, Type::hole()))
+                }
+                None => ident(span, symbol),
+            })
+        }).unwrap()
+}
+
+fn ident(span: Span<BytePos>, s: Symbol) -> SpannedExpr<Symbol> {
+    pos::spanned(span, Expr::Ident(TypedIdent::new(s)))
+}
+
+fn literal(span: Span<BytePos>, s: &str) -> SpannedExpr<Symbol> {
+    pos::spanned(span, Expr::Literal(Literal::String(s.to_string())))
+}
+fn paren(span: Span<BytePos>, expr: SpannedExpr<Symbol>) -> SpannedExpr<Symbol> {
+    pos::spanned(
+        span,
+        Expr::Tuple {
+            elems: vec![expr],
+            typ: Type::hole(),
+        },
+    )
+}
+
+fn app(span: Span<BytePos>, func: Symbol, args: Vec<SpannedExpr<Symbol>>) -> SpannedExpr<Symbol> {
+    pos::spanned(
+        span,
+        Expr::App {
+            func: Box::new(pos::spanned(span, Expr::Ident(TypedIdent::new(func)))),
+            implicit_args: Vec::new(),
+            args,
+        },
+    )
+}
+
+fn infix(
+    span: Span<BytePos>,
+    lhs: SpannedExpr<Symbol>,
+    op: Symbol,
+    rhs: SpannedExpr<Symbol>,
+) -> SpannedExpr<Symbol> {
+    pos::spanned(
+        span,
+        Expr::Infix {
+            lhs: lhs.into(),
+            op: pos::spanned(span, TypedIdent::new(op)),
+            rhs: rhs.into(),
+            implicit_args: Vec::new(),
+        },
+    )
+}
+
+fn is_self_type(self_: &Symbol, typ: &AstType<Symbol>) -> bool {
+    match **typ {
+        Type::App(ref f, _) => is_self_type(self_, f),
+        Type::Alias(ref alias) => alias.name == *self_,
+        Type::Ident(ref id) => id == self_,
+        _ => false,
+    }
+}
+
+fn binding_type(
+    symbols: &mut Symbols,
+    derive_type_name: &str,
+    self_type: AstType<Symbol>,
+    bind: &TypeBinding<Symbol>,
+) -> AstType<Symbol> {
+    let derive_type: AstType<_> = Type::ident(symbols.symbol(derive_type_name));
+    Type::function_implicit(
+        bind.alias
+            .value
+            .params()
+            .iter()
+            .cloned()
+            .map(|g| Type::app(derive_type.clone(), collect![Type::generic(g)])),
+        Type::app(derive_type.clone(), collect![self_type]),
+    )
+}
diff --git a/vm/src/derive/serialize.rs b/vm/src/derive/serialize.rs
new file mode 100644
index 00000000..5bbb9bce
--- /dev/null
+++ b/vm/src/derive/serialize.rs
@@ -0,0 +1,218 @@
+use base::ast::{
+    Alternative, Argument, AstType, Expr, ExprField, Pattern, TypeBinding, TypedIdent, ValueBinding,
+};
+use base::pos;
+use base::symbol::{Symbol, Symbols};
+use base::types::{arg_iter, remove_forall, row_iter, Type};
+
+use macros::Error;
+
+use derive::*;
+
+pub fn generate(
+    symbols: &mut Symbols,
+    bind: &TypeBinding<Symbol>,
+) -> Result<ValueBinding<Symbol>, Error> {
+    let span = bind.name.span;
+
+    let x = Symbol::from("x");
+
+    let serialize_fn = TypedIdent::new(symbols.symbol("serialize"));
+
+    let self_type: AstType<_> = Type::app(
+        Type::ident(bind.alias.value.name.clone()),
+        bind.alias
+            .value
+            .params()
+            .iter()
+            .cloned()
+            .map(Type::generic)
+            .collect(),
+    );
+
+    let serializer_expr = match **remove_forall(bind.alias.value.unresolved_type()) {
+        Type::Record(ref row) => {
+            let field_symbols: Vec<_> = row_iter(row)
+                .map(|field| {
+                    TypedIdent::new(Symbol::from(format!("{}", field.name.declared_name())))
+                }).collect();
+
+            let construct_map_expr = field_symbols
+                .iter()
+                .fold(None, |prev, symbol| {
+                    let serialize_field = ident(span, symbol.name.clone());
+
+                    let map = app(
+                        span,
+                        symbols.symbol("singleton"),
+                        vec![literal(span, symbol.name.declared_name()), serialize_field],
+                    );
+
+                    Some(match prev {
+                        Some(prev) => infix(span, prev, symbols.symbol("<>"), map),
+                        None => map,
+                    })
+                }).unwrap_or_else(|| ident(span, symbols.symbol("empty")));
+
+            let construct_object_expr = app(
+                span,
+                symbols.symbol("Object"),
+                vec![paren(span, construct_map_expr)],
+            );
+
+            let expr = sequence_actions(
+                symbols,
+                span,
+                &field_symbols,
+                construct_object_expr,
+                &mut |symbol| {
+                    app(
+                        span,
+                        serialize_fn.name.clone(),
+                        vec![ident(span, symbol.clone())],
+                    )
+                },
+            );
+
+            pos::spanned(
+                span,
+                Expr::Match(
+                    Box::new(ident(span, x.clone())),
+                    vec![Alternative {
+                        pattern: generate_record_pattern(span, row, field_symbols),
+                        expr,
+                    }],
+                ),
+            )
+        }
+        Type::Variant(ref row) => {
+            let alts = row_iter(row)
+                .map(|variant| {
+                    let pattern_args: Vec<_> = arg_iter(&variant.typ)
+                        .enumerate()
+                        .map(|(i, _typ)| TypedIdent::new(Symbol::from(format!("arg_{}", i))))
+                        .collect();
+
+                    let expr = {
+                        if pattern_args.len() > 1 {
+                            return Err(format!(
+                                "Variants with more than 1 argument is not yet supported"
+                            ).into());
+                        }
+
+                        if pattern_args.is_empty() {
+                            app(
+                                span,
+                                serialize_fn.name.clone(),
+                                vec![pos::spanned(
+                                    span,
+                                    Expr::Tuple {
+                                        elems: vec![],
+                                        typ: Type::hole(),
+                                    },
+                                )],
+                            )
+                        } else {
+                            let arg = &pattern_args[0];
+                            app(
+                                span,
+                                serialize_fn.name.clone(),
+                                vec![ident(span, arg.name.clone())],
+                            )
+                        }
+                    };
+
+                    let ctor_pattern = |pattern_args: Vec<_>| {
+                        pos::spanned(
+                            span,
+                            Pattern::Constructor(
+                                TypedIdent::new(variant.name.clone()),
+                                pattern_args
+                                    .into_iter()
+                                    .map(|arg| pos::spanned(span, Pattern::Ident(arg)))
+                                    .collect(),
+                            ),
+                        )
+                    };
+                    Ok(Alternative {
+                        pattern: ctor_pattern(pattern_args),
+                        expr,
+                    })
+                }).collect::<Result<_, Error>>()?;
+
+            pos::spanned(span, Expr::Match(Box::new(ident(span, x.clone())), alts))
+        }
+
+        _ => return Err("Unable to derive Deserialize for this type".into()),
+    };
+
+    let serialization_import = generate_import_(
+        span,
+        symbols,
+        &["ValueSerializer", "Value"],
+        &["serialize"],
+        true,
+        "std.json.ser",
+    );
+    let functor_import = generate_import(span, symbols, &[], &["map"], "std.functor");
+    let applicative_import = generate_import(span, symbols, &[], &["<*>"], "std.applicative");
+    let map_import = generate_import_(span, symbols, &[], &["singleton", "empty"], true, "std.map");
+    let semigroup_import = generate_import(span, symbols, &[], &["<>"], "std.semigroup");
+    let result_import = generate_import_(span, symbols, &[], &[], true, "std.result");
+
+    let serialize_ = TypedIdent::new(symbols.symbol("serialize_"));
+    let serializer_binding = ValueBinding {
+        name: pos::spanned(span, Pattern::Ident(serialize_.clone())),
+        args: vec![Argument::explicit(pos::spanned(
+            span,
+            TypedIdent::new(x.clone()),
+        ))],
+        expr: serializer_expr,
+        metadata: Default::default(),
+        typ: Some(Type::function(collect![self_type.clone()], Type::hole())),
+        resolved_type: Type::hole(),
+    };
+
+    let export_expr = pos::spanned(
+        span,
+        Expr::Record {
+            typ: Type::hole(),
+            types: Vec::new(),
+            exprs: vec![ExprField {
+                metadata: Default::default(),
+                name: pos::spanned(span, symbols.symbol("serialize")),
+                value: Some(ident(span, serialize_.name.clone())),
+            }],
+            base: None,
+        },
+    );
+
+    let serializer_record_expr = vec![
+        result_import,
+        serialization_import,
+        functor_import,
+        applicative_import,
+        map_import,
+        semigroup_import,
+        serializer_binding,
+    ].into_iter()
+    .rev()
+    .fold(export_expr, |expr, bind| {
+        pos::spanned(span, Expr::let_binding(bind, expr))
+    });
+
+    Ok(ValueBinding {
+        name: pos::spanned(
+            span,
+            Pattern::Ident(TypedIdent::new(symbols.symbol(format!(
+                "serialize_{}",
+                bind.alias.value.name.declared_name()
+            )))),
+        ),
+        args: Vec::new(),
+        expr: serializer_record_expr,
+        metadata: Default::default(),
+        typ: Some(binding_type(symbols, "Serialize", self_type, bind)),
+        resolved_type: Type::hole(),
+    })
+}
diff --git a/vm/src/derive/show.rs b/vm/src/derive/show.rs
new file mode 100644
index 00000000..6e14afa2
--- /dev/null
+++ b/vm/src/derive/show.rs
@@ -0,0 +1,189 @@
+use base::ast::{
+    Alternative, Argument, AstType, Expr, ExprField, Pattern, TypeBinding, TypedIdent, ValueBinding,
+};
+use base::pos;
+use base::symbol::{Symbol, Symbols};
+use base::types::{arg_iter, remove_forall, row_iter, Type};
+
+use macros::Error;
+
+use derive::*;
+
+pub fn generate(
+    symbols: &mut Symbols,
+    bind: &TypeBinding<Symbol>,
+) -> Result<ValueBinding<Symbol>, Error> {
+    let span = bind.name.span;
+
+    let x = Symbol::from("x");
+    let show_fn = TypedIdent::new(symbols.symbol("show_"));
+
+    let show_expr = match **remove_forall(bind.alias.value.unresolved_type()) {
+        Type::Variant(ref variants) => {
+            let alts = row_iter(variants)
+                .map(|variant| {
+                    let pattern_args: Vec<_> = arg_iter(&variant.typ)
+                        .enumerate()
+                        .map(|(i, typ)| {
+                            (
+                                is_self_type(&bind.alias.value.name, typ),
+                                TypedIdent::new(Symbol::from(format!("arg_{}", i))),
+                            )
+                        }).collect();
+
+                    let expr = {
+                        let open_brace = literal(span, variant.name.declared_name());
+
+                        pattern_args
+                            .iter()
+                            .fold(open_brace, |acc, &(self_type, ref x)| {
+                                let show_function = if self_type {
+                                    show_fn.name.clone()
+                                } else {
+                                    symbols.symbol("show")
+                                };
+                                let show = infix(
+                                    span,
+                                    literal(span, "("),
+                                    symbols.symbol("++"),
+                                    infix(
+                                        span,
+                                        app(span, show_function, vec![ident(span, x.name.clone())]),
+                                        symbols.symbol("++"),
+                                        literal(span, ")"),
+                                    ),
+                                );
+                                infix(
+                                    span,
+                                    acc,
+                                    symbols.symbol("++"),
+                                    infix(span, literal(span, " "), symbols.symbol("++"), show),
+                                )
+                            })
+                    };
+
+                    let ctor_pattern = |pattern_args: Vec<_>| {
+                        pos::spanned(
+                            span,
+                            Pattern::Constructor(
+                                TypedIdent::new(variant.name.clone()),
+                                pattern_args
+                                    .into_iter()
+                                    .map(|arg| pos::spanned(span, Pattern::Ident(arg)))
+                                    .collect(),
+                            ),
+                        )
+                    };
+                    Alternative {
+                        pattern: ctor_pattern(pattern_args.into_iter().map(|t| t.1).collect()),
+                        expr,
+                    }
+                }).collect();
+            Expr::Match(Box::new(ident(span, x.clone())), alts)
+        }
+        Type::Record(ref row) => {
+            let field_symbols: Vec<_> = row_iter(row)
+                .map(|field| {
+                    TypedIdent::new(Symbol::from(format!("{}", field.name.declared_name())))
+                }).collect();
+
+            let expr = {
+                let open_brace = literal(span, "{ ");
+
+                let show_expr = field_symbols
+                    .iter()
+                    .enumerate()
+                    .fold(open_brace, |acc, (i, x)| {
+                        let show = app(
+                            span,
+                            symbols.symbol("show"),
+                            vec![ident(span, x.name.clone())],
+                        );
+
+                        let show_field = infix(
+                            span,
+                            acc,
+                            symbols.symbol("++"),
+                            infix(
+                                span,
+                                literal(span, &format!("{} = ", x.name.declared_name())),
+                                symbols.symbol("++"),
+                                show,
+                            ),
+                        );
+
+                        let last = i == field_symbols.len() - 1;
+                        let suffix = if last { " " } else { ", " };
+                        infix(
+                            span,
+                            show_field,
+                            symbols.symbol("++"),
+                            literal(span, suffix),
+                        )
+                    });
+
+                infix(span, show_expr, symbols.symbol("++"), literal(span, "}"))
+            };
+            Expr::Match(
+                Box::new(ident(span, x.clone())),
+                vec![Alternative {
+                    pattern: generate_record_pattern(span, row, field_symbols),
+                    expr,
+                }],
+            )
+        }
+        _ => return Err("Unable to derive Show for this type".into()),
+    };
+
+    let self_type: AstType<_> = Type::app(
+        Type::ident(bind.alias.value.name.clone()),
+        bind.alias
+            .value
+            .params()
+            .iter()
+            .cloned()
+            .map(Type::generic)
+            .collect(),
+    );
+
+    let show_record_expr = Expr::rec_let_bindings(
+        vec![ValueBinding {
+            name: pos::spanned(span, Pattern::Ident(show_fn.clone())),
+            args: vec![Argument::explicit(pos::spanned(
+                span,
+                TypedIdent::new(x.clone()),
+            ))],
+            expr: pos::spanned(span, show_expr),
+            metadata: Default::default(),
+            typ: Some(Type::function(vec![self_type.clone()], Type::string())),
+            resolved_type: Type::hole(),
+        }],
+        pos::spanned(
+            span,
+            Expr::Record {
+                typ: Type::hole(),
+                types: Vec::new(),
+                exprs: vec![ExprField {
+                    metadata: Default::default(),
+                    name: pos::spanned(span, symbols.symbol("show")),
+                    value: Some(ident(span, show_fn.name.clone())),
+                }],
+                base: None,
+            },
+        ),
+    );
+
+    Ok(ValueBinding {
+        name: pos::spanned(
+            span,
+            Pattern::Ident(TypedIdent::new(
+                symbols.symbol(format!("show_{}", bind.alias.value.name.declared_name())),
+            )),
+        ),
+        args: Vec::new(),
+        expr: pos::spanned(span, show_record_expr),
+        metadata: Default::default(),
+        typ: Some(binding_type(symbols, "Show", self_type, bind)),
+        resolved_type: Type::hole(),
+    })
+}
diff --git a/vm/src/lib.rs b/vm/src/lib.rs
index 990f549d..5607f66d 100644
--- a/vm/src/lib.rs
+++ b/vm/src/lib.rs
@@ -196,7 +196,7 @@ impl<'a> fmt::Display for Panic<'a> {
     }
 }
 
-pub type ExternLoader = Box<FnMut(&Thread) -> Result<ExternModule> + Send + Sync>;
+pub type ExternLoader = fn(&Thread) -> Result<ExternModule>;
 
 pub struct ExternModule {
     pub metadata: Metadata,
diff --git a/vm/src/stack.rs b/vm/src/stack.rs
index 6527d2e6..9b29d97d 100644
--- a/vm/src/stack.rs
+++ b/vm/src/stack.rs
@@ -360,7 +360,8 @@ impl Stack {
                 })),
                 State::Unknown => Some(None),
                 State::Lock => None,
-            }).collect();
+            })
+            .collect();
         Stacktrace { frames: frames }
     }
 }
diff --git a/vm/src/thread.rs b/vm/src/thread.rs
index 4afa04ab..5de5ec84 100644
--- a/vm/src/thread.rs
+++ b/vm/src/thread.rs
@@ -871,12 +871,10 @@ where
         Self: Send + Sync,
     {
         let self_ = RootedThread::root(self.borrow());
-        let level = self_.context().stack.get_frames().len();
-
         Box::new(self.call_thunk(closure).or_else(move |mut err| {
             let mut context = self_.context();
             let stack = StackFrame::<State>::current(&mut context.stack);
-            let new_trace = reset_stack(stack, level)?;
+            let new_trace = reset_stack(stack, 1)?;
             if let Error::Panic(_, ref mut trace) = err {
                 *trace = Some(new_trace);
             }
@@ -895,11 +893,10 @@ where
         Self: Send + Sync,
     {
         let self_ = RootedThread::root(self.borrow());
-        let level = self_.context().stack.get_frames().len();
         Box::new(self.execute_io(value).or_else(move |mut err| {
             let mut context = self_.context();
             let stack = StackFrame::<State>::current(&mut context.stack);
-            let new_trace = reset_stack(stack, level)?;
+            let new_trace = reset_stack(stack, 1)?;
             if let Error::Panic(_, ref mut trace) = err {
                 *trace = Some(new_trace);
             }


Test patch:
diff --git a/repl/tests/rexpect.rs b/repl/tests/rexpect.rs
index 045ac0fb..e325664d 100644
--- a/repl/tests/rexpect.rs
+++ b/repl/tests/rexpect.rs
@@ -22,14 +22,16 @@ impl REPL {
     /// Wraps a rexpect::session::PtySession. expecting the prompt after launch.
     fn new_() -> Result<REPL> {
         let timeout: u64 = 30_000;
-        let prompt: &'static str = "REXPECT> ";
 
         let mut command = Command::new("../target/debug/gluon");
         command
-            .args(&["-i", "--color", "never", "--prompt", prompt])
+            .arg("-i")
+            .arg("--color")
+            .arg("never")
             .env("GLUON_PATH", "..");
         let mut session = spawn_command(command, Some(timeout))?;
 
+        let prompt: &'static str = "> ";
         session.exp_string(prompt)?;
 
         Ok(REPL { session, prompt })