Repository: rhaiscript/rhai (language: rust)

Issue:
More than six arguments?
Having read how Rhai functions are dispatched, I don't quite get why there is a fixed number of arguments limit. Arguments are basically passed as a six-elements-long homogeneous tuple of `Option<&mut Box<Any>>` only to assume last N of them are `None`. Not only does it introduce a argument number limit but implies quite a lot of code repetition. Wouldn't it be better to use slices instead of tuples?

Of course `fn_register` needs heterogeneousity for native Rust functions, at least until variadic generics come into play. But as for now, why not introduce some macro to automate N-arguments-precisely problem? Then it would be trivial to write another macro to abstract it away just to the limit of N. And even then, any limit doesn't seem really rational for Rhai-defined functions as it doesn't depend on variadic generics (it's dynamic anyway).


Discussion / hints:
The reason is historical, I'd say. When Jonathan first wrote Rhai, he was still learning Rust. Most of the baggage this brought has been fixed by refactoring, but stuff like this still remains, as we haven't gotten to changing it yet.

It would probably be better to use slices today, yes. Addition of macros to simplify things is on the TODO list, too.

Would you be interested in developing this better dispatch for Rhai functions?
Surely, why not.

Fix patch:
diff --git a/examples/arrays_and_structs.rs b/examples/arrays_and_structs.rs
index 4cd22b315..af7246fbc 100644
--- a/examples/arrays_and_structs.rs
+++ b/examples/arrays_and_structs.rs
@@ -1,9 +1,9 @@
 extern crate rhai;
-use rhai::{Engine, FnRegister};
+use rhai::{Engine, RegisterFn};
 
 #[derive(Clone, Debug)]
 struct TestStruct {
-    x: i64
+    x: i64,
 }
 
 impl TestStruct {
@@ -24,5 +24,12 @@ fn main() {
     engine.register_fn("update", TestStruct::update);
     engine.register_fn("new_ts", TestStruct::new);
 
-    println!("{:?}", engine.eval::<TestStruct>("let x = [new_ts()]; x[0].update(); x[0]"));
+    println!(
+        "{:?}",
+        engine.eval::<TestStruct>("let x = new_ts(); x.update(); x")
+    );
+    println!(
+        "{:?}",
+        engine.eval::<TestStruct>("let x = [new_ts()]; x[0].update(); x[0]")
+    );
 }
diff --git a/examples/custom_types_and_methods.rs b/examples/custom_types_and_methods.rs
index 7b70914fc..65cf32250 100644
--- a/examples/custom_types_and_methods.rs
+++ b/examples/custom_types_and_methods.rs
@@ -1,9 +1,9 @@
 extern crate rhai;
-use rhai::{Engine, FnRegister};
+use rhai::{Engine, RegisterFn};
 
 #[derive(Clone)]
 struct TestStruct {
-    x: i64
+    x: i64,
 }
 
 impl TestStruct {
diff --git a/examples/hello.rs b/examples/hello.rs
index 7f493b243..925902b8e 100644
--- a/examples/hello.rs
+++ b/examples/hello.rs
@@ -5,6 +5,6 @@ fn main() {
     let mut engine = Engine::new();
 
     if let Ok(result) = engine.eval::<i64>("40 + 2") {
-        println!("Answer: {}", result);  // prints 42
+        println!("Answer: {}", result); // prints 42
     }
-}
\ No newline at end of file
+}
diff --git a/examples/repl.rs b/examples/repl.rs
index bc2ee8023..145c288be 100644
--- a/examples/repl.rs
+++ b/examples/repl.rs
@@ -3,38 +3,40 @@ extern crate rhai;
 use std::fmt::Display;
 use std::process::exit;
 use std::io::{stdin, stdout, Write};
-use rhai::{Engine, Scope, FnRegister};
+use rhai::{Engine, RegisterFn, Scope};
 
 fn showit<T: Display>(x: &mut T) -> () {
     println!("{}", x)
 }
 
-fn quit() { exit(0); }
+fn quit() {
+    exit(0);
+}
 
 pub fn main() {
-	let mut engine = Engine::new();
-	let mut scope = Scope::new();
+    let mut engine = Engine::new();
+    let mut scope = Scope::new();
 
-	engine.register_fn("print", showit as fn(x: &mut i32)->());
-    engine.register_fn("print", showit as fn(x: &mut i64)->());
-    engine.register_fn("print", showit as fn(x: &mut u32)->());
-    engine.register_fn("print", showit as fn(x: &mut u64)->());
-    engine.register_fn("print", showit as fn(x: &mut f32)->());
-    engine.register_fn("print", showit as fn(x: &mut f64)->());
-    engine.register_fn("print", showit as fn(x: &mut bool)->());
-    engine.register_fn("print", showit as fn(x: &mut String)->());
+    engine.register_fn("print", showit as fn(x: &mut i32) -> ());
+    engine.register_fn("print", showit as fn(x: &mut i64) -> ());
+    engine.register_fn("print", showit as fn(x: &mut u32) -> ());
+    engine.register_fn("print", showit as fn(x: &mut u64) -> ());
+    engine.register_fn("print", showit as fn(x: &mut f32) -> ());
+    engine.register_fn("print", showit as fn(x: &mut f64) -> ());
+    engine.register_fn("print", showit as fn(x: &mut bool) -> ());
+    engine.register_fn("print", showit as fn(x: &mut String) -> ());
     engine.register_fn("exit", quit);
 
-	loop {
-		print!("> ");
-		let mut input = String::new();
-		stdout().flush().expect("couldn't flush stdout");
-		if let Err(e) = stdin().read_line(&mut input)  {
-			println!("input error: {}", e);
-		}
+    loop {
+        print!("> ");
+        let mut input = String::new();
+        stdout().flush().expect("couldn't flush stdout");
+        if let Err(e) = stdin().read_line(&mut input) {
+            println!("input error: {}", e);
+        }
 
-		if let Err(e) = engine.consume_with_scope(&mut scope, &input) {
-			println!("error: {}", e);
-		}
-	}
+        if let Err(e) = engine.consume_with_scope(&mut scope, &input) {
+            println!("error: {}", e);
+        }
+    }
 }
diff --git a/examples/reuse_scope.rs b/examples/reuse_scope.rs
index 9d2459c77..c7a8726bb 100644
--- a/examples/reuse_scope.rs
+++ b/examples/reuse_scope.rs
@@ -5,10 +5,14 @@ fn main() {
     let mut engine = Engine::new();
     let mut scope: Scope = Vec::new();
 
-    if !engine.eval_with_scope::<()>(&mut scope, "let x = 4 + 5").is_ok() { assert!(false); }
+    if !engine
+        .eval_with_scope::<()>(&mut scope, "let x = 4 + 5")
+        .is_ok()
+    {
+        assert!(false);
+    }
 
     if let Ok(result) = engine.eval_with_scope::<i64>(&mut scope, "x") {
-       println!("result: {}", result);
+        println!("result: {}", result);
     }
 }
-
diff --git a/examples/rhai_runner.rs b/examples/rhai_runner.rs
index abffb6ed3..6637844a9 100644
--- a/examples/rhai_runner.rs
+++ b/examples/rhai_runner.rs
@@ -2,7 +2,7 @@ use std::env;
 use std::fmt::Display;
 
 extern crate rhai;
-use rhai::{Engine, FnRegister};
+use rhai::{Engine, RegisterFn};
 
 fn showit<T: Display>(x: &mut T) -> () {
     println!("{}", x)
@@ -12,19 +12,18 @@ fn main() {
     for fname in env::args().skip(1) {
         let mut engine = Engine::new();
 
-        engine.register_fn("print", showit as fn(x: &mut i32)->());
-        engine.register_fn("print", showit as fn(x: &mut i64)->());
-        engine.register_fn("print", showit as fn(x: &mut u32)->());
-        engine.register_fn("print", showit as fn(x: &mut u64)->());
-        engine.register_fn("print", showit as fn(x: &mut f32)->());
-        engine.register_fn("print", showit as fn(x: &mut f64)->());
-        engine.register_fn("print", showit as fn(x: &mut bool)->());
-        engine.register_fn("print", showit as fn(x: &mut String)->());
+        engine.register_fn("print", showit as fn(x: &mut i32) -> ());
+        engine.register_fn("print", showit as fn(x: &mut i64) -> ());
+        engine.register_fn("print", showit as fn(x: &mut u32) -> ());
+        engine.register_fn("print", showit as fn(x: &mut u64) -> ());
+        engine.register_fn("print", showit as fn(x: &mut f32) -> ());
+        engine.register_fn("print", showit as fn(x: &mut f64) -> ());
+        engine.register_fn("print", showit as fn(x: &mut bool) -> ());
+        engine.register_fn("print", showit as fn(x: &mut String) -> ());
 
         match engine.eval_file::<()>(&fname) {
             Ok(_) => (),
-            Err(e) => {println!("Error: {}", e)}
+            Err(e) => println!("Error: {}", e),
         }
     }
 }
-
diff --git a/examples/simple_fn.rs b/examples/simple_fn.rs
index 541edf1e1..04b55b68a 100644
--- a/examples/simple_fn.rs
+++ b/examples/simple_fn.rs
@@ -1,5 +1,5 @@
 extern crate rhai;
-use rhai::{Engine, FnRegister};
+use rhai::{Engine, RegisterFn};
 
 fn add(x: i64, y: i64) -> i64 {
     x + y
@@ -9,8 +9,7 @@ fn main() {
     let mut engine = Engine::new();
 
     engine.register_fn("add", add);
-
     if let Ok(result) = engine.eval::<i64>("add(40, 2)") {
-       println!("Answer: {}", result);  // prints 42
+        println!("Answer: {}", result); // prints 42
     }
 }
diff --git a/src/any.rs b/src/any.rs
new file mode 100644
index 000000000..114dad9b7
--- /dev/null
+++ b/src/any.rs
@@ -0,0 +1,102 @@
+use std::any::{Any as StdAny, TypeId};
+use std::fmt;
+
+pub trait Any: StdAny {
+    fn type_id(&self) -> TypeId;
+
+    fn box_clone(&self) -> Box<Any>;
+
+    /// This type may only be implemented by `rhai`.
+    #[doc(hidden)]
+    fn _closed(&self) -> _Private;
+}
+
+impl<T> Any for T
+    where
+        T: Clone + StdAny + ?Sized
+{
+    #[inline]
+    fn type_id(&self) -> TypeId {
+        TypeId::of::<T>()
+    }
+
+    #[inline]
+    fn box_clone(&self) -> Box<Any> {
+        Box::new(self.clone())
+    }
+
+    fn _closed(&self) -> _Private {
+        _Private
+    }
+}
+
+impl Any {
+    #[inline]
+    fn box_clone(&self) -> Box<Any> {
+        Any::box_clone(self)
+    }
+
+    #[inline]
+    pub fn is<T: Any>(&self) -> bool {
+        let t = TypeId::of::<T>();
+        let boxed = self.type_id();
+
+        t == boxed
+    }
+
+    #[inline]
+    pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
+        if self.is::<T>() {
+            unsafe {
+                Some(&*(self as *const Any as *const T))
+            }
+        } else {
+            None
+        }
+    }
+
+    #[inline]
+    pub fn downcast_mut<T: Any>(&mut self) -> Option<&mut T> {
+        if self.is::<T>() {
+            unsafe {
+                Some(&mut *(self as *mut Any as *mut T))
+            }
+        } else {
+            None
+        }
+    }
+}
+
+impl Clone for Box<Any> {
+    fn clone(&self) -> Self {
+        Any::box_clone(self.as_ref() as &Any)
+    }
+}
+
+impl fmt::Debug for Any {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        f.pad("Any")
+    }
+}
+
+pub trait AnyExt: Sized {
+    fn downcast<T: Any + Clone>(self) -> Result<Box<T>, Self>;
+}
+
+impl AnyExt for Box<Any> {
+    fn downcast<T: Any + Clone>(self) -> Result<Box<T>, Self> {
+        if self.is::<T>() {
+            unsafe {
+                let raw: *mut Any = Box::into_raw(self);
+                Ok(Box::from_raw(raw as *mut T))
+            }
+        } else {
+            Err(self)
+        }
+    }
+}
+
+/// Private type which ensures that `rhai::Any` can only
+/// be implemented by this crate.
+#[doc(hidden)]
+pub struct _Private;
diff --git a/src/call.rs b/src/call.rs
new file mode 100644
index 000000000..cf3313e3b
--- /dev/null
+++ b/src/call.rs
@@ -0,0 +1,36 @@
+//! Helper module which defines `FnArgs`
+//! to make function calling easier.
+
+use any::Any;
+
+pub trait FunArgs<'a> {
+    fn into_vec(self) -> Vec<&'a mut Any>;
+}
+
+macro_rules! impl_args {
+    ($($p:ident),*) => {
+        impl<'a, $($p),*> FunArgs<'a> for ($(&'a mut $p,)*)
+        where
+            $($p: Any + Clone),*
+        {
+            fn into_vec(self) -> Vec<&'a mut Any> {
+                let ($($p,)*) = self;
+
+                let mut v = Vec::new();
+                $(v.push($p as &mut Any);)*
+
+                v
+            }
+        }
+
+        impl_args!(@pop $($p),*);
+    };
+    (@pop) => {
+    };
+    (@pop $head:ident $(, $tail:ident)*) => {
+        impl_args!($($tail),*);
+    };
+}
+
+#[cfg_attr(rustfmt, rustfmt_skip)]
+impl_args!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S);
diff --git a/src/engine.rs b/src/engine.rs
index 18aa0ce6b..88563a45c 100644
--- a/src/engine.rs
+++ b/src/engine.rs
@@ -1,14 +1,15 @@
+use std::any::TypeId;
+use std::borrow::Borrow;
+use std::cmp::{PartialEq, PartialOrd};
 use std::collections::HashMap;
 use std::error::Error;
-use std::any::Any;
-use std::boxed::Box;
 use std::fmt;
+use std::ops::{Add, BitAnd, BitOr, BitXor, Deref, Div, Mul, Neg, Rem, Shl, Shr, Sub};
 
-use parser::{lex, parse, Expr, Stmt, FnDef};
-use fn_register::FnRegister;
-
-use std::ops::{Add, Sub, Mul, Div, Neg, BitAnd, BitOr, BitXor, Shl, Shr, Rem};
-use std::cmp::{PartialOrd, PartialEq};
+use any::{Any, AnyExt};
+use fn_register::{Mut, RegisterFn};
+use parser::{lex, parse, Expr, FnDef, Stmt};
+use call::FunArgs;
 
 #[derive(Debug)]
 pub enum EvalAltResult {
@@ -27,6 +28,27 @@ pub enum EvalAltResult {
     Return(Box<Any>),
 }
 
+impl PartialEq for EvalAltResult {
+    fn eq(&self, other: &Self) -> bool {
+        use EvalAltResult::*;
+
+        match (self, other) {
+            (&ErrorFunctionNotFound, &ErrorFunctionNotFound) => true,
+            (&ErrorFunctionArgMismatch, &ErrorFunctionArgMismatch) => true,
+            (&ErrorFunctionCallNotSupported, &ErrorFunctionCallNotSupported) => true,
+            (&ErrorIndexMismatch, &ErrorIndexMismatch) => true,
+            (&ErrorIfGuardMismatch, &ErrorIfGuardMismatch) => true,
+            (&ErrorVariableNotFound(ref a), &ErrorVariableNotFound(ref b)) => a == b,
+            (&ErrorFunctionArityNotSupported, &ErrorFunctionArityNotSupported) => true,
+            (&ErrorAssignmentToUnknownLHS, &ErrorAssignmentToUnknownLHS) => true,
+            (&ErrorMismatchOutputType, &ErrorMismatchOutputType) => true,
+            (&ErrorCantOpenScriptFile, &ErrorCantOpenScriptFile) => true,
+            (&InternalErrorMalformedDotExpression, &InternalErrorMalformedDotExpression) => true,
+            (&LoopBreak, &LoopBreak) => true,
+            _ => false,
+        }
+    }
+}
 
 impl Error for EvalAltResult {
     fn description(&self) -> &str {
@@ -66,29 +88,10 @@ impl fmt::Display for EvalAltResult {
     }
 }
 
-pub enum FnType {
-    ExternalFn0(Box<Fn() -> Result<Box<Any>, EvalAltResult>>),
-    ExternalFn1(Box<Fn(&mut Box<Any>) -> Result<Box<Any>, EvalAltResult>>),
-    ExternalFn2(Box<Fn(&mut Box<Any>, &mut Box<Any>) -> Result<Box<Any>, EvalAltResult>>),
-    ExternalFn3(Box<Fn(&mut Box<Any>, &mut Box<Any>, &mut Box<Any>)
-                       -> Result<Box<Any>, EvalAltResult>>),
-    ExternalFn4(Box<Fn(&mut Box<Any>, &mut Box<Any>, &mut Box<Any>, &mut Box<Any>)
-                       -> Result<Box<Any>, EvalAltResult>>),
-    ExternalFn5(Box<Fn(&mut Box<Any>,
-                       &mut Box<Any>,
-                       &mut Box<Any>,
-                       &mut Box<Any>,
-                       &mut Box<Any>)
-                       -> Result<Box<Any>, EvalAltResult>>),
-    ExternalFn6(Box<Fn(&mut Box<Any>,
-                       &mut Box<Any>,
-                       &mut Box<Any>,
-                       &mut Box<Any>,
-                       &mut Box<Any>,
-                       &mut Box<Any>)
-                       -> Result<Box<Any>, EvalAltResult>>),
-
-    InternalFn(FnDef),
+#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
+pub struct FnSpec {
+    ident: String,
+    args: Option<Vec<TypeId>>,
 }
 
 /// Rhai's engine type. This is what you use to run Rhai scripts
@@ -107,9 +110,16 @@ pub enum FnType {
 /// ```
 pub struct Engine {
     /// A hashmap containing all functions know to the engine
-    pub fns: HashMap<String, Vec<FnType>>,
+    pub fns: HashMap<FnSpec, FnIntExt>,
 }
 
+pub enum FnIntExt {
+    Ext(Box<FnAny>),
+    Int(FnDef),
+}
+
+pub type FnAny = Fn(Vec<&mut Any>) -> Result<Box<Any>, EvalAltResult>;
+
 /// A type containing information about current scope.
 /// Useful for keeping state between `Engine` runs
 ///
@@ -127,820 +137,280 @@ pub struct Engine {
 pub type Scope = Vec<(String, Box<Any>)>;
 
 impl Engine {
+    pub fn call_fn<'a, I, A, T>(&self, ident: I, args: A) -> Result<T, EvalAltResult>
+    where
+        I: Into<String>,
+        A: FunArgs<'a>,
+        T: Any + Clone,
+    {
+        self.call_fn_raw(ident.into(), args.into_vec())
+            .and_then(|b| {
+                b.downcast()
+                    .map(|b| *b)
+                    .map_err(|_| EvalAltResult::ErrorMismatchOutputType)
+            })
+    }
+
     /// Universal method for calling functions, that are either
     /// registered with the `Engine` or written in Rhai
-    pub fn call_fn(&self,
-               name: &str,
-               arg1: Option<&mut Box<Any>>,
-               arg2: Option<&mut Box<Any>>,
-               arg3: Option<&mut Box<Any>>,
-               arg4: Option<&mut Box<Any>>,
-               arg5: Option<&mut Box<Any>>,
-               arg6: Option<&mut Box<Any>>)
-               -> Result<Box<Any>, EvalAltResult> {
-
-        match self.fns.get(name) {
-            Some(vf) => {
-                match (arg1, arg2, arg3, arg4, arg5, arg6) {
-                    (Some(ref mut a1),
-                     Some(ref mut a2),
-                     Some(ref mut a3),
-                     Some(ref mut a4),
-                     Some(ref mut a5),
-                     Some(ref mut a6)) => {
-                        for arr_f in vf {
-                            match *arr_f {
-                                FnType::ExternalFn6(ref f) => {
-                                    if let Ok(v) = f(*a1, *a2, *a3, *a4, *a5, *a6) {
-                                        return Ok(v);
-                                    }
-                                }
-                                FnType::InternalFn(ref f) => {
-                                    if f.params.len() != 6 {
-                                        return Err(EvalAltResult::ErrorFunctionArgMismatch);
-                                    }
-
-                                    let mut new_scope: Scope = Vec::new();
-                                    let result1 = self.call_fn("clone",
-                                                               Some(a1),
-                                                               None,
-                                                               None,
-                                                               None,
-                                                               None,
-                                                               None);
-                                    let result2 = self.call_fn("clone",
-                                                               Some(a2),
-                                                               None,
-                                                               None,
-                                                               None,
-                                                               None,
-                                                               None);
-                                    let result3 = self.call_fn("clone",
-                                                               Some(a3),
-                                                               None,
-                                                               None,
-                                                               None,
-                                                               None,
-                                                               None);
-                                    let result4 = self.call_fn("clone",
-                                                               Some(a4),
-                                                               None,
-                                                               None,
-                                                               None,
-                                                               None,
-                                                               None);
-                                    let result5 = self.call_fn("clone",
-                                                               Some(a5),
-                                                               None,
-                                                               None,
-                                                               None,
-                                                               None,
-                                                               None);
-                                    let result6 = self.call_fn("clone",
-                                                               Some(a6),
-                                                               None,
-                                                               None,
-                                                               None,
-                                                               None,
-                                                               None);
-
-                                    match (result1, result2, result3, result4, result5, result6) {
-                                        (Ok(r1), Ok(r2), Ok(r3), Ok(r4), Ok(r5), Ok(r6)) => {
-                                            new_scope.push((f.params[0].clone(), r1));
-                                            new_scope.push((f.params[1].clone(), r2));
-                                            new_scope.push((f.params[2].clone(), r3));
-                                            new_scope.push((f.params[3].clone(), r4));
-                                            new_scope.push((f.params[4].clone(), r5));
-                                            new_scope.push((f.params[5].clone(), r6));
-                                        }
-                                        _ => return Err(EvalAltResult::ErrorFunctionArgMismatch),
-                                    }
-                                    match self.eval_stmt(&mut new_scope, &*f.body) {
-                                        Err(EvalAltResult::Return(x)) => return Ok(x),
-                                        x => return x,
-                                    }
-                                }
-                                _ => (),
-                            }
-                        }
-                        Err(EvalAltResult::ErrorFunctionArgMismatch)
-                    }
-                    (Some(ref mut a1),
-                     Some(ref mut a2),
-                     Some(ref mut a3),
-                     Some(ref mut a4),
-                     Some(ref mut a5),
-                     None) => {
-                        for arr_f in vf {
-                            match *arr_f {
-                                FnType::ExternalFn5(ref f) => {
-                                    if let Ok(v) = f(*a1, *a2, *a3, *a4, *a5) {
-                                        return Ok(v);
-                                    }
-                                }
-                                FnType::InternalFn(ref f) => {
-                                    if f.params.len() != 5 {
-                                        return Err(EvalAltResult::ErrorFunctionArgMismatch);
-                                    }
-
-                                    let mut new_scope: Scope = Vec::new();
-                                    let result1 = self.call_fn("clone",
-                                                               Some(a1),
-                                                               None,
-                                                               None,
-                                                               None,
-                                                               None,
-                                                               None);
-                                    let result2 = self.call_fn("clone",
-                                                               Some(a2),
-                                                               None,
-                                                               None,
-                                                               None,
-                                                               None,
-                                                               None);
-                                    let result3 = self.call_fn("clone",
-                                                               Some(a3),
-                                                               None,
-                                                               None,
-                                                               None,
-                                                               None,
-                                                               None);
-                                    let result4 = self.call_fn("clone",
-                                                               Some(a4),
-                                                               None,
-                                                               None,
-                                                               None,
-                                                               None,
-                                                               None);
-                                    let result5 = self.call_fn("clone",
-                                                               Some(a5),
-                                                               None,
-                                                               None,
-                                                               None,
-                                                               None,
-                                                               None);
-
-                                    match (result1, result2, result3, result4, result5) {
-                                        (Ok(r1), Ok(r2), Ok(r3), Ok(r4), Ok(r5)) => {
-                                            new_scope.push((f.params[0].clone(), r1));
-                                            new_scope.push((f.params[1].clone(), r2));
-                                            new_scope.push((f.params[2].clone(), r3));
-                                            new_scope.push((f.params[3].clone(), r4));
-                                            new_scope.push((f.params[4].clone(), r5));
-                                        }
-                                        _ => return Err(EvalAltResult::ErrorFunctionArgMismatch),
-                                    }
-                                    match self.eval_stmt(&mut new_scope, &*f.body) {
-                                        Err(EvalAltResult::Return(x)) => return Ok(x),
-                                        x => return x,
-                                    }
-                                }
-                                _ => (),
-                            }
-                        }
-                        Err(EvalAltResult::ErrorFunctionArgMismatch)
-                    }
-                    (Some(ref mut a1),
-                     Some(ref mut a2),
-                     Some(ref mut a3),
-                     Some(ref mut a4),
-                     None,
-                     None) => {
-                        for arr_f in vf {
-                            match *arr_f {
-                                FnType::ExternalFn4(ref f) => {
-                                    if let Ok(v) = f(*a1, *a2, *a3, *a4) {
-                                        return Ok(v)
-                                    }
-                                }
-                                FnType::InternalFn(ref f) => {
-                                    if f.params.len() != 4 {
-                                        return Err(EvalAltResult::ErrorFunctionArgMismatch);
-                                    }
-
-                                    let mut new_scope: Scope = Vec::new();
-                                    let result1 = self.call_fn("clone",
-                                                               Some(a1),
-                                                               None,
-                                                               None,
-                                                               None,
-                                                               None,
-                                                               None);
-                                    let result2 = self.call_fn("clone",
-                                                               Some(a2),
-                                                               None,
-                                                               None,
-                                                               None,
-                                                               None,
-                                                               None);
-                                    let result3 = self.call_fn("clone",
-                                                               Some(a3),
-                                                               None,
-                                                               None,
-                                                               None,
-                                                               None,
-                                                               None);
-                                    let result4 = self.call_fn("clone",
-                                                               Some(a4),
-                                                               None,
-                                                               None,
-                                                               None,
-                                                               None,
-                                                               None);
-                                    match (result1, result2, result3, result4) {
-                                        (Ok(r1), Ok(r2), Ok(r3), Ok(r4)) => {
-                                            new_scope.push((f.params[0].clone(), r1));
-                                            new_scope.push((f.params[1].clone(), r2));
-                                            new_scope.push((f.params[2].clone(), r3));
-                                            new_scope.push((f.params[3].clone(), r4));
-                                        }
-                                        _ => return Err(EvalAltResult::ErrorFunctionArgMismatch),
-                                    }
-                                    match self.eval_stmt(&mut new_scope, &*f.body) {
-                                        Err(EvalAltResult::Return(x)) => return Ok(x),
-                                        x => return x,
-                                    }
-                                }
-                                _ => (),
-                            }
-                        }
-                        Err(EvalAltResult::ErrorFunctionArgMismatch)
-                    }
-                    (Some(ref mut a1), Some(ref mut a2), Some(ref mut a3), None, None, None) => {
-                        for arr_f in vf {
-                            match *arr_f {
-                                FnType::ExternalFn3(ref f) => {
-                                    if let Ok(v) = f(*a1, *a2, *a3) {
-                                        return Ok(v);
-                                    }
-                                }
-                                FnType::InternalFn(ref f) => {
-                                    if f.params.len() != 3 {
-                                        return Err(EvalAltResult::ErrorFunctionArgMismatch);
-                                    }
-
-                                    let mut new_scope: Scope = Vec::new();
-                                    let result1 = self.call_fn("clone",
-                                                               Some(a1),
-                                                               None,
-                                                               None,
-                                                               None,
-                                                               None,
-                                                               None);
-                                    let result2 = self.call_fn("clone",
-                                                               Some(a2),
-                                                               None,
-                                                               None,
-                                                               None,
-                                                               None,
-                                                               None);
-                                    let result3 = self.call_fn("clone",
-                                                               Some(a3),
-                                                               None,
-                                                               None,
-                                                               None,
-                                                               None,
-                                                               None);
-                                    match (result1, result2, result3) {
-                                        (Ok(r1), Ok(r2), Ok(r3)) => {
-                                            new_scope.push((f.params[0].clone(), r1));
-                                            new_scope.push((f.params[1].clone(), r2));
-                                            new_scope.push((f.params[2].clone(), r3));
-                                        }
-                                        _ => return Err(EvalAltResult::ErrorFunctionArgMismatch),
-                                    }
-                                    match self.eval_stmt(&mut new_scope, &*f.body) {
-                                        Err(EvalAltResult::Return(x)) => return Ok(x),
-                                        x => return x,
-                                    }
-                                }
-                                _ => (),
-                            }
-                        }
-                        Err(EvalAltResult::ErrorFunctionArgMismatch)
+    pub fn call_fn_raw(
+        &self,
+        ident: String,
+        args: Vec<&mut Any>,
+    ) -> Result<Box<Any>, EvalAltResult> {
+        println!(
+            "Trying to call function {:?} with args {:?}",
+            ident,
+            args.iter().map(|x| (&**x).type_id()).collect::<Vec<_>>()
+        );
+
+        let spec = FnSpec {
+            ident: ident.clone(),
+            args: Some(args.iter().map(|a| <Any as Any>::type_id(&**a)).collect()),
+        };
+        let spec1 = FnSpec { ident, args: None };
+
+        self.fns
+            .get(&spec)
+            .or_else(|| self.fns.get(&spec1))
+            .ok_or(EvalAltResult::ErrorFunctionNotFound)
+            .and_then(move |f| match *f {
+                FnIntExt::Ext(ref f) => f(args),
+                FnIntExt::Int(ref f) => {
+                    let mut scope = Scope::new();
+                    scope.extend(
+                        f.params
+                            .iter()
+                            .cloned()
+                            .zip(args.iter().map(|x| (&**x).box_clone())),
+                    );
+
+                    match self.eval_stmt(&mut scope, &*f.body) {
+                        Err(EvalAltResult::Return(x)) => Ok(x),
+                        other => other,
                     }
-                    (Some(ref mut a1), Some(ref mut a2), None, None, None, None) => {
-                        for arr_f in vf {
-                            match *arr_f {
-                                FnType::ExternalFn2(ref f) => {
-                                    if let Ok(v) = f(*a1, *a2) {
-                                        return Ok(v);
-                                    }
-                                }
-                                FnType::InternalFn(ref f) => {
-                                    if f.params.len() != 2 {
-                                        return Err(EvalAltResult::ErrorFunctionArgMismatch);
-                                    }
+                }
+            })
+    }
 
-                                    let mut new_scope: Scope = Vec::new();
-                                    let result1 = self.call_fn("clone",
-                                                               Some(a1),
-                                                               None,
-                                                               None,
-                                                               None,
-                                                               None,
-                                                               None);
-                                    let result2 = self.call_fn("clone",
-                                                               Some(a2),
-                                                               None,
-                                                               None,
-                                                               None,
-                                                               None,
-                                                               None);
-                                    match (result1, result2) {
-                                        (Ok(r1), Ok(r2)) => {
-                                            new_scope.push((f.params[0].clone(), r1));
-                                            new_scope.push((f.params[1].clone(), r2));
-                                        }
-                                        _ => return Err(EvalAltResult::ErrorFunctionArgMismatch),
-                                    }
-                                    match self.eval_stmt(&mut new_scope, &*f.body) {
-                                        Err(EvalAltResult::Return(x)) => return Ok(x),
-                                        x => return x,
-                                    }
-                                }
-                                _ => (),
-                            }
-                        }
-                        Err(EvalAltResult::ErrorFunctionArgMismatch)
-                    }
-                    (Some(ref mut a1), None, None, None, None, None) => {
-                        for arr_f in vf {
-                            match *arr_f {
-                                FnType::ExternalFn1(ref f) => {
-                                    if let Ok(v) = f(*a1) {
-                                        return Ok(v);
-                                    }
-                                }
-                                FnType::InternalFn(ref f) => {
-                                    if f.params.len() != 1 {
-                                        return Err(EvalAltResult::ErrorFunctionArgMismatch);
-                                    }
+    pub fn register_fn_raw(&mut self, ident: String, args: Option<Vec<TypeId>>, f: Box<FnAny>) {
+        println!("Register; {:?} with args {:?}", ident, args,);
 
-                                    let mut new_scope: Scope = Vec::new();
-                                    let result1 = self.call_fn("clone",
-                                                               Some(a1),
-                                                               None,
-                                                               None,
-                                                               None,
-                                                               None,
-                                                               None);
-                                    match result1 {
-                                        Ok(r1) => {
-                                            new_scope.push((f.params[0].clone(), r1));
-                                        }
-                                        _ => return Err(EvalAltResult::ErrorFunctionArgMismatch),
-                                    }
-                                    match self.eval_stmt(&mut new_scope, &*f.body) {
-                                        Err(EvalAltResult::Return(x)) => return Ok(x),
-                                        x => return x,
-                                    }
-                                }
-                                _ => (),
-                            }
-                        }
-                        Err(EvalAltResult::ErrorFunctionArgMismatch)
-                    }
-                    _ => {
-                        for arr_f in vf {
-                            match *arr_f {
-                                FnType::ExternalFn0(ref f) => {
-                                    if let Ok(v) = f() {
-                                        return Ok(v);
-                                    }
-                                }
-                                FnType::InternalFn(ref f) => {
-                                    if !f.params.is_empty() {
-                                        return Err(EvalAltResult::ErrorFunctionArgMismatch);
-                                    }
+        let spec = FnSpec { ident, args };
 
-                                    let mut new_scope: Scope = Vec::new();
-                                    match self.eval_stmt(&mut new_scope, &*f.body) {
-                                        Err(EvalAltResult::Return(x)) => return Ok(x),
-                                        x => return x,
-                                    }
-                                }
-                                _ => (),
-                            }
-                        }
-                        Err(EvalAltResult::ErrorFunctionArgMismatch)
-                    }
-                }
-            }
-            None => Err(EvalAltResult::ErrorFunctionNotFound),
-        }
+        self.fns.insert(spec, FnIntExt::Ext(f));
     }
 
     /// Register a type for use with Engine. Keep in mind that
     /// your type must implement Clone.
-    pub fn register_type<T: Clone + Any>(&mut self) {
-        fn clone_helper<T: Clone>(t: T) -> T {
-            t.clone()
-        };
-
-        self.register_fn("clone", clone_helper as fn(T) -> T);
+    pub fn register_type<T: Any>(&mut self) {
+        // currently a no-op, exists for future extensibility
     }
 
     /// Register a get function for a member of a registered type
     pub fn register_get<T: Clone + Any, U: Clone + Any, F>(&mut self, name: &str, get_fn: F)
-        where F: 'static + Fn(&mut T) -> U
+    where
+        F: 'static + Fn(&mut T) -> U,
     {
-
         let get_name = "get$".to_string() + name;
         self.register_fn(&get_name, get_fn);
     }
 
     /// Register a set function for a member of a registered type
     pub fn register_set<T: Clone + Any, U: Clone + Any, F>(&mut self, name: &str, set_fn: F)
-        where F: 'static + Fn(&mut T, U) -> ()
+    where
+        F: 'static + Fn(&mut T, U) -> (),
     {
-
         let set_name = "set$".to_string() + name;
         self.register_fn(&set_name, set_fn);
     }
 
     /// Shorthand for registering both getters and setters
-    pub fn register_get_set<T: Clone + Any, U: Clone + Any, F, G>(&mut self,
-                                                                  name: &str,
-                                                                  get_fn: F,
-                                                                  set_fn: G)
-        where F: 'static + Fn(&mut T) -> U,
-              G: 'static + Fn(&mut T, U) -> ()
+    pub fn register_get_set<T: Clone + Any, U: Clone + Any, F, G>(
+        &mut self,
+        name: &str,
+        get_fn: F,
+        set_fn: G,
+    ) where
+        F: 'static + Fn(&mut T) -> U,
+        G: 'static + Fn(&mut T, U) -> (),
     {
-
         self.register_get(name, get_fn);
         self.register_set(name, set_fn);
     }
 
-    fn get_dot_val_helper(&self,
-                          scope: &mut Scope,
-                          this_ptr: &mut Box<Any>,
-                          dot_rhs: &Expr)
-                          -> Result<Box<Any>, EvalAltResult> {
+    fn get_dot_val_helper(
+        &self,
+        scope: &mut Scope,
+        this_ptr: &mut Any,
+        dot_rhs: &Expr,
+    ) -> Result<Box<Any>, EvalAltResult> {
+        use std::iter::once;
+
         match *dot_rhs {
             Expr::FnCall(ref fn_name, ref args) => {
-                if args.is_empty() {
-                    self.call_fn(fn_name, Some(this_ptr), None, None, None, None, None)
-                } else if args.len() == 1 {
-                    let mut arg = self.eval_expr(scope, &args[0])?;
-
-                    self.call_fn(fn_name,
-                                 Some(this_ptr),
-                                 Some(&mut arg),
-                                 None,
-                                 None,
-                                 None,
-                                 None)
-                } else if args.len() == 2 {
-                    let mut arg1 = self.eval_expr(scope, &args[0])?;
-                    let mut arg2 = self.eval_expr(scope, &args[1])?;
-
-                    self.call_fn(fn_name,
-                                 Some(this_ptr),
-                                 Some(&mut arg1),
-                                 Some(&mut arg2),
-                                 None,
-                                 None,
-                                 None)
-                } else if args.len() == 3 {
-                    let mut arg1 = self.eval_expr(scope, &args[0])?;
-                    let mut arg2 = self.eval_expr(scope, &args[1])?;
-                    let mut arg3 = self.eval_expr(scope, &args[2])?;
-
-                    self.call_fn(fn_name,
-                                 Some(this_ptr),
-                                 Some(&mut arg1),
-                                 Some(&mut arg2),
-                                 Some(&mut arg3),
-                                 None,
-                                 None)
-                } else if args.len() == 4 {
-                    let mut arg1 = self.eval_expr(scope, &args[0])?;
-                    let mut arg2 = self.eval_expr(scope, &args[1])?;
-                    let mut arg3 = self.eval_expr(scope, &args[2])?;
-                    let mut arg4 = self.eval_expr(scope, &args[3])?;
-
-                    self.call_fn(fn_name,
-                                 Some(this_ptr),
-                                 Some(&mut arg1),
-                                 Some(&mut arg2),
-                                 Some(&mut arg3),
-                                 Some(&mut arg4),
-                                 None)
-                } else if args.len() == 5 {
-                    let mut arg1 = self.eval_expr(scope, &args[0])?;
-                    let mut arg2 = self.eval_expr(scope, &args[1])?;
-                    let mut arg3 = self.eval_expr(scope, &args[2])?;
-                    let mut arg4 = self.eval_expr(scope, &args[3])?;
-                    let mut arg5 = self.eval_expr(scope, &args[4])?;
-
-                    self.call_fn(fn_name,
-                                 Some(this_ptr),
-                                 Some(&mut arg1),
-                                 Some(&mut arg2),
-                                 Some(&mut arg3),
-                                 Some(&mut arg4),
-                                 Some(&mut arg5))
-                } else {
-                    Err(EvalAltResult::ErrorFunctionCallNotSupported)
-                }
+                let mut args: Vec<Box<Any>> = args.iter()
+                    .map(|arg| self.eval_expr(scope, arg))
+                    .collect::<Result<Vec<_>, _>>()?;
+                let args = once(this_ptr)
+                    .chain(args.iter_mut().map(|b| b.as_mut()))
+                    .collect();
+
+                self.call_fn_raw(fn_name.to_owned(), args)
             }
             Expr::Identifier(ref id) => {
                 let get_fn_name = "get$".to_string() + id;
-                self.call_fn(&get_fn_name, Some(this_ptr), None, None, None, None, None)
+
+                self.call_fn_raw(get_fn_name, vec![this_ptr])
             }
             Expr::Index(ref id, ref idx_raw) => {
                 let idx = self.eval_expr(scope, idx_raw)?;
-
                 let get_fn_name = "get$".to_string() + id;
 
-                if let Ok(mut val) = self.call_fn(&get_fn_name,
-                                                  Some(this_ptr),
-                                                  None,
-                                                  None,
-                                                  None,
-                                                  None,
-                                                  None) {
-                    if let Ok(i) = idx.downcast::<i64>() {
-                        if let Some(arr_typed) =
-                               (*val).downcast_mut() as Option<&mut Vec<Box<Any>>> {
-                            return self.call_fn("clone",
-                                                Some(&mut arr_typed[*i as usize]),
-                                                None,
-                                                None,
-                                                None,
-                                                None,
-                                                None);
-                        } else {
-                            return Err(EvalAltResult::ErrorIndexMismatch);
-                        }
-                    } else {
-                        return Err(EvalAltResult::ErrorIndexMismatch);
-                    }
-                } else {
-                    return Err(EvalAltResult::ErrorIndexMismatch);
-                }
+                let mut val = self.call_fn_raw(get_fn_name, vec![this_ptr])?;
+
+                ((*val).downcast_mut() as Option<&mut Vec<Box<Any>>>)
+                    .and_then(|arr| idx.downcast_ref::<i64>().map(|idx| (arr, *idx as usize)))
+                    .map(|(arr, idx)| arr[idx].clone())
+                    .ok_or(EvalAltResult::ErrorIndexMismatch)
             }
-            Expr::Dot(ref inner_lhs, ref inner_rhs) => {
-                match **inner_lhs {
-                    Expr::Identifier(ref id) => {
-                        let get_fn_name = "get$".to_string() + id;
-                        let result = self.call_fn(&get_fn_name,
-                                                  Some(this_ptr),
-                                                  None,
-                                                  None,
-                                                  None,
-                                                  None,
-                                                  None);
-
-                        match result {
-                            Ok(mut v) => self.get_dot_val_helper(scope, &mut v, inner_rhs),
-                            e => e,
-                        }
-                    }
-                    _ => Err(EvalAltResult::InternalErrorMalformedDotExpression),
+            Expr::Dot(ref inner_lhs, ref inner_rhs) => match **inner_lhs {
+                Expr::Identifier(ref id) => {
+                    let get_fn_name = "get$".to_string() + id;
+                    self.call_fn_raw(get_fn_name, vec![this_ptr])
+                        .and_then(|mut v| self.get_dot_val_helper(scope, v.as_mut(), inner_rhs))
                 }
-            }
+                _ => Err(EvalAltResult::InternalErrorMalformedDotExpression),
+            },
             _ => Err(EvalAltResult::InternalErrorMalformedDotExpression),
         }
     }
 
-    fn get_dot_val(&self,
-                   scope: &mut Scope,
-                   dot_lhs: &Expr,
-                   dot_rhs: &Expr)
-                   -> Result<Box<Any>, EvalAltResult> {
-        match *dot_lhs {
-            Expr::Identifier(ref id) => {
-                let mut target: Option<Box<Any>> = None;
-
-                for &mut (ref name, ref mut val) in &mut scope.iter_mut().rev() {
-                    if *id == *name {
-                        let result = self.call_fn("clone", Some(val), None, None, None, None, None);
+    fn search_scope<'a, F, T>(
+        scope: &'a mut Scope,
+        id: &str,
+        map: F,
+    ) -> Result<(usize, T), EvalAltResult>
+    where
+        F: FnOnce(&'a mut Any) -> Result<T, EvalAltResult>,
+    {
+        scope
+            .iter_mut()
+            .enumerate()
+            .rev()
+            .find(|&(_, &mut (ref name, _))| *id == *name)
+            .ok_or_else(|| EvalAltResult::ErrorVariableNotFound(id.to_owned()))
+            .and_then(move |(idx, &mut (_, ref mut val))| map(val.as_mut()).map(|val| (idx, val)))
+    }
 
-                        if let Ok(clone) = result {
-                            target = Some(clone);
-                            break;
-                        } else {
-                            return result;
-                        }
-                    }
-                }
+    fn array_value(
+        &self,
+        scope: &mut Scope,
+        id: &str,
+        idx: &Expr,
+    ) -> Result<(usize, usize, Box<Any>), EvalAltResult> {
+        let idx_boxed = self.eval_expr(scope, idx)?
+            .downcast::<i64>()
+            .map_err(|_| EvalAltResult::ErrorIndexMismatch)?;
+        let idx = *idx_boxed as usize;
+        let (idx_sc, val) = Self::search_scope(scope, id, |val| {
+            ((*val).downcast_mut() as Option<&mut Vec<Box<Any>>>)
+                .map(|arr| arr[idx].clone())
+                .ok_or(EvalAltResult::ErrorIndexMismatch)
+        })?;
+
+        Ok((idx_sc, idx, val))
+    }
 
-                if let Some(mut t) = target {
-                    let result = self.get_dot_val_helper(scope, &mut t, dot_rhs);
+    fn get_dot_val(
+        &self,
+        scope: &mut Scope,
+        dot_lhs: &Expr,
+        dot_rhs: &Expr,
+    ) -> Result<Box<Any>, EvalAltResult> {
+        match *dot_lhs {
+            Expr::Identifier(ref id) => {
+                let (sc_idx, mut target) = Self::search_scope(scope, id, |x| Ok(x.box_clone()))?;
+                let value = self.get_dot_val_helper(scope, target.as_mut(), dot_rhs);
 
-                    for &mut (ref name, ref mut val) in &mut scope.iter_mut().rev() {
-                        if *id == *name {
-                            *val = t;
-                            break;
-                        }
-                    }
-                    return result;
-                }
+                // In case the expression mutated `target`, we need to reassign it because
+                // of the above `clone`.
+                scope[sc_idx].1 = target;
 
-                Err(EvalAltResult::ErrorVariableNotFound(id.clone()))
+                value
             }
             Expr::Index(ref id, ref idx_raw) => {
-                let idx_boxed = self.eval_expr(scope, idx_raw)?;
-                let idx = if let Ok(i) = idx_boxed.downcast::<i64>() {
-                    i
-                } else {
-                    return Err(EvalAltResult::ErrorIndexMismatch);
-                };
-
-                let mut target: Option<Box<Any>> = None;
-
-                for &mut (ref name, ref mut val) in &mut scope.iter_mut().rev() {
-                    if *id == *name {
-                        if let Some(arr_typed) =
-                               (*val).downcast_mut() as Option<&mut Vec<Box<Any>>> {
-                            let result = self.call_fn("clone",
-                                                      Some(&mut arr_typed[*idx as usize]),
-                                                      None,
-                                                      None,
-                                                      None,
-                                                      None,
-                                                      None);
-
-                            if let Ok(clone) = result {
-                                target = Some(clone);
-                                break;
-                            } else {
-                                return result;
-                            }
-                        } else {
-                            return Err(EvalAltResult::ErrorIndexMismatch);
-                        }
-                    }
-                }
+                let (sc_idx, idx, mut target) = self.array_value(scope, id, idx_raw)?;
+                let value = self.get_dot_val_helper(scope, target.as_mut(), dot_rhs);
 
-                if let Some(mut t) = target {
-                    let result = self.get_dot_val_helper(scope, &mut t, dot_rhs);
-                    for &mut (ref name, ref mut val) in &mut scope.iter_mut().rev() {
-                        if *id == *name {
-                            if let Some(arr_typed) =
-                                   (*val).downcast_mut() as Option<&mut Vec<Box<Any>>> {
-                                arr_typed[*idx as usize] = t;
-                                break;
-                            }
-                        }
-                    }
-                    return result;
-                }
+                // In case the expression mutated `target`, we need to reassign it because
+                // of the above `clone`.
+                scope[sc_idx].1.downcast_mut::<Vec<Box<Any>>>().unwrap()[idx] = target;
 
-                Err(EvalAltResult::ErrorVariableNotFound(id.clone()))
+                value
             }
             _ => Err(EvalAltResult::InternalErrorMalformedDotExpression),
         }
     }
 
-    fn set_dot_val_helper(&self,
-                          this_ptr: &mut Box<Any>,
-                          dot_rhs: &Expr,
-                          mut source_val: Box<Any>)
-                          -> Result<Box<Any>, EvalAltResult> {
+    fn set_dot_val_helper(
+        &self,
+        this_ptr: &mut Any,
+        dot_rhs: &Expr,
+        mut source_val: Box<Any>,
+    ) -> Result<Box<Any>, EvalAltResult> {
         match *dot_rhs {
             Expr::Identifier(ref id) => {
                 let set_fn_name = "set$".to_string() + id;
-                self.call_fn(&set_fn_name,
-                             Some(this_ptr),
-                             Some(&mut source_val),
-                             None,
-                             None,
-                             None,
-                             None)
+                self.call_fn_raw(set_fn_name, vec![this_ptr, source_val.as_mut()])
             }
-            Expr::Dot(ref inner_lhs, ref inner_rhs) => {
-                match **inner_lhs {
-                    Expr::Identifier(ref id) => {
-                        let get_fn_name = "get$".to_string() + id;
-                        let result = self.call_fn(&get_fn_name,
-                                                  Some(this_ptr),
-                                                  None,
-                                                  None,
-                                                  None,
-                                                  None,
-                                                  None);
-
-                        match result {
-                            Ok(mut v) => {
-                                match self.set_dot_val_helper(&mut v, inner_rhs, source_val) {
-                                    Ok(_) => {
-                                        let set_fn_name = "set$".to_string() + id;
-
-                                        self.call_fn(&set_fn_name,
-                                                     Some(this_ptr),
-                                                     Some(&mut v),
-                                                     None,
-                                                     None,
-                                                     None,
-                                                     None)
-                                    }
-                                    e => e,
-                                }
-                            }
-                            e => e,
-                        }
-
-                    }
-                    _ => Err(EvalAltResult::InternalErrorMalformedDotExpression),
+            Expr::Dot(ref inner_lhs, ref inner_rhs) => match **inner_lhs {
+                Expr::Identifier(ref id) => {
+                    let get_fn_name = "get$".to_string() + id;
+                    self.call_fn_raw(get_fn_name, vec![this_ptr])
+                        .and_then(|mut v| {
+                            self.set_dot_val_helper(v.as_mut(), inner_rhs, source_val)
+                                .map(|_| v) // Discard Ok return value
+                        })
+                        .and_then(|mut v| {
+                            let set_fn_name = "set$".to_string() + id;
+
+                            self.call_fn_raw(set_fn_name, vec![this_ptr, v.as_mut()])
+                        })
                 }
-            }
+                _ => Err(EvalAltResult::InternalErrorMalformedDotExpression),
+            },
             _ => Err(EvalAltResult::InternalErrorMalformedDotExpression),
         }
     }
 
-    fn set_dot_val(&self,
-                   scope: &mut Scope,
-                   dot_lhs: &Expr,
-                   dot_rhs: &Expr,
-                   source_val: Box<Any>)
-                   -> Result<Box<Any>, EvalAltResult> {
+    fn set_dot_val(
+        &self,
+        scope: &mut Scope,
+        dot_lhs: &Expr,
+        dot_rhs: &Expr,
+        source_val: Box<Any>,
+    ) -> Result<Box<Any>, EvalAltResult> {
         match *dot_lhs {
             Expr::Identifier(ref id) => {
-                let mut target: Option<Box<Any>> = None;
+                let (sc_idx, mut target) = Self::search_scope(scope, id, |x| Ok(x.box_clone()))?;
+                let value = self.set_dot_val_helper(target.as_mut(), dot_rhs, source_val);
 
-                for &mut (ref name, ref mut val) in &mut scope.iter_mut().rev() {
-                    if *id == *name {
-                        if let Ok(clone) = self.call_fn("clone",
-                                                        Some(val),
-                                                        None,
-                                                        None,
-                                                        None,
-                                                        None,
-                                                        None) {
-                            target = Some(clone);
-                            break;
-                        } else {
-                            return Err(EvalAltResult::ErrorVariableNotFound(id.clone()));
-                        }
-                    }
-                }
+                // In case the expression mutated `target`, we need to reassign it because
+                // of the above `clone`.
+                scope[sc_idx].1 = target;
 
-                if let Some(mut t) = target {
-                    let result = self.set_dot_val_helper(&mut t, dot_rhs, source_val);
-
-                    for &mut (ref name, ref mut val) in &mut scope.iter_mut().rev() {
-                        if *id == *name {
-                            *val = t;
-                            break;
-                        }
-                    }
-                    return result;
-                }
-
-                Err(EvalAltResult::ErrorAssignmentToUnknownLHS)
+                value
             }
             Expr::Index(ref id, ref idx_raw) => {
-                let idx_boxed = self.eval_expr(scope, idx_raw)?;
-                let idx = if let Ok(i) = idx_boxed.downcast::<i64>() {
-                    i
-                } else {
-                    return Err(EvalAltResult::ErrorIndexMismatch);
-                };
-
-                let mut target: Option<Box<Any>> = None;
+                let (sc_idx, idx, mut target) = self.array_value(scope, id, idx_raw)?;
+                let value = self.set_dot_val_helper(target.as_mut(), dot_rhs, source_val);
 
-                for &mut (ref name, ref mut val) in &mut scope.iter_mut().rev() {
-                    if *id == *name {
-                        if let Some(arr_typed) =
-                               (*val).downcast_mut() as Option<&mut Vec<Box<Any>>> {
-                            let result = self.call_fn("clone",
-                                                      Some(&mut arr_typed[*idx as usize]),
-                                                      None,
-                                                      None,
-                                                      None,
-                                                      None,
-                                                      None);
-
-                            if let Ok(clone) = result {
-                                target = Some(clone);
-                                break;
-                            } else {
-                                return result;
-                            }
-                        } else {
-                            return Err(EvalAltResult::ErrorIndexMismatch);
-                        }
-                    }
-                }
+                // In case the expression mutated `target`, we need to reassign it because
+                // of the above `clone`.
+                scope[sc_idx].1.downcast_mut::<Vec<Box<Any>>>().unwrap()[idx] = target;
 
-                if let Some(mut t) = target {
-                    let result = self.set_dot_val_helper(&mut t, dot_rhs, source_val);
-                    for &mut (ref name, ref mut val) in &mut scope.iter_mut().rev() {
-                        if *id == *name {
-                            if let Some(arr_typed) =
-                                   (*val).downcast_mut() as Option<&mut Vec<Box<Any>>> {
-                                arr_typed[*idx as usize] = t;
-                                break;
-                            }
-                        }
-                    }
-                    return result;
-                }
-
-                Err(EvalAltResult::ErrorVariableNotFound(id.clone()))
+                value
             }
             _ => Err(EvalAltResult::InternalErrorMalformedDotExpression),
         }
@@ -955,36 +425,13 @@ impl Engine {
             Expr::Identifier(ref id) => {
                 for &mut (ref name, ref mut val) in &mut scope.iter_mut().rev() {
                     if *id == *name {
-                        return self.call_fn("clone", Some(val), None, None, None, None, None);
+                        return Ok(val.clone());
                     }
                 }
                 Err(EvalAltResult::ErrorVariableNotFound(id.clone()))
             }
             Expr::Index(ref id, ref idx_raw) => {
-                let idx = self.eval_expr(scope, idx_raw)?;
-
-                for &mut (ref name, ref mut val) in &mut scope.iter_mut().rev() {
-                    if *id == *name {
-                        if let Ok(i) = idx.downcast::<i64>() {
-                            if let Some(arr_typed) =
-                                   (*val).downcast_mut() as Option<&mut Vec<Box<Any>>> {
-                                return self.call_fn("clone",
-                                                    Some(&mut arr_typed[*i as usize]),
-                                                    None,
-                                                    None,
-                                                    None,
-                                                    None,
-                                                    None);
-                            } else {
-                                return Err(EvalAltResult::ErrorIndexMismatch);
-                            }
-                        } else {
-                            return Err(EvalAltResult::ErrorIndexMismatch);
-                        }
-                    }
-                }
-
-                Err(EvalAltResult::ErrorVariableNotFound(id.clone()))
+                self.array_value(scope, id, idx_raw).map(|(_, _, x)| x)
             }
             Expr::Assignment(ref id, ref rhs) => {
                 let rhs_val = self.eval_expr(scope, rhs)?;
@@ -993,7 +440,6 @@ impl Engine {
                     Expr::Identifier(ref n) => {
                         for &mut (ref name, ref mut val) in &mut scope.iter_mut().rev() {
                             if *n == *name {
-
                                 *val = rhs_val;
 
                                 return Ok(Box::new(()));
@@ -1006,9 +452,10 @@ impl Engine {
 
                         for &mut (ref name, ref mut val) in &mut scope.iter_mut().rev() {
                             if *id == *name {
-                                if let Ok(i) = idx.downcast::<i64>() {
+                                if let Some(i) = idx.downcast_ref::<i64>() {
                                     if let Some(arr_typed) =
-                                           (*val).downcast_mut() as Option<&mut Vec<Box<Any>>> {
+                                        (*val).downcast_mut() as Option<&mut Vec<Box<Any>>>
+                                    {
                                         arr_typed[*i as usize] = rhs_val;
                                         return Ok(Box::new(()));
                                     } else {
@@ -1039,82 +486,15 @@ impl Engine {
 
                 Ok(Box::new(arr))
             }
-            Expr::FnCall(ref fn_name, ref args) => {
-                if args.is_empty() {
-                    self.call_fn(fn_name, None, None, None, None, None, None)
-                } else if args.len() == 1 {
-                    let mut arg = self.eval_expr(scope, &args[0])?;
-
-                    self.call_fn(fn_name, Some(&mut arg), None, None, None, None, None)
-                } else if args.len() == 2 {
-                    let mut arg1 = self.eval_expr(scope, &args[0])?;
-                    let mut arg2 = self.eval_expr(scope, &args[1])?;
-
-                    self.call_fn(fn_name,
-                                 Some(&mut arg1),
-                                 Some(&mut arg2),
-                                 None,
-                                 None,
-                                 None,
-                                 None)
-                } else if args.len() == 3 {
-                    let mut arg1 = self.eval_expr(scope, &args[0])?;
-                    let mut arg2 = self.eval_expr(scope, &args[1])?;
-                    let mut arg3 = self.eval_expr(scope, &args[2])?;
-
-                    self.call_fn(fn_name,
-                                 Some(&mut arg1),
-                                 Some(&mut arg2),
-                                 Some(&mut arg3),
-                                 None,
-                                 None,
-                                 None)
-                } else if args.len() == 4 {
-                    let mut arg1 = self.eval_expr(scope, &args[0])?;
-                    let mut arg2 = self.eval_expr(scope, &args[1])?;
-                    let mut arg3 = self.eval_expr(scope, &args[2])?;
-                    let mut arg4 = self.eval_expr(scope, &args[3])?;
-
-                    self.call_fn(fn_name,
-                                 Some(&mut arg1),
-                                 Some(&mut arg2),
-                                 Some(&mut arg3),
-                                 Some(&mut arg4),
-                                 None,
-                                 None)
-                } else if args.len() == 5 {
-                    let mut arg1 = self.eval_expr(scope, &args[0])?;
-                    let mut arg2 = self.eval_expr(scope, &args[1])?;
-                    let mut arg3 = self.eval_expr(scope, &args[2])?;
-                    let mut arg4 = self.eval_expr(scope, &args[3])?;
-                    let mut arg5 = self.eval_expr(scope, &args[4])?;
-
-                    self.call_fn(fn_name,
-                                 Some(&mut arg1),
-                                 Some(&mut arg2),
-                                 Some(&mut arg3),
-                                 Some(&mut arg4),
-                                 Some(&mut arg5),
-                                 None)
-                } else if args.len() == 6 {
-                    let mut arg1 = self.eval_expr(scope, &args[0])?;
-                    let mut arg2 = self.eval_expr(scope, &args[1])?;
-                    let mut arg3 = self.eval_expr(scope, &args[2])?;
-                    let mut arg4 = self.eval_expr(scope, &args[3])?;
-                    let mut arg5 = self.eval_expr(scope, &args[4])?;
-                    let mut arg6 = self.eval_expr(scope, &args[5])?;
-
-                    self.call_fn(fn_name,
-                                 Some(&mut arg1),
-                                 Some(&mut arg2),
-                                 Some(&mut arg3),
-                                 Some(&mut arg4),
-                                 Some(&mut arg5),
-                                 Some(&mut arg6))
-                } else {
-                    Err(EvalAltResult::ErrorFunctionCallNotSupported)
-                }
-            }
+            Expr::FnCall(ref fn_name, ref args) => self.call_fn_raw(
+                fn_name.to_owned(),
+                args.iter()
+                    .map(|ex| self.eval_expr(scope, ex))
+                    .collect::<Result<Vec<Box<Any>>, _>>()?
+                    .iter_mut()
+                    .map(|b| b.as_mut())
+                    .collect(),
+            ),
             Expr::True => Ok(Box::new(true)),
             Expr::False => Ok(Box::new(false)),
         }
@@ -1167,42 +547,38 @@ impl Engine {
                     Err(_) => Err(EvalAltResult::ErrorIfGuardMismatch),
                 }
             }
-            Stmt::While(ref guard, ref body) => {
-                loop {
-                    let guard_result = self.eval_expr(scope, guard)?;
-                    match guard_result.downcast::<bool>() {
-                        Ok(g) => {
-                            if *g {
-                                match self.eval_stmt(scope, body) {
-                                    Err(EvalAltResult::LoopBreak) => {
-                                        return Ok(Box::new(()));
-                                    }
-                                    Err(x) => {
-                                        return Err(x);
-                                    }
-                                    _ => (),
+            Stmt::While(ref guard, ref body) => loop {
+                let guard_result = self.eval_expr(scope, guard)?;
+                match guard_result.downcast::<bool>() {
+                    Ok(g) => {
+                        if *g {
+                            match self.eval_stmt(scope, body) {
+                                Err(EvalAltResult::LoopBreak) => {
+                                    return Ok(Box::new(()));
                                 }
-                            } else {
-                                return Ok(Box::new(()));
+                                Err(x) => {
+                                    return Err(x);
+                                }
+                                _ => (),
                             }
+                        } else {
+                            return Ok(Box::new(()));
                         }
-                        Err(_) => return Err(EvalAltResult::ErrorIfGuardMismatch),
                     }
+                    Err(_) => return Err(EvalAltResult::ErrorIfGuardMismatch),
                 }
-            }
-            Stmt::Loop(ref body) => {
-                loop {
-                    match self.eval_stmt(scope, body) {
-                        Err(EvalAltResult::LoopBreak) => {
-                            return Ok(Box::new(()));
-                        }
-                        Err(x) => {
-                            return Err(x);
-                        }
-                        _ => (),
+            },
+            Stmt::Loop(ref body) => loop {
+                match self.eval_stmt(scope, body) {
+                    Err(EvalAltResult::LoopBreak) => {
+                        return Ok(Box::new(()));
+                    }
+                    Err(x) => {
+                        return Err(x);
                     }
+                    _ => (),
                 }
-            }
+            },
             Stmt::Break => Err(EvalAltResult::LoopBreak),
             Stmt::Return => Err(EvalAltResult::Return(Box::new(()))),
             Stmt::ReturnWithVal(ref a) => {
@@ -1250,10 +626,11 @@ impl Engine {
     }
 
     /// Evaluate with own scope
-    pub fn eval_with_scope<T: Any + Clone>(&mut self,
-                                           scope: &mut Scope,
-                                           input: &str)
-                                           -> Result<T, EvalAltResult> {
+    pub fn eval_with_scope<T: Any + Clone>(
+        &mut self,
+        scope: &mut Scope,
+        input: &str,
+    ) -> Result<T, EvalAltResult> {
         let tokens = lex(input);
 
         let mut peekables = tokens.peekable();
@@ -1264,13 +641,15 @@ impl Engine {
                 let mut x: Result<Box<Any>, EvalAltResult> = Ok(Box::new(()));
 
                 for f in fns {
-                    if f.params.len() > 6 {
-                        return Err(EvalAltResult::ErrorFunctionArityNotSupported);
-                    }
                     let name = f.name.clone();
                     let local_f = f.clone();
-                    let ent = self.fns.entry(name).or_insert_with(Vec::new);
-                    (*ent).push(FnType::InternalFn(local_f));
+
+                    let spec = FnSpec {
+                        ident: name,
+                        args: None,
+                    };
+
+                    self.fns.insert(spec, FnIntExt::Int(local_f));
                 }
 
                 for o in os {
@@ -1280,14 +659,11 @@ impl Engine {
                     }
                 }
 
-                match x {
-                    Ok(v) => {
-                        match v.downcast::<T>() {
-                            Ok(out) => Ok(*out),
-                            Err(_) => Err(EvalAltResult::ErrorMismatchOutputType),
-                        }
-                    }
-                    Err(e) => Err(e),
+                let x = x?;
+
+                match x.downcast::<T>() {
+                    Ok(out) => Ok(*out),
+                    Err(_) => Err(EvalAltResult::ErrorMismatchOutputType),
                 }
             }
             Err(_) => Err(EvalAltResult::ErrorFunctionArgMismatch),
@@ -1307,7 +683,9 @@ impl Engine {
             if f.read_to_string(&mut contents).is_ok() {
                 if let e @ Err(_) = self.consume(&contents) {
                     return e;
-                } else { return Ok(()); }
+                } else {
+                    return Ok(());
+                }
             } else {
                 Err(EvalAltResult::ErrorCantOpenScriptFile)
             }
@@ -1330,7 +708,11 @@ impl Engine {
     /// Evaluate a string with own scoppe, but only return errors, if there are any.
     /// Useful for when you don't need the result, but still need
     /// to keep track of possible errors
-    pub fn consume_with_scope(&mut self, scope: &mut Scope, input: &str) -> Result<(), EvalAltResult> {
+    pub fn consume_with_scope(
+        &mut self,
+        scope: &mut Scope,
+        input: &str,
+    ) -> Result<(), EvalAltResult> {
         let tokens = lex(input);
 
         let mut peekables = tokens.peekable();
@@ -1344,8 +726,13 @@ impl Engine {
                     }
                     let name = f.name.clone();
                     let local_f = f.clone();
-                    let ent = self.fns.entry(name).or_insert_with(Vec::new);
-                    (*ent).push(FnType::InternalFn(local_f));
+
+                    let spec = FnSpec {
+                        ident: name,
+                        args: None,
+                    };
+
+                    self.fns.insert(spec, FnIntExt::Int(local_f));
                 }
 
                 for o in os {
@@ -1355,7 +742,7 @@ impl Engine {
                 }
 
                 Ok(())
-            },
+            }
             Err(_) => Err(EvalAltResult::ErrorFunctionArgMismatch),
         }
     }
@@ -1397,30 +784,78 @@ impl Engine {
             )
         }
 
-        fn add<T: Add>(x: T, y: T) -> <T as Add>::Output { x + y }
-        fn sub<T: Sub>(x: T, y: T) -> <T as Sub>::Output { x - y }
-        fn mul<T: Mul>(x: T, y: T) -> <T as Mul>::Output { x * y }
-        fn div<T: Div>(x: T, y: T) -> <T as Div>::Output { x / y }
-        fn neg<T: Neg>(x: T) -> <T as Neg>::Output { -x }
-        fn lt<T: PartialOrd>(x: T, y: T)  -> bool { x < y  }
-        fn lte<T: PartialOrd>(x: T, y: T) -> bool { x <= y }
-        fn gt<T: PartialOrd>(x: T, y: T)  -> bool { x > y  }
-        fn gte<T: PartialOrd>(x: T, y: T) -> bool { x >= y }
-        fn eq<T: PartialEq>(x: T, y: T)   -> bool { x == y }
-        fn ne<T: PartialEq>(x: T, y: T)   -> bool { x != y }
-        fn and(x: bool, y: bool)   -> bool { x && y }
-        fn or(x: bool, y: bool)    -> bool { x || y }
-        fn not(x: bool)            -> bool { !x }
-        fn concat(x: String, y: String) -> String { x + &y }
-        fn binary_and<T: BitAnd>(x: T, y: T)   -> <T as BitAnd>::Output { x & y }
-        fn binary_or<T: BitOr>(x: T, y: T)   -> <T as BitOr>::Output { x | y }
-        fn binary_xor<T: BitXor>(x: T, y: T)   -> <T as BitXor>::Output { x ^ y }
-        fn left_shift<T: Shl<T>>(x: T, y: T)   -> <T as Shl<T>>::Output { x.shl(y) }
-        fn right_shift<T: Shr<T>>(x: T, y: T)   -> <T as Shr<T>>::Output { x.shr(y) }
-        fn modulo<T: Rem<T>>(x: T, y: T)   -> <T as Rem<T>>::Output { x % y}
-        fn pow_i64_i64(x: i64, y: i64) -> i64 { x.pow(y as u32) }
-        fn pow_f64_f64(x: f64, y: f64) -> f64 { x.powf(y) }
-        fn pow_f64_i64(x: f64, y: i64) -> f64 { x.powi(y as i32) }
+        fn add<T: Add>(x: T, y: T) -> <T as Add>::Output {
+            x + y
+        }
+        fn sub<T: Sub>(x: T, y: T) -> <T as Sub>::Output {
+            x - y
+        }
+        fn mul<T: Mul>(x: T, y: T) -> <T as Mul>::Output {
+            x * y
+        }
+        fn div<T: Div>(x: T, y: T) -> <T as Div>::Output {
+            x / y
+        }
+        fn neg<T: Neg>(x: T) -> <T as Neg>::Output {
+            -x
+        }
+        fn lt<T: PartialOrd>(x: T, y: T) -> bool {
+            x < y
+        }
+        fn lte<T: PartialOrd>(x: T, y: T) -> bool {
+            x <= y
+        }
+        fn gt<T: PartialOrd>(x: T, y: T) -> bool {
+            x > y
+        }
+        fn gte<T: PartialOrd>(x: T, y: T) -> bool {
+            x >= y
+        }
+        fn eq<T: PartialEq>(x: T, y: T) -> bool {
+            x == y
+        }
+        fn ne<T: PartialEq>(x: T, y: T) -> bool {
+            x != y
+        }
+        fn and(x: bool, y: bool) -> bool {
+            x && y
+        }
+        fn or(x: bool, y: bool) -> bool {
+            x || y
+        }
+        fn not(x: bool) -> bool {
+            !x
+        }
+        fn concat(x: String, y: String) -> String {
+            x + &y
+        }
+        fn binary_and<T: BitAnd>(x: T, y: T) -> <T as BitAnd>::Output {
+            x & y
+        }
+        fn binary_or<T: BitOr>(x: T, y: T) -> <T as BitOr>::Output {
+            x | y
+        }
+        fn binary_xor<T: BitXor>(x: T, y: T) -> <T as BitXor>::Output {
+            x ^ y
+        }
+        fn left_shift<T: Shl<T>>(x: T, y: T) -> <T as Shl<T>>::Output {
+            x.shl(y)
+        }
+        fn right_shift<T: Shr<T>>(x: T, y: T) -> <T as Shr<T>>::Output {
+            x.shr(y)
+        }
+        fn modulo<T: Rem<T>>(x: T, y: T) -> <T as Rem<T>>::Output {
+            x % y
+        }
+        fn pow_i64_i64(x: i64, y: i64) -> i64 {
+            x.pow(y as u32)
+        }
+        fn pow_f64_f64(x: f64, y: f64) -> f64 {
+            x.powf(y)
+        }
+        fn pow_f64_i64(x: f64, y: i64) -> f64 {
+            x.powi(y as i32)
+        }
 
         reg_op!(engine, "+", add, i32, i64, u32, u64, f32, f64);
         reg_op!(engine, "-", sub, i32, i64, u32, u64, f32, f64);
@@ -1461,7 +896,9 @@ impl Engine {
 
     /// Make a new engine
     pub fn new() -> Engine {
-        let mut engine = Engine { fns: HashMap::new() };
+        let mut engine = Engine {
+            fns: HashMap::new(),
+        };
 
         Engine::register_default_lib(&mut engine);
 
diff --git a/src/fn_register.rs b/src/fn_register.rs
index d6d062f87..ad4fbe031 100644
--- a/src/fn_register.rs
+++ b/src/fn_register.rs
@@ -1,421 +1,68 @@
-use std::any::Any;
-use std::boxed::Box;
+use std::any::TypeId;
 
-use engine::{EvalAltResult, Engine, FnType};
+use any::Any;
+use engine::{Engine, EvalAltResult};
 
-/// A trait used for registering functions to an Engine
-/// Currently, Rhai supports functions with up to 6 parameters
-pub trait FnRegister<A, RetVal, Args> {
-	/// A method used for registering functions and methods to a Engine
-    fn register_fn(&mut self, name: &str, f: A);
+pub trait RegisterFn<FN, ARGS, RET> {
+    fn register_fn(&mut self, name: &str, f: FN);
 }
 
-impl<'a, A, T, U, V, W, X, Y, Z> FnRegister<A, Z, (&'a mut T, U, V, W, X, Y)> for Engine
-    where A: 'static + Fn(&mut T, U, V, W, X, Y) -> Z,
-          T: Any,
-          U: Clone + Any,
-          V: Clone + Any,
-          W: Clone + Any,
-          X: Clone + Any,
-          Y: Clone + Any,
-          Z: Any
-{
-    fn register_fn(&mut self, name: &str, fun: A) {
-        let wrapped: Box<Fn(&mut Box<Any>,
-                            &mut Box<Any>,
-                            &mut Box<Any>,
-                            &mut Box<Any>,
-                            &mut Box<Any>,
-                            &mut Box<Any>)
-                            -> Result<Box<Any>, EvalAltResult>> =
-            Box::new(move |arg1: &mut Box<Any>,
-                           arg2: &mut Box<Any>,
-                           arg3: &mut Box<Any>,
-                           arg4: &mut Box<Any>,
-                           arg5: &mut Box<Any>,
-                           arg6: &mut Box<Any>| {
+pub struct Ref<A>(A);
+pub struct Mut<A>(A);
 
-                let inside1 = (*arg1).downcast_mut() as Option<&mut T>;
-                let inside2 = (*arg2).downcast_mut() as Option<&mut U>;
-                let inside3 = (*arg3).downcast_mut() as Option<&mut V>;
-                let inside4 = (*arg4).downcast_mut() as Option<&mut W>;
-                let inside5 = (*arg5).downcast_mut() as Option<&mut X>;
-                let inside6 = (*arg6).downcast_mut() as Option<&mut Y>;
-
-                match (inside1, inside2, inside3, inside4, inside5, inside6) {
-                    (Some(b), Some(c), Some(d), Some(e), Some(f), Some(g)) => {
-                        Ok(Box::new(fun(b,
-                                        c.clone(),
-                                        d.clone(),
-                                        e.clone(),
-                                        f.clone(),
-                                        g.clone())) as Box<Any>)
-                    }
-                    _ => Err(EvalAltResult::ErrorFunctionArgMismatch),
-                }
-            });
-
-        let ent = self.fns.entry(name.to_string()).or_insert_with(Vec::new);
-        (*ent).push(FnType::ExternalFn6(wrapped));
-    }
-}
-
-impl<'a, A, T, U, V, W, X, Y, Z> FnRegister<A, Z, (&'a T, U, V, W, X, Y)> for Engine
-    where A: 'static + Fn(T, U, V, W, X, Y) -> Z,
-          T: Clone + Any,
-          U: Clone + Any,
-          V: Clone + Any,
-          W: Clone + Any,
-          X: Clone + Any,
-          Y: Clone + Any,
-          Z: Any
-{
-    fn register_fn(&mut self, name: &str, fun: A) {
-        let wrapped: Box<Fn(&mut Box<Any>,
-                            &mut Box<Any>,
-                            &mut Box<Any>,
-                            &mut Box<Any>,
-                            &mut Box<Any>,
-                            &mut Box<Any>)
-                            -> Result<Box<Any>, EvalAltResult>> =
-            Box::new(move |arg1: &mut Box<Any>,
-                           arg2: &mut Box<Any>,
-                           arg3: &mut Box<Any>,
-                           arg4: &mut Box<Any>,
-                           arg5: &mut Box<Any>,
-                           arg6: &mut Box<Any>| {
-
-                let inside1 = (*arg1).downcast_mut() as Option<&mut T>;
-                let inside2 = (*arg2).downcast_mut() as Option<&mut U>;
-                let inside3 = (*arg3).downcast_mut() as Option<&mut V>;
-                let inside4 = (*arg4).downcast_mut() as Option<&mut W>;
-                let inside5 = (*arg5).downcast_mut() as Option<&mut X>;
-                let inside6 = (*arg6).downcast_mut() as Option<&mut Y>;
-
-                match (inside1, inside2, inside3, inside4, inside5, inside6) {
-                    (Some(b), Some(c), Some(d), Some(e), Some(f), Some(g)) => {
-                        Ok(Box::new(fun(b.clone(),
-                                        c.clone(),
-                                        d.clone(),
-                                        e.clone(),
-                                        f.clone(),
-                                        g.clone())) as Box<Any>)
-                    }
-                    _ => Err(EvalAltResult::ErrorFunctionArgMismatch),
-                }
-            });
-
-        let ent = self.fns.entry(name.to_string()).or_insert_with(Vec::new);
-        (*ent).push(FnType::ExternalFn6(wrapped));
-    }
-}
-
-impl<'a, A, T, U, V, W, X, Y> FnRegister<A, Y, (&'a mut T, U, V, W, X)> for Engine
-    where A: 'static + Fn(&mut T, U, V, W, X) -> Y,
-          T: Any,
-          U: Clone + Any,
-          V: Clone + Any,
-          W: Clone + Any,
-          X: Clone + Any,
-          Y: Any
-{
-    fn register_fn(&mut self, name: &str, fun: A) {
-        let wrapped: Box<Fn(&mut Box<Any>,
-                            &mut Box<Any>,
-                            &mut Box<Any>,
-                            &mut Box<Any>,
-                            &mut Box<Any>)
-                            -> Result<Box<Any>, EvalAltResult>> =
-            Box::new(move |arg1: &mut Box<Any>,
-                           arg2: &mut Box<Any>,
-                           arg3: &mut Box<Any>,
-                           arg4: &mut Box<Any>,
-                           arg5: &mut Box<Any>| {
-
-                let inside1 = (*arg1).downcast_mut() as Option<&mut T>;
-                let inside2 = (*arg2).downcast_mut() as Option<&mut U>;
-                let inside3 = (*arg3).downcast_mut() as Option<&mut V>;
-                let inside4 = (*arg4).downcast_mut() as Option<&mut W>;
-                let inside5 = (*arg5).downcast_mut() as Option<&mut X>;
-
-                match (inside1, inside2, inside3, inside4, inside5) {
-                    (Some(b), Some(c), Some(d), Some(e), Some(f)) => {
-                        Ok(Box::new(fun(b, c.clone(), d.clone(), e.clone(), f.clone())) as Box<Any>)
-                    }
-                    _ => Err(EvalAltResult::ErrorFunctionArgMismatch),
-                }
-            });
-
-        let ent = self.fns.entry(name.to_string()).or_insert_with(Vec::new);
-        (*ent).push(FnType::ExternalFn5(wrapped));
-    }
-}
-
-impl<'a, A, T, U, V, W, X, Y> FnRegister<A, Y, (&'a T, U, V, W, X)> for Engine
-    where A: 'static + Fn(T, U, V, W, X) -> Y,
-          T: Clone + Any,
-          U: Clone + Any,
-          V: Clone + Any,
-          W: Clone + Any,
-          X: Clone + Any,
-          Y: Any
-{
-    fn register_fn(&mut self, name: &str, fun: A) {
-        let wrapped: Box<Fn(&mut Box<Any>,
-                            &mut Box<Any>,
-                            &mut Box<Any>,
-                            &mut Box<Any>,
-                            &mut Box<Any>)
-                            -> Result<Box<Any>, EvalAltResult>> =
-            Box::new(move |arg1: &mut Box<Any>,
-                           arg2: &mut Box<Any>,
-                           arg3: &mut Box<Any>,
-                           arg4: &mut Box<Any>,
-                           arg5: &mut Box<Any>| {
-
-                let inside1 = (*arg1).downcast_mut() as Option<&mut T>;
-                let inside2 = (*arg2).downcast_mut() as Option<&mut U>;
-                let inside3 = (*arg3).downcast_mut() as Option<&mut V>;
-                let inside4 = (*arg4).downcast_mut() as Option<&mut W>;
-                let inside5 = (*arg5).downcast_mut() as Option<&mut X>;
-
-                match (inside1, inside2, inside3, inside4, inside5) {
-                    (Some(b), Some(c), Some(d), Some(e), Some(f)) => {
-                        Ok(Box::new(fun(b.clone(),
-                                        c.clone(),
-                                        d.clone(),
-                                        e.clone(),
-                                        f.clone())) as Box<Any>)
-                    }
-                    _ => Err(EvalAltResult::ErrorFunctionArgMismatch),
-                }
-            });
-
-        let ent = self.fns.entry(name.to_string()).or_insert_with(Vec::new);
-        (*ent).push(FnType::ExternalFn5(wrapped));
-    }
+macro_rules! count_args {
+    () => {0usize};
+    ($head:ident $($tail:ident)*) => {1usize + count_args!($($tail)*)};
 }
 
-impl<'a, A, T, U, V, W, X> FnRegister<A, X, (&'a mut T, U, V, W)> for Engine
-    where A: 'static + Fn(&mut T, U, V, W) -> X,
-          T: Any,
-          U: Clone + Any,
-          V: Clone + Any,
-          W: Clone + Any,
-          X: Any
-{
-    fn register_fn(&mut self, name: &str, fun: A) {
-        let wrapped: Box<Fn(&mut Box<Any>, &mut Box<Any>, &mut Box<Any>, &mut Box<Any>)
-                            -> Result<Box<Any>, EvalAltResult>> =
-            Box::new(move |arg1: &mut Box<Any>,
-                           arg2: &mut Box<Any>,
-                           arg3: &mut Box<Any>,
-                           arg4: &mut Box<Any>| {
-                let inside1 = (*arg1).downcast_mut() as Option<&mut T>;
-                let inside2 = (*arg2).downcast_mut() as Option<&mut U>;
-                let inside3 = (*arg3).downcast_mut() as Option<&mut V>;
-                let inside4 = (*arg4).downcast_mut() as Option<&mut W>;
-
-                match (inside1, inside2, inside3, inside4) {
-                    (Some(b), Some(c), Some(d), Some(e)) => {
-                        Ok(Box::new(fun(b, c.clone(), d.clone(), e.clone())) as Box<Any>)
+macro_rules! def_register {
+    () => {
+        def_register!(imp);
+    };
+    (imp $($par:ident => $mark:ty => $param:ty => $clone:expr),*) => {
+        impl<$($par,)* FN, RET> RegisterFn<FN, ($($mark,)*), RET> for Engine
+        where
+            $($par: Any + Clone,)*
+            FN: Fn($($param),*) -> RET + 'static,
+            RET: Any,
+        {
+            fn register_fn(&mut self, name: &str, f: FN) {
+                let fun = move |mut args: Vec<&mut Any>| {
+                    // Check for length at the beginning to avoid
+                    // per-element bound checks.
+                    if args.len() != count_args!($($par)*) {
+                        return Err(EvalAltResult::ErrorFunctionArgMismatch);
                     }
-                    _ => Err(EvalAltResult::ErrorFunctionArgMismatch),
-                }
-            });
 
-        let ent = self.fns.entry(name.to_string()).or_insert_with(Vec::new);
-        (*ent).push(FnType::ExternalFn4(wrapped));
-    }
+                    let mut drain = args.drain(..);
+                    $(
+                    // Downcast every element, return in case of a type mismatch
+                    let $par = ((*drain.next().unwrap()).downcast_mut() as Option<&mut $par>)
+                        .ok_or(EvalAltResult::ErrorFunctionArgMismatch)?;
+                    )*
+
+                    // Call the user-supplied function using ($clone) to
+                    // potentially clone the value, otherwise pass the reference.
+                    Ok(Box::new(f($(($clone)($par)),*)) as Box<Any>)
+                };
+                self.register_fn_raw(name.to_owned(), Some(vec![$(TypeId::of::<$par>()),*]), Box::new(fun));
+            }
+        }
+
+        //def_register!(imp_pop $($par => $mark => $param),*);
+    };
+    ($p0:ident $(, $p:ident)*) => {
+        def_register!(imp $p0 => $p0 => $p0 => Clone::clone $(, $p => $p => $p => Clone::clone)*);
+        def_register!(imp $p0 => Ref<$p0> => &$p0 => |x| { x } $(, $p => $p => $p => Clone::clone)*);
+        def_register!(imp $p0 => Mut<$p0> => &mut $p0 => |x| { x } $(, $p => $p => $p => Clone::clone)*);
+
+        def_register!($($p),*);
+    };
+//    (imp_pop) => {};
+//    (imp_pop $head:ident => $head_mark:ty => $head_param:ty $(,$tail:ident => $tail_mark:ty => $tp:ty)*) => {
+//        def_register!(imp $($tail => $tail_mark => $tp),*);
+//    };
 }
 
-impl<'a, A, T, U, V, W, X> FnRegister<A, X, (&'a T, U, V, W)> for Engine
-    where A: 'static + Fn(T, U, V, W) -> X,
-          T: Clone + Any,
-          U: Clone + Any,
-          V: Clone + Any,
-          W: Clone + Any,
-          X: Any
-{
-    fn register_fn(&mut self, name: &str, fun: A) {
-        let wrapped: Box<Fn(&mut Box<Any>, &mut Box<Any>, &mut Box<Any>, &mut Box<Any>)
-                            -> Result<Box<Any>, EvalAltResult>> =
-            Box::new(move |arg1: &mut Box<Any>,
-                           arg2: &mut Box<Any>,
-                           arg3: &mut Box<Any>,
-                           arg4: &mut Box<Any>| {
-                let inside1 = (*arg1).downcast_mut() as Option<&mut T>;
-                let inside2 = (*arg2).downcast_mut() as Option<&mut U>;
-                let inside3 = (*arg3).downcast_mut() as Option<&mut V>;
-                let inside4 = (*arg4).downcast_mut() as Option<&mut W>;
-
-                match (inside1, inside2, inside3, inside4) {
-                    (Some(b), Some(c), Some(d), Some(e)) => {
-                        Ok(Box::new(fun(b.clone(), c.clone(), d.clone(), e.clone())) as Box<Any>)
-                    }
-                    _ => Err(EvalAltResult::ErrorFunctionArgMismatch),
-                }
-            });
-
-        let ent = self.fns.entry(name.to_string()).or_insert_with(Vec::new);
-        (*ent).push(FnType::ExternalFn4(wrapped));
-    }
-}
-
-impl<'a, A, T, U, V, W> FnRegister<A, W, (&'a mut T, U, V)> for Engine
-    where A: 'static + Fn(&mut T, U, V) -> W,
-          T: Any,
-          U: Clone + Any,
-          V: Clone + Any,
-          W: Any
-{
-    fn register_fn(&mut self, name: &str, fun: A) {
-        let wrapped: Box<Fn(&mut Box<Any>, &mut Box<Any>, &mut Box<Any>)
-                            -> Result<Box<Any>, EvalAltResult>> =
-            Box::new(move |arg1: &mut Box<Any>, arg2: &mut Box<Any>, arg3: &mut Box<Any>| {
-                let inside1 = (*arg1).downcast_mut() as Option<&mut T>;
-                let inside2 = (*arg2).downcast_mut() as Option<&mut U>;
-                let inside3 = (*arg3).downcast_mut() as Option<&mut V>;
-
-                match (inside1, inside2, inside3) {
-                    (Some(b), Some(c), Some(d)) => {
-                        Ok(Box::new(fun(b, c.clone(), d.clone())) as Box<Any>)
-                    }
-                    _ => Err(EvalAltResult::ErrorFunctionArgMismatch),
-                }
-            });
-
-        let ent = self.fns.entry(name.to_string()).or_insert_with(Vec::new);
-        (*ent).push(FnType::ExternalFn3(wrapped));
-    }
-}
-
-impl<'a, A, T, U, V, W> FnRegister<A, W, (&'a T, U, V)> for Engine
-    where A: 'static + Fn(T, U, V) -> W,
-          T: Clone + Any,
-          U: Clone + Any,
-          V: Clone + Any,
-          W: Any
-{
-    fn register_fn(&mut self, name: &str, fun: A) {
-        let wrapped: Box<Fn(&mut Box<Any>, &mut Box<Any>, &mut Box<Any>)
-                            -> Result<Box<Any>, EvalAltResult>> =
-            Box::new(move |arg1: &mut Box<Any>, arg2: &mut Box<Any>, arg3: &mut Box<Any>| {
-                let inside1 = (*arg1).downcast_mut() as Option<&mut T>;
-                let inside2 = (*arg2).downcast_mut() as Option<&mut U>;
-                let inside3 = (*arg3).downcast_mut() as Option<&mut V>;
-
-                match (inside1, inside2, inside3) {
-                    (Some(b), Some(c), Some(d)) => {
-                        Ok(Box::new(fun(b.clone(), c.clone(), d.clone())) as Box<Any>)
-                    }
-                    _ => Err(EvalAltResult::ErrorFunctionArgMismatch),
-                }
-            });
-
-        let ent = self.fns.entry(name.to_string()).or_insert_with(Vec::new);
-        (*ent).push(FnType::ExternalFn3(wrapped));
-    }
-}
-
-impl<'a, A, T, U, V> FnRegister<A, V, (&'a mut T, U)> for Engine
-    where A: 'static + Fn(&mut T, U) -> V,
-          T: Any,
-          U: Clone + Any,
-          V: Any
-{
-    fn register_fn(&mut self, name: &str, fun: A) {
-        let wrapped: Box<Fn(&mut Box<Any>, &mut Box<Any>) -> Result<Box<Any>, EvalAltResult>> =
-            Box::new(move |arg1: &mut Box<Any>, arg2: &mut Box<Any>| {
-                let inside1 = (*arg1).downcast_mut() as Option<&mut T>;
-                let inside2 = (*arg2).downcast_mut() as Option<&mut U>;
-
-                match (inside1, inside2) {
-                    (Some(b), Some(c)) => Ok(Box::new(fun(b, c.clone())) as Box<Any>),
-                    _ => Err(EvalAltResult::ErrorFunctionArgMismatch),
-                }
-            });
-
-        let ent = self.fns.entry(name.to_string()).or_insert_with(Vec::new);
-        (*ent).push(FnType::ExternalFn2(wrapped));
-    }
-}
-
-impl<'a, A, T, U, V> FnRegister<A, V, (&'a T, U)> for Engine
-    where A: 'static + Fn(T, U) -> V,
-          T: Clone + Any,
-          U: Clone + Any,
-          V: Any
-{
-    fn register_fn(&mut self, name: &str, fun: A) {
-        let wrapped: Box<Fn(&mut Box<Any>, &mut Box<Any>) -> Result<Box<Any>, EvalAltResult>> =
-            Box::new(move |arg1: &mut Box<Any>, arg2: &mut Box<Any>| {
-                let inside1 = (*arg1).downcast_mut() as Option<&mut T>;
-                let inside2 = (*arg2).downcast_mut() as Option<&mut U>;
-
-                match (inside1, inside2) {
-                    (Some(b), Some(c)) => Ok(Box::new(fun(b.clone(), c.clone())) as Box<Any>),
-                    _ => Err(EvalAltResult::ErrorFunctionArgMismatch),
-                }
-            });
-
-        let ent = self.fns.entry(name.to_string()).or_insert_with(Vec::new);
-        (*ent).push(FnType::ExternalFn2(wrapped));
-    }
-}
-
-impl<'a, A, T, U> FnRegister<A, U, (&'a mut T)> for Engine
-    where A: 'static + Fn(&mut T) -> U,
-          T: Any,
-          U: Any
-{
-    fn register_fn(&mut self, name: &str, fun: A) {
-        let wrapped: Box<Fn(&mut Box<Any>) -> Result<Box<Any>, EvalAltResult>> =
-            Box::new(move |arg: &mut Box<Any>| {
-                let inside = (*arg).downcast_mut() as Option<&mut T>;
-
-                match inside {
-                    Some(b) => Ok(Box::new(fun(b)) as Box<Any>),
-                    None => Err(EvalAltResult::ErrorFunctionArgMismatch),
-                }
-            });
-
-        let ent = self.fns.entry(name.to_string()).or_insert_with(Vec::new);
-        (*ent).push(FnType::ExternalFn1(wrapped));
-    }
-}
-
-
-impl<'a, A, T, U> FnRegister<A, U, (&'a T)> for Engine
-    where A: 'static + Fn(T) -> U,
-          T: Clone + Any,
-          U: Any
-{
-    fn register_fn(&mut self, name: &str, fun: A) {
-        let wrapped: Box<Fn(&mut Box<Any>) -> Result<Box<Any>, EvalAltResult>> =
-            Box::new(move |arg: &mut Box<Any>| {
-                let inside = (*arg).downcast_mut() as Option<&mut T>;
-                match inside {
-                    Some(b) => Ok(Box::new(fun(b.clone())) as Box<Any>),
-                    None => Err(EvalAltResult::ErrorFunctionArgMismatch),
-                }
-            });
-
-        let ent = self.fns.entry(name.to_string()).or_insert_with(Vec::new);
-        (*ent).push(FnType::ExternalFn1(wrapped));
-    }
-}
-
-impl<A, T> FnRegister<A, T, ()> for Engine
-    where A: 'static + Fn() -> T,
-          T: Any
-{
-    fn register_fn(&mut self, name: &str, fun: A) {
-        let wrapped: Box<Fn() -> Result<Box<Any>, EvalAltResult>> = Box::new(move || {
-            Ok(Box::new(fun()) as Box<Any>)
-        });
-
-        let ent = self.fns.entry(name.to_string()).or_insert_with(Vec::new);
-        (*ent).push(FnType::ExternalFn0(wrapped));
-    }
-}
+#[cfg_attr(rustfmt, rustfmt_skip)]
+def_register!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S);
diff --git a/src/lib.rs b/src/lib.rs
index 03ba09456..0ffb0a62b 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,10 +1,11 @@
 //! # Rhai - embedded scripting for Rust
+//!
 //! Rhai is a tiny, simple and very fast embedded scripting language for Rust
 //! that gives you a safe and easy way to add scripting to your applications.
 //! It provides a familiar syntax based on JS and Rust and a simple Rust interface.
 //! Here is a quick example. First, the contents of `my_script.rhai`:
-//! 
-//! ```rust_todo_disable_testing_enable_highlighting
+//!
+//! ```rust,ignore
 //! fn factorial(x) {
 //!     if x == 1 { return 1; }
 //!	    x * factorial(x - 1)
@@ -12,36 +13,35 @@
 //!
 //! compute_something(factorial(10))
 //! ```
-//! 
+//!
 //! And the Rust part:
-//! 
+//!
 //! ```rust
-//! use rhai::{FnRegister, Engine};
-//! 
+//! use rhai::{Engine, RegisterFn};
+//!
 //! fn compute_something(x: i64) -> bool {
 //!	    (x % 40) == 0
 //! }
-//! 
+//!
 //! let mut engine = Engine::new();
 //! engine.register_fn("compute_something", compute_something);
-//! # // Very ugly hack incoming, TODO
+//! # // Very ugly hack incoming, TODO (maybe mark as no_run?)
 //! # use std::fs::{File, remove_file};
 //! # use std::io::Write;
 //! # let mut f = File::create("my_script.rhai").unwrap();
 //! # let _ = write!(f, "{}", "fn f(x) { if x == 1 { return 1; } x * f(x-1) } compute_something(f(10))");
-//! assert!(engine.eval_file::<bool>("my_script.rhai").unwrap());
+//! assert_eq!(engine.eval_file::<bool>("my_script.rhai"), Ok(true));
 //! # let _ = remove_file("my_script.rhai");
 //! ```
 //!
-//! [Check out the README on github for more information!](https://github.com/jonathandturner/rhai)
+//! [Check out the README on GitHub for more information!](https://github.com/jonathandturner/rhai)
 
 // lints required by Rhai
-#![allow(unknown_lints,
-        type_complexity,
-        new_without_default_derive,
-        needless_pass_by_value,
-        too_many_arguments)]
+#![allow(warnings, unknown_lints, type_complexity, new_without_default_derive,
+         needless_pass_by_value, too_many_arguments)]
 
+mod any;
+mod call;
 mod engine;
 mod fn_register;
 mod parser;
@@ -49,6 +49,6 @@ mod parser;
 #[cfg(test)]
 mod tests;
 
-pub use engine::{Engine, Scope, EvalAltResult};
-pub use fn_register::FnRegister;
-
+pub use any::Any;
+pub use engine::{Engine, EvalAltResult, Scope};
+pub use fn_register::RegisterFn;


Test patch:
diff --git a/src/tests/arrays.rs b/src/tests/arrays.rs
index 4d87728b0..07e2cd30e 100644
--- a/src/tests/arrays.rs
+++ b/src/tests/arrays.rs
@@ -1,5 +1,5 @@
 use engine::Engine;
-use fn_register::FnRegister;
+use fn_register::RegisterFn;
 
 #[test]
 fn test_arrays() {
diff --git a/src/tests/float.rs b/src/tests/float.rs
index 6030a5b2c..573077ff2 100644
--- a/src/tests/float.rs
+++ b/src/tests/float.rs
@@ -1,5 +1,5 @@
 use engine::Engine;
-use fn_register::FnRegister;
+use fn_register::RegisterFn;
 
 #[test]
 fn test_float() {
diff --git a/src/tests/get_set.rs b/src/tests/get_set.rs
index 46790e819..6315ad2ca 100644
--- a/src/tests/get_set.rs
+++ b/src/tests/get_set.rs
@@ -1,5 +1,5 @@
 use engine::Engine;
-use fn_register::FnRegister;
+use fn_register::RegisterFn;
 
 #[test]
 fn test_get_set() {
@@ -86,9 +86,5 @@ fn test_big_get_set() {
 
     engine.register_fn("new_tp", TestParent::new);
 
-    if let Ok(result) = engine.eval::<i64>("let a = new_tp(); a.child.x = 500; a.child.x") {
-        assert_eq!(result, 500);
-    } else {
-        assert!(false);
-    }
+    assert_eq!(engine.eval::<i64>("let a = new_tp(); a.child.x = 500; a.child.x"), Ok(500));
 }
diff --git a/src/tests/method_call.rs b/src/tests/method_call.rs
index a2c36ed92..72ed30648 100644
--- a/src/tests/method_call.rs
+++ b/src/tests/method_call.rs
@@ -1,5 +1,5 @@
 use engine::Engine;
-use fn_register::FnRegister;
+use fn_register::RegisterFn;
 
 #[test]
 fn test_method_call() {
diff --git a/src/tests/mismatched_op.rs b/src/tests/mismatched_op.rs
index 381e2b398..a3e04999b 100644
--- a/src/tests/mismatched_op.rs
+++ b/src/tests/mismatched_op.rs
@@ -4,8 +4,8 @@ use engine::{Engine, EvalAltResult};
 fn test_mismatched_op() {
     let mut engine = Engine::new();
 
-    match engine.eval::<i64>("60 + \"hello\"") {
-        Err(EvalAltResult::ErrorFunctionArgMismatch) => (),
-        _ => assert!(false),
-    }
+    assert_eq!(
+        engine.eval::<i64>("60 + \"hello\""),
+        Err(EvalAltResult::ErrorFunctionNotFound)
+    );
 }