Repository: cobalt-org/liquid-rust
Pull request #386 (2020-01-23 00:30:26)

Remove an allocation with filters
Currently, we lookup a value, clone it, and then apply filters successively.  The clone is so we match the output type of a filter.  Switching to a Cow let's us unify the types.

- First, we needed to cleanup our filter test code because its double-borrows were breaking the trait API change
- We then switch the Filter trait to accepting a borrow
- Now we can add a Cow and use it.


Patch:
diff --git a/crates/compiler/src/filter.rs b/crates/compiler/src/filter.rs
index 0a966cb..b5ef9b0 100644
--- a/crates/compiler/src/filter.rs
+++ b/crates/compiler/src/filter.rs
@@ -2,7 +2,7 @@ use std::fmt::{Debug, Display};
 
 use liquid_error::Result;
 use liquid_interpreter::{Context, Expression};
-use liquid_value::Value;
+use liquid_value::{Value, ValueView};
 
 /// A structure that holds the information of a single parameter in a filter.
 /// This includes its name, description and whether it is optional or required.
@@ -94,7 +94,7 @@ pub struct FilterArguments<'a> {
 /// struct AbsFilter; // There are no parameters, so implements `Default`.
 ///
 /// impl Filter for AbsFilter {
-///     fn evaluate(&self, input: &Value, _context: &Context) -> Result<Value> {
+///     fn evaluate(&self, input: &dyn  ValueView, _context: &Context) -> Result<Value> {
 ///         // Implementation of the filter here
 ///     }
 /// }
@@ -110,7 +110,7 @@ pub struct FilterArguments<'a> {
 /// }
 ///
 /// impl Filter for AtLeastFilter {
-///     fn evaluate(&self, input: &Value, context: &Context) -> Result<Value> {
+///     fn evaluate(&self, input: &ValueViwe, context: &Context) -> Result<Value> {
 ///         // Evaluate the `FilterParameters`
 ///         let args = self.args.evaluate(context)?;
 ///
@@ -132,7 +132,7 @@ pub struct FilterArguments<'a> {
 /// }
 ///
 /// impl Filter for AtLeastFilter {
-///     fn evaluate(&self, input: &Value, context: &Context) -> Result<Value> {
+///     fn evaluate(&self, input: &dyn ValueView, context: &Context) -> Result<Value> {
 ///         // Evaluate the `FilterParameters`
 ///         let args = self.args.evaluate(context)?;
 ///
@@ -142,7 +142,7 @@ pub struct FilterArguments<'a> {
 /// ```
 pub trait Filter: Send + Sync + Debug + Display {
     // This will evaluate the expressions and evaluate the filter.
-    fn evaluate(&self, input: &Value, context: &Context) -> Result<Value>;
+    fn evaluate(&self, input: &dyn ValueView, context: &Context) -> Result<Value>;
 }
 
 /// A trait to register a new filter in the `liquid::Parser`.
diff --git a/crates/compiler/src/filter_chain.rs b/crates/compiler/src/filter_chain.rs
index 36718df..3bb9ef4 100644
--- a/crates/compiler/src/filter_chain.rs
+++ b/crates/compiler/src/filter_chain.rs
@@ -8,7 +8,7 @@ use liquid_error::{Result, ResultLiquidExt, ResultLiquidReplaceExt};
 use liquid_interpreter::Context;
 use liquid_interpreter::Expression;
 use liquid_interpreter::Renderable;
-use liquid_value::{Value, ValueView};
+use liquid_value::{ValueCow, ValueView};
 
 /// A `Value` expression.
 #[derive(Debug)]
@@ -24,19 +24,21 @@ impl FilterChain {
     }
 
     /// Process `Value` expression within `context`'s stack.
-    pub fn evaluate(&self, context: &Context) -> Result<Value> {
+    pub fn evaluate<'s>(&'s self, context: &'s Context) -> Result<ValueCow<'s>> {
         // take either the provided value or the value from the provided variable
-        let mut entry = self.entry.evaluate(context)?.to_value();
+        let mut entry = ValueCow::Borrowed(self.entry.evaluate(context)?);
 
         // apply all specified filters
         for filter in &self.filters {
-            entry = filter
-                .evaluate(&entry, context)
-                .trace("Filter error")
-                .context_key("filter")
-                .value_with(|| format!("{}", filter).into())
-                .context_key("input")
-                .value_with(|| format!("{}", entry.source()).into())?;
+            entry = ValueCow::Owned(
+                filter
+                    .evaluate(entry.as_view(), context)
+                    .trace("Filter error")
+                    .context_key("filter")
+                    .value_with(|| format!("{}", filter).into())
+                    .context_key("input")
+                    .value_with(|| format!("{}", entry.source()).into())?,
+            );
         }
 
         Ok(entry)
diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs
index 4e0cded..426fcd4 100644
--- a/crates/core/src/lib.rs
+++ b/crates/core/src/lib.rs
@@ -24,8 +24,26 @@ pub use liquid_interpreter::Context;
 pub use liquid_interpreter::Expression;
 pub use liquid_interpreter::Renderable;
 pub use liquid_interpreter::Template;
-pub use liquid_value::object;
-pub use liquid_value::to_object;
-pub use liquid_value::Object;
-pub use liquid_value::Value;
+pub use liquid_value::{object, to_object, Object};
+pub use liquid_value::{to_value, value, Value};
 pub use liquid_value::{ObjectView, ValueView};
+
+#[allow(unused_macros)]
+#[macro_export]
+macro_rules! call_filter {
+    ($filter:expr, $input:expr) => {{
+        $crate::call_filter!($filter, $input, )
+    }};
+    ($filter:expr, $input:expr, $($args:expr),*) => {{
+        let positional = Box::new(vec![$($crate::Expression::Literal($crate::value!($args))),*].into_iter());
+        let keyword = Box::new(Vec::new().into_iter());
+        let args = $crate::compiler::FilterArguments { positional, keyword };
+
+        let context = $crate::Context::default();
+
+        let input = $crate::value!($input);
+
+        $crate::ParseFilter::parse(&$filter, args)
+            .and_then(|filter| $crate::Filter::evaluate(&*filter, &input, &context))
+    }};
+}
diff --git a/crates/value/src/cow.rs b/crates/value/src/cow.rs
new file mode 100644
index 0000000..f575613
--- /dev/null
+++ b/crates/value/src/cow.rs
@@ -0,0 +1,92 @@
+use kstring::KStringCow;
+
+use super::ArrayView;
+use super::DisplayCow;
+use super::ObjectView;
+use super::ScalarCow;
+use super::State;
+use super::Value;
+use super::ValueView;
+
+/// Abstract the lifetime of a `Value`.
+#[derive(Clone, Debug)]
+pub enum ValueCow<'s> {
+    /// A boxed `Value`
+    Owned(Value),
+    /// A borrowed `Value`
+    Borrowed(&'s dyn ValueView),
+}
+
+impl<'s> ValueCow<'s> {
+    /// Extracts the owned data.
+    ///
+    /// Clones the data if it is not already owned.
+    pub fn into_owned(self) -> Value {
+        match self {
+            ValueCow::Owned(x) => x,
+            ValueCow::Borrowed(x) => x.to_value(),
+        }
+    }
+
+    /// Performs the conversion.
+    pub fn as_view(&self) -> &dyn ValueView {
+        match self {
+            ValueCow::Owned(o) => o.as_view(),
+            ValueCow::Borrowed(b) => *b,
+        }
+    }
+}
+
+impl<'s> ValueView for ValueCow<'s> {
+    fn render(&self) -> DisplayCow<'_> {
+        self.as_view().render()
+    }
+    fn source(&self) -> DisplayCow<'_> {
+        self.as_view().source()
+    }
+    fn type_name(&self) -> &'static str {
+        self.as_view().type_name()
+    }
+    fn query_state(&self, state: State) -> bool {
+        self.as_view().query_state(state)
+    }
+
+    fn to_kstr(&self) -> KStringCow<'_> {
+        self.as_view().to_kstr()
+    }
+    fn to_value(&self) -> Value {
+        self.as_view().to_value()
+    }
+
+    fn as_scalar(&self) -> Option<ScalarCow<'_>> {
+        self.as_view().as_scalar()
+    }
+
+    fn as_array(&self) -> Option<&dyn ArrayView> {
+        self.as_view().as_array()
+    }
+
+    fn as_object(&self) -> Option<&dyn ObjectView> {
+        self.as_view().as_object()
+    }
+
+    fn as_state(&self) -> Option<State> {
+        self.as_view().as_state()
+    }
+
+    fn is_nil(&self) -> bool {
+        self.as_view().is_nil()
+    }
+}
+
+impl From<Value> for ValueCow<'static> {
+    fn from(other: Value) -> Self {
+        ValueCow::Owned(other)
+    }
+}
+
+impl<'s> From<&'s Value> for ValueCow<'s> {
+    fn from(other: &'s Value) -> Self {
+        ValueCow::Borrowed(other.as_view())
+    }
+}
diff --git a/crates/value/src/lib.rs b/crates/value/src/lib.rs
index ca8ad9e..54e478b 100644
--- a/crates/value/src/lib.rs
+++ b/crates/value/src/lib.rs
@@ -11,6 +11,7 @@ extern crate serde;
 mod macros;
 
 mod array;
+mod cow;
 mod date;
 mod display;
 mod object;
@@ -24,6 +25,7 @@ mod view;
 pub mod map;
 
 pub use crate::array::*;
+pub use crate::cow::*;
 pub use crate::date::*;
 pub use crate::display::*;
 pub use crate::object::*;
diff --git a/crates/value/src/macros.rs b/crates/value/src/macros.rs
index 397d3e2..0f8153a 100644
--- a/crates/value/src/macros.rs
+++ b/crates/value/src/macros.rs
@@ -87,7 +87,7 @@ macro_rules! scalar {
     // Any Serialize type: numbers, strings, struct literals, variables etc.
     // Must be below every other rule.
     ($other:expr) => {
-        $crate::to_scalar($other).unwrap()
+        $crate::to_scalar(&$other).unwrap()
     };
 }
 
@@ -113,7 +113,7 @@ macro_rules! value_internal {
     };
 
     ([]) => {
-        $crate::Value::Array(Default::default())
+        $crate::Value::Array(::std::default::Default::default())
     };
 
     ([ $($tt:tt)+ ]) => {
@@ -121,7 +121,7 @@ macro_rules! value_internal {
     };
 
     ({}) => {
-        $crate::Value::Object(Default::default())
+        $crate::Value::Object(::std::default::Default::default())
     };
 
     ({ $($tt:tt)+ }) => {
@@ -139,7 +139,7 @@ macro_rules! value_internal {
     // Any Serialize type: numbers, strings, struct literals, variables etc.
     // Must be below every other rule.
     ($other:expr) => {
-        $crate::to_value($other).unwrap()
+        $crate::to_value(&$other).unwrap()
     };
 }
 
@@ -277,7 +277,7 @@ macro_rules! object_internal {
     // Any Serialize type: numbers, strings, struct literals, variables etc.
     // Must be below every other rule.
     ($other:expr) => {
-        $crate::to_object($other).unwrap()
+        $crate::to_object(&$other).unwrap()
     };
 }
 
diff --git a/crates/value/src/object.rs b/crates/value/src/object.rs
index 42dbafe..120cfd6 100644
--- a/crates/value/src/object.rs
+++ b/crates/value/src/object.rs
@@ -153,12 +153,12 @@ impl ObjectView for Object {
     }
 
     fn values<'k>(&'k self) -> Box<dyn Iterator<Item = &'k dyn ValueView> + 'k> {
-        let i = Object::values(self).map(|v| v.as_value());
+        let i = Object::values(self).map(|v| v.as_view());
         Box::new(i)
     }
 
     fn iter<'k>(&'k self) -> Box<dyn Iterator<Item = (KStringCow<'k>, &'k dyn ValueView)> + 'k> {
-        let i = Object::iter(self).map(|(k, v)| (k.as_str().into(), v.as_value()));
+        let i = Object::iter(self).map(|(k, v)| (k.as_str().into(), v.as_view()));
         Box::new(i)
     }
 
@@ -167,7 +167,7 @@ impl ObjectView for Object {
     }
 
     fn get<'s>(&'s self, index: &str) -> Option<&'s dyn ValueView> {
-        Object::get(self, index).map(|v| v.as_value())
+        Object::get(self, index).map(|v| v.as_view())
     }
 }
 
@@ -236,12 +236,12 @@ impl<K: ObjectIndex, V: ValueView, S: ::std::hash::BuildHasher> ObjectView for H
     }
 
     fn values<'k>(&'k self) -> Box<dyn Iterator<Item = &'k dyn ValueView> + 'k> {
-        let i = HashMap::values(self).map(as_value);
+        let i = HashMap::values(self).map(as_view);
         Box::new(i)
     }
 
     fn iter<'k>(&'k self) -> Box<dyn Iterator<Item = (KStringCow<'k>, &'k dyn ValueView)> + 'k> {
-        let i = HashMap::iter(self).map(|(k, v)| (k.as_str().into(), as_value(v)));
+        let i = HashMap::iter(self).map(|(k, v)| (k.as_str().into(), as_view(v)));
         Box::new(i)
     }
 
@@ -250,7 +250,7 @@ impl<K: ObjectIndex, V: ValueView, S: ::std::hash::BuildHasher> ObjectView for H
     }
 
     fn get<'s>(&'s self, index: &str) -> Option<&'s dyn ValueView> {
-        HashMap::get(self, index).map(as_value)
+        HashMap::get(self, index).map(as_view)
     }
 }
 
@@ -299,12 +299,12 @@ impl<K: ObjectIndex, V: ValueView> ObjectView for BTreeMap<K, V> {
     }
 
     fn values<'k>(&'k self) -> Box<dyn Iterator<Item = &'k dyn ValueView> + 'k> {
-        let i = BTreeMap::values(self).map(as_value);
+        let i = BTreeMap::values(self).map(as_view);
         Box::new(i)
     }
 
     fn iter<'k>(&'k self) -> Box<dyn Iterator<Item = (KStringCow<'k>, &'k dyn ValueView)> + 'k> {
-        let i = BTreeMap::iter(self).map(|(k, v)| (k.as_str().into(), as_value(v)));
+        let i = BTreeMap::iter(self).map(|(k, v)| (k.as_str().into(), as_view(v)));
         Box::new(i)
     }
 
@@ -313,11 +313,11 @@ impl<K: ObjectIndex, V: ValueView> ObjectView for BTreeMap<K, V> {
     }
 
     fn get<'s>(&'s self, index: &str) -> Option<&'s dyn ValueView> {
-        BTreeMap::get(self, index).map(as_value)
+        BTreeMap::get(self, index).map(as_view)
     }
 }
 
-fn as_value<T: ValueView>(value: &T) -> &dyn ValueView {
+fn as_view<T: ValueView>(value: &T) -> &dyn ValueView {
     value
 }
 
diff --git a/crates/value/src/ser.rs b/crates/value/src/ser.rs
index d90e21c..e630123 100644
--- a/crates/value/src/ser.rs
+++ b/crates/value/src/ser.rs
@@ -15,10 +15,10 @@ use super::Value;
 ///
 /// ```rust
 /// let s = "foo";
-/// let value = liquid_value::to_value(s).unwrap();
+/// let value = liquid_value::to_value(&s).unwrap();
 /// assert_eq!(value, liquid_value::Value::scalar(s));
 /// ```
-pub fn to_value<T>(value: T) -> Result<Value, liquid_error::Error>
+pub fn to_value<T>(value: &T) -> Result<Value, liquid_error::Error>
 where
     T: Serialize,
 {
@@ -26,7 +26,7 @@ where
 }
 
 /// Convert a `T` into `liquid_value::Object`.
-pub fn to_object<T>(value: T) -> Result<Object, liquid_error::Error>
+pub fn to_object<T>(value: &T) -> Result<Object, liquid_error::Error>
 where
     T: Serialize,
 {
@@ -39,10 +39,10 @@ where
 ///
 /// ```rust
 /// let s = "foo";
-/// let value = liquid_value::to_scalar(s).unwrap();
+/// let value = liquid_value::to_scalar(&s).unwrap();
 /// assert_eq!(value, liquid_value::Scalar::new(s));
 /// ```
-pub fn to_scalar<T>(value: T) -> Result<Scalar, liquid_error::Error>
+pub fn to_scalar<T>(value: &T) -> Result<Scalar, liquid_error::Error>
 where
     T: Serialize,
 {
diff --git a/crates/value/src/values.rs b/crates/value/src/values.rs
index d7905c8..55b5721 100644
--- a/crates/value/src/values.rs
+++ b/crates/value/src/values.rs
@@ -6,7 +6,7 @@ use crate::DisplayCow;
 use crate::State;
 use crate::{Array, ArrayView, Object, ObjectView};
 use crate::{Scalar, ScalarCow};
-use crate::{ValueView, ValueViewCmp};
+use crate::{ValueCow, ValueView, ValueViewCmp};
 
 /// An enum to represent different value types
 #[derive(Clone, Debug, Serialize, Deserialize)]
@@ -37,7 +37,7 @@ impl Value {
     }
 
     /// Performs the conversion.
-    pub fn as_value(&self) -> &dyn ValueView {
+    pub fn as_view(&self) -> &dyn ValueView {
         match &self {
             Value::Scalar(ref x) => x,
             Value::Object(ref x) => x,
@@ -258,13 +258,19 @@ impl Default for Value {
 
 impl PartialEq<Value> for Value {
     fn eq(&self, other: &Self) -> bool {
-        crate::value_eq(self, other)
+        crate::value_eq(self.as_view(), other.as_view())
     }
 }
 
 impl<'v> PartialEq<ValueViewCmp<'v>> for Value {
     fn eq(&self, other: &ValueViewCmp<'v>) -> bool {
-        ValueViewCmp::new(self) == *other
+        ValueViewCmp::new(self.as_view()) == *other
+    }
+}
+
+impl<'v> PartialEq<ValueCow<'v>> for Value {
+    fn eq(&self, other: &ValueCow<'v>) -> bool {
+        crate::value_eq(self.as_view(), other.as_view())
     }
 }
 
diff --git a/src/filters/extra/date.rs b/src/filters/extra/date.rs
index 4ad7708..149e65b 100644
--- a/src/filters/extra/date.rs
+++ b/src/filters/extra/date.rs
@@ -39,7 +39,7 @@ struct DateInTzFilter {
 }
 
 impl Filter for DateInTzFilter {
-    fn evaluate(&self, input: &Value, context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, input: &dyn ValueView, context: &Context<'_>) -> Result<Value> {
         let args = self.args.evaluate(context)?;
 
         let date = input
@@ -57,136 +57,93 @@ impl Filter for DateInTzFilter {
 
 #[cfg(test)]
 mod tests {
-
     use super::*;
 
-    macro_rules! unit {
-        ($a:ident, $b:expr) => {{
-            unit!($a, $b, )
-        }};
-        ($a:ident, $b:expr, $($c:expr),*) => {{
-            let positional = Box::new(vec![$(::liquid_core::interpreter::Expression::Literal($c)),*].into_iter());
-            let keyword = Box::new(Vec::new().into_iter());
-            let args = ::liquid_core::compiler::FilterArguments { positional, keyword };
-
-            let context = ::liquid_core::interpreter::Context::default();
-
-            let filter = ::liquid_core::compiler::ParseFilter::parse(&$a, args).unwrap();
-            ::liquid_core::compiler::Filter::evaluate(&*filter, &$b, &context).unwrap()
-        }};
-    }
-
-    macro_rules! failed {
-        ($a:ident, $b:expr) => {{
-            failed!($a, $b, )
-        }};
-        ($a:ident, $b:expr, $($c:expr),*) => {{
-            let positional = Box::new(vec![$(::liquid_core::interpreter::Expression::Literal($c)),*].into_iter());
-            let keyword = Box::new(Vec::new().into_iter());
-            let args = ::liquid_core::compiler::FilterArguments { positional, keyword };
-
-            let context = ::liquid_core::interpreter::Context::default();
-
-            ::liquid_core::compiler::ParseFilter::parse(&$a, args)
-                .and_then(|filter| ::liquid_core::compiler::Filter::evaluate(&*filter, &$b, &context))
-                .unwrap_err()
-        }};
-    }
-
-    macro_rules! tos {
-        ($a:expr) => {{
-            Value::scalar($a.to_owned())
-        }};
-    }
-
     #[test]
     fn unit_date_in_tz_same_day() {
-        let input = tos!("13 Jun 2016 12:00:00 +0000");
-        let unit_result = unit!(
+        let unit_result = liquid_core::call_filter!(
             DateInTz,
-            input,
-            tos!("%Y-%m-%d %H:%M:%S %z"),
-            Value::scalar(3i32)
-        );
-        let desired_result = tos!("2016-06-13 15:00:00 +0300");
+            "13 Jun 2016 12:00:00 +0000",
+            "%Y-%m-%d %H:%M:%S %z",
+            3i32
+        )
+        .unwrap();
+        let desired_result = liquid_core::value!("2016-06-13 15:00:00 +0300");
         assert_eq!(unit_result, desired_result);
     }
 
     #[test]
     fn unit_date_in_tz_previous_day() {
-        let input = tos!("13 Jun 2016 12:00:00 +0000");
-        let unit_result = unit!(
+        let unit_result = liquid_core::call_filter!(
             DateInTz,
-            input,
-            tos!("%Y-%m-%d %H:%M:%S %z"),
-            Value::scalar(-13i32)
-        );
-        let desired_result = tos!("2016-06-12 23:00:00 -1300");
+            "13 Jun 2016 12:00:00 +0000",
+            "%Y-%m-%d %H:%M:%S %z",
+            -13i32
+        )
+        .unwrap();
+        let desired_result = liquid_core::value!("2016-06-12 23:00:00 -1300");
         assert_eq!(unit_result, desired_result);
     }
 
     #[test]
     fn unit_date_in_tz_next_day() {
-        let input = tos!("13 Jun 2016 12:00:00 +0000");
-        let unit_result = unit!(
+        let unit_result = liquid_core::call_filter!(
             DateInTz,
-            input,
-            tos!("%Y-%m-%d %H:%M:%S %z"),
-            Value::scalar(13i32)
-        );
-        let desired_result = tos!("2016-06-14 01:00:00 +1300");
+            "13 Jun 2016 12:00:00 +0000",
+            "%Y-%m-%d %H:%M:%S %z",
+            13i32
+        )
+        .unwrap();
+        let desired_result = liquid_core::value!("2016-06-14 01:00:00 +1300");
         assert_eq!(unit_result, desired_result);
     }
 
     #[test]
     fn unit_date_in_tz_input_not_a_string() {
-        let input = &Value::scalar(0f64);
-        failed!(
-            DateInTz,
-            input,
-            tos!("%Y-%m-%d %H:%M:%S %z"),
-            Value::scalar(0i32)
-        );
+        liquid_core::call_filter!(DateInTz, 0f64, "%Y-%m-%d %H:%M:%S %z", 0i32).unwrap_err();
     }
 
     #[test]
     fn unit_date_in_tz_input_not_a_date_string() {
-        let input = &tos!("blah blah blah");
-        failed!(
-            DateInTz,
-            input,
-            tos!("%Y-%m-%d %H:%M:%S %z"),
-            Value::scalar(0i32)
-        );
+        liquid_core::call_filter!(DateInTz, "blah blah blah", "%Y-%m-%d %H:%M:%S %z", 0i32)
+            .unwrap_err();
     }
 
     #[test]
     fn unit_date_in_tz_offset_not_a_num() {
-        let input = &tos!("13 Jun 2016 12:00:00 +0000");
-        failed!(DateInTz, input, tos!("%Y-%m-%d %H:%M:%S %z"), tos!("Hello"));
+        liquid_core::call_filter!(
+            DateInTz,
+            "13 Jun 2016 12:00:00 +0000",
+            "%Y-%m-%d %H:%M:%S %z",
+            "Hello"
+        )
+        .unwrap_err();
     }
 
     #[test]
     fn unit_date_in_tz_zero_arguments() {
-        let input = &tos!("13 Jun 2016 12:00:00 +0000");
-        failed!(DateInTz, input);
+        liquid_core::call_filter!(DateInTz, "13 Jun 2016 12:00:00 +0000").unwrap_err();
     }
 
     #[test]
     fn unit_date_in_tz_one_argument() {
-        let input = &tos!("13 Jun 2016 12:00:00 +0000");
-        failed!(DateInTz, input, tos!("%Y-%m-%d %H:%M:%S %z"));
+        liquid_core::call_filter!(
+            DateInTz,
+            "13 Jun 2016 12:00:00 +0000",
+            "%Y-%m-%d %H:%M:%S %z"
+        )
+        .unwrap_err();
     }
 
     #[test]
     fn unit_date_in_tz_three_arguments() {
-        let input = &tos!("13 Jun 2016 12:00:00 +0000");
-        failed!(
+        liquid_core::call_filter!(
             DateInTz,
-            input,
-            tos!("%Y-%m-%d %H:%M:%S %z"),
-            Value::scalar(0f64),
-            Value::scalar(1f64)
-        );
+            "13 Jun 2016 12:00:00 +0000",
+            "%Y-%m-%d %H:%M:%S %z",
+            0f64,
+            1f64
+        )
+        .unwrap_err();
     }
 }
diff --git a/src/filters/extra/pluralize.rs b/src/filters/extra/pluralize.rs
index 32e98ea..25f24bf 100644
--- a/src/filters/extra/pluralize.rs
+++ b/src/filters/extra/pluralize.rs
@@ -35,7 +35,7 @@ struct PluralizeFilter {
 }
 
 impl Filter for PluralizeFilter {
-    fn evaluate(&self, input: &Value, context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, input: &dyn ValueView, context: &Context<'_>) -> Result<Value> {
         let args = self.args.evaluate(context)?;
 
         let n = input
@@ -53,41 +53,18 @@ impl Filter for PluralizeFilter {
 
 #[cfg(test)]
 mod tests {
-
     use super::*;
 
-    macro_rules! unit {
-        ($a:ident, $b:expr) => {{
-            unit!($a, $b, )
-        }};
-        ($a:ident, $b:expr, $($c:expr),*) => {{
-            let positional = Box::new(vec![$(::liquid_core::interpreter::Expression::Literal($c)),*].into_iter());
-            let keyword = Box::new(Vec::new().into_iter());
-            let args = ::liquid_core::compiler::FilterArguments { positional, keyword };
-
-            let context = ::liquid_core::interpreter::Context::default();
-
-            let filter = ::liquid_core::compiler::ParseFilter::parse(&$a, args).unwrap();
-            ::liquid_core::compiler::Filter::evaluate(&*filter, &$b, &context).unwrap()
-        }};
-    }
-
-    macro_rules! tos {
-        ($a:expr) => {{
-            Value::scalar($a.to_owned())
-        }};
-    }
-
     #[test]
     fn unit_pluralize() {
         assert_eq!(
-            unit!(Pluralize, Value::scalar(1i32), tos!("one"), tos!("many")),
-            tos!("one")
+            liquid_core::call_filter!(Pluralize, 1i32, "one", "many").unwrap(),
+            liquid_core::value!("one")
         );
 
         assert_eq!(
-            unit!(Pluralize, Value::scalar(2i32), tos!("one"), tos!("many")),
-            tos!("many")
+            liquid_core::call_filter!(Pluralize, 2i32, "one", "many").unwrap(),
+            liquid_core::value!("many")
         );
     }
 }
diff --git a/src/filters/jekyll/array.rs b/src/filters/jekyll/array.rs
index 154dcd3..bc85012 100644
--- a/src/filters/jekyll/array.rs
+++ b/src/filters/jekyll/array.rs
@@ -33,7 +33,7 @@ struct PushFilter {
 }
 
 impl Filter for PushFilter {
-    fn evaluate(&self, input: &Value, context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, input: &dyn ValueView, context: &Context<'_>) -> Result<Value> {
         let args = self.args.evaluate(context)?;
 
         let element = args.element.to_value();
@@ -60,7 +60,7 @@ pub struct Pop;
 struct PopFilter;
 
 impl Filter for PopFilter {
-    fn evaluate(&self, input: &Value, _context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, input: &dyn ValueView, _context: &Context<'_>) -> Result<Value> {
         let mut array = input
             .to_value()
             .into_array()
@@ -94,7 +94,7 @@ struct UnshiftFilter {
 }
 
 impl Filter for UnshiftFilter {
-    fn evaluate(&self, input: &Value, context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, input: &dyn ValueView, context: &Context<'_>) -> Result<Value> {
         let args = self.args.evaluate(context)?;
 
         let element = args.element.to_value();
@@ -121,7 +121,7 @@ pub struct Shift;
 struct ShiftFilter;
 
 impl Filter for ShiftFilter {
-    fn evaluate(&self, input: &Value, _context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, input: &dyn ValueView, _context: &Context<'_>) -> Result<Value> {
         let mut array = input
             .to_value()
             .into_array()
@@ -161,7 +161,7 @@ struct ArrayToSentenceStringFilter {
 }
 
 impl Filter for ArrayToSentenceStringFilter {
-    fn evaluate(&self, input: &Value, context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, input: &dyn ValueView, context: &Context<'_>) -> Result<Value> {
         let args = self.args.evaluate(context)?;
 
         let connector = args.connector.unwrap_or("and".into());
@@ -193,126 +193,93 @@ impl Filter for ArrayToSentenceStringFilter {
 
 #[cfg(test)]
 mod tests {
-
     use super::*;
 
-    macro_rules! unit {
-        ($a:ident, $b:expr) => {{
-            unit!($a, $b, )
-        }};
-        ($a:ident, $b:expr, $($c:expr),*) => {{
-            let positional = Box::new(vec![$(::liquid_core::interpreter::Expression::Literal($c)),*].into_iter());
-            let keyword = Box::new(Vec::new().into_iter());
-            let args = ::liquid_core::compiler::FilterArguments { positional, keyword };
-
-            let context = ::liquid_core::interpreter::Context::default();
-
-            let filter = ::liquid_core::compiler::ParseFilter::parse(&$a, args).unwrap();
-            ::liquid_core::compiler::Filter::evaluate(&*filter, &$b, &context).unwrap()
-        }};
-    }
-
     #[test]
     fn unit_push() {
-        let input = Value::Array(vec![Value::scalar("Seattle"), Value::scalar("Tacoma")]);
-        let unit_result = unit!(Push, input, Value::scalar("Spokane"));
-        let desired_result = Value::Array(vec![
-            Value::scalar("Seattle"),
-            Value::scalar("Tacoma"),
-            Value::scalar("Spokane"),
-        ]);
+        let input = liquid_core::value!(["Seattle", "Tacoma"]);
+        let unit_result = liquid_core::call_filter!(Push, input, "Spokane").unwrap();
+        let desired_result = liquid_core::value!(["Seattle", "Tacoma", "Spokane",]);
         assert_eq!(unit_result, desired_result);
     }
 
     #[test]
     fn unit_pop() {
-        let input = Value::Array(vec![Value::scalar("Seattle"), Value::scalar("Tacoma")]);
-        let unit_result = unit!(Pop, input);
-        let desired_result = Value::Array(vec![Value::scalar("Seattle")]);
+        let input = liquid_core::value!(["Seattle", "Tacoma"]);
+        let unit_result = liquid_core::call_filter!(Pop, input).unwrap();
+        let desired_result = liquid_core::value!(["Seattle"]);
         assert_eq!(unit_result, desired_result);
     }
 
     #[test]
     fn unit_pop_empty() {
-        let input = Value::Array(vec![]);
-        let unit_result = unit!(Pop, input);
-        let desired_result = Value::Array(vec![]);
+        let input = liquid_core::value!([]);
+        let unit_result = liquid_core::call_filter!(Pop, input).unwrap();
+        let desired_result = liquid_core::value!([]);
         assert_eq!(unit_result, desired_result);
     }
 
     #[test]
     fn unit_unshift() {
-        let input = Value::Array(vec![Value::scalar("Seattle"), Value::scalar("Tacoma")]);
-        let unit_result = unit!(Unshift, input, Value::scalar("Olympia"));
-        let desired_result = Value::Array(vec![
-            Value::scalar("Olympia"),
-            Value::scalar("Seattle"),
-            Value::scalar("Tacoma"),
-        ]);
+        let input = liquid_core::value!(["Seattle", "Tacoma"]);
+        let unit_result = liquid_core::call_filter!(Unshift, input, "Olympia").unwrap();
+        let desired_result = liquid_core::value!(["Olympia", "Seattle", "Tacoma"]);
         assert_eq!(unit_result, desired_result);
     }
 
     #[test]
     fn unit_shift() {
-        let input = Value::Array(vec![Value::scalar("Seattle"), Value::scalar("Tacoma")]);
-        let unit_result = unit!(Shift, input);
-        let desired_result = Value::Array(vec![Value::scalar("Tacoma")]);
+        let input = liquid_core::value!(["Seattle", "Tacoma"]);
+        let unit_result = liquid_core::call_filter!(Shift, input).unwrap();
+        let desired_result = liquid_core::value!(["Tacoma"]);
         assert_eq!(unit_result, desired_result);
     }
 
     #[test]
     fn unit_shift_empty() {
-        let input = Value::Array(vec![]);
-        let unit_result = unit!(Shift, input);
-        let desired_result = Value::Array(vec![]);
+        let input = liquid_core::value!([]);
+        let unit_result = liquid_core::call_filter!(Shift, input).unwrap();
+        let desired_result = liquid_core::value!([]);
         assert_eq!(unit_result, desired_result);
     }
 
     #[test]
     fn unit_array_to_sentence_string() {
-        let input = Value::Array(vec![
-            Value::scalar("foo"),
-            Value::scalar("bar"),
-            Value::scalar("baz"),
-        ]);
-        let unit_result = unit!(ArrayToSentenceString, input);
-        let desired_result = Value::scalar("foo, bar, and baz");
+        let input = liquid_core::value!(["foo", "bar", "baz"]);
+        let unit_result = liquid_core::call_filter!(ArrayToSentenceString, input).unwrap();
+        let desired_result = "foo, bar, and baz";
         assert_eq!(unit_result, desired_result);
     }
 
     #[test]
     fn unit_array_to_sentence_string_two_elements() {
-        let input = Value::Array(vec![Value::scalar("foo"), Value::scalar("bar")]);
-        let unit_result = unit!(ArrayToSentenceString, input);
-        let desired_result = Value::scalar("foo, and bar");
+        let input = liquid_core::value!(["foo", "bar"]);
+        let unit_result = liquid_core::call_filter!(ArrayToSentenceString, input).unwrap();
+        let desired_result = "foo, and bar";
         assert_eq!(unit_result, desired_result);
     }
 
     #[test]
     fn unit_array_to_sentence_string_one_element() {
-        let input = Value::Array(vec![Value::scalar("foo")]);
-        let unit_result = unit!(ArrayToSentenceString, input);
-        let desired_result = Value::scalar("foo");
+        let input = liquid_core::value!(["foo"]);
+        let unit_result = liquid_core::call_filter!(ArrayToSentenceString, input).unwrap();
+        let desired_result = "foo";
         assert_eq!(unit_result, desired_result);
     }
 
     #[test]
     fn unit_array_to_sentence_string_no_elements() {
-        let input = Value::Array(vec![]);
-        let unit_result = unit!(ArrayToSentenceString, input);
-        let desired_result = Value::scalar("");
+        let input = liquid_core::value!([]);
+        let unit_result = liquid_core::call_filter!(ArrayToSentenceString, input).unwrap();
+        let desired_result = "";
         assert_eq!(unit_result, desired_result);
     }
 
     #[test]
     fn unit_array_to_sentence_string_custom_connector() {
-        let input = Value::Array(vec![
-            Value::scalar("foo"),
-            Value::scalar("bar"),
-            Value::scalar("baz"),
-        ]);
-        let unit_result = unit!(ArrayToSentenceString, input, Value::scalar("or"));
-        let desired_result = Value::scalar("foo, bar, or baz");
+        let input = liquid_core::value!(["foo", "bar", "baz"]);
+        let unit_result = liquid_core::call_filter!(ArrayToSentenceString, input, "or").unwrap();
+        let desired_result = "foo, bar, or baz";
         assert_eq!(unit_result, desired_result);
     }
 }
diff --git a/src/filters/jekyll/slugify.rs b/src/filters/jekyll/slugify.rs
index c854af4..6ec32fd 100644
--- a/src/filters/jekyll/slugify.rs
+++ b/src/filters/jekyll/slugify.rs
@@ -67,7 +67,7 @@ struct SlugifyFilter {
 }
 
 impl Filter for SlugifyFilter {
-    fn evaluate(&self, input: &Value, context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, input: &dyn ValueView, context: &Context<'_>) -> Result<Value> {
         let args = self.args.evaluate(context)?;
 
         let s = input.to_kstr();
@@ -98,68 +98,45 @@ impl Filter for SlugifyFilter {
 
 #[cfg(test)]
 mod tests {
-
     use super::*;
 
-    macro_rules! unit {
-        ($a:ident, $b:expr) => {{
-            unit!($a, $b, )
-        }};
-        ($a:ident, $b:expr, $($c:expr),*) => {{
-            let positional = Box::new(vec![$(::liquid_core::interpreter::Expression::Literal($c)),*].into_iter());
-            let keyword = Box::new(Vec::new().into_iter());
-            let args = ::liquid_core::compiler::FilterArguments { positional, keyword };
-
-            let context = ::liquid_core::interpreter::Context::default();
-
-            let filter = ::liquid_core::compiler::ParseFilter::parse(&$a, args).unwrap();
-            ::liquid_core::compiler::Filter::evaluate(&*filter, &$b, &context).unwrap()
-        }};
-    }
-
-    macro_rules! tos {
-        ($a:expr) => {{
-            Value::scalar($a.to_owned())
-        }};
-    }
-
     #[test]
     fn test_slugify_default() {
         assert_eq!(
-            unit!(Slugify, tos!("The _cönfig.yml file")),
-            tos!("the-cönfig-yml-file")
+            liquid_core::call_filter!(Slugify, "The _cönfig.yml file").unwrap(),
+            liquid_core::value!("the-cönfig-yml-file")
         );
     }
 
     #[test]
     fn test_slugify_ascii() {
         assert_eq!(
-            unit!(Slugify, tos!("The _cönfig.yml file"), tos!("ascii")),
-            tos!("the-c-nfig-yml-file")
+            liquid_core::call_filter!(Slugify, "The _cönfig.yml file", "ascii").unwrap(),
+            liquid_core::value!("the-c-nfig-yml-file")
         );
     }
 
     #[test]
     fn test_slugify_latin() {
         assert_eq!(
-            unit!(Slugify, tos!("The _cönfig.yml file"), tos!("latin")),
-            tos!("the-config-yml-file")
+            liquid_core::call_filter!(Slugify, "The _cönfig.yml file", "latin").unwrap(),
+            liquid_core::value!("the-config-yml-file")
         );
     }
 
     #[test]
     fn test_slugify_raw() {
         assert_eq!(
-            unit!(Slugify, tos!("The _config.yml file"), tos!("raw")),
-            tos!("the-_config.yml-file")
+            liquid_core::call_filter!(Slugify, "The _config.yml file", "raw").unwrap(),
+            liquid_core::value!("the-_config.yml-file")
         );
     }
 
     #[test]
     fn test_slugify_none() {
         assert_eq!(
-            unit!(Slugify, tos!("The _config.yml file"), tos!("none")),
-            tos!("the _config.yml file")
+            liquid_core::call_filter!(Slugify, "The _config.yml file", "none").unwrap(),
+            liquid_core::value!("the _config.yml file")
         );
     }
 }
diff --git a/src/filters/std/array.rs b/src/filters/std/array.rs
index c476895..9610eca 100644
--- a/src/filters/std/array.rs
+++ b/src/filters/std/array.rs
@@ -47,7 +47,7 @@ struct JoinFilter {
 }
 
 impl Filter for JoinFilter {
-    fn evaluate(&self, input: &Value, context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, input: &dyn ValueView, context: &Context<'_>) -> Result<Value> {
         let args = self.args.evaluate(context)?;
 
         let separator = args.separator.unwrap_or_else(|| " ".into());
@@ -120,7 +120,7 @@ fn safe_property_getter<'a>(value: &'a Value, property: &str) -> &'a dyn ValueVi
 }
 
 impl Filter for SortFilter {
-    fn evaluate(&self, input: &Value, context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, input: &dyn ValueView, context: &Context<'_>) -> Result<Value> {
         let args = self.args.evaluate(context)?;
 
         let input: Vec<_> = as_sequence(input).collect();
@@ -162,7 +162,7 @@ struct SortNaturalFilter {
 }
 
 impl Filter for SortNaturalFilter {
-    fn evaluate(&self, input: &Value, context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, input: &dyn ValueView, context: &Context<'_>) -> Result<Value> {
         let args = self.args.evaluate(context)?;
 
         let input: Vec<_> = as_sequence(input).collect();
@@ -223,24 +223,20 @@ struct WhereFilter {
 }
 
 impl Filter for WhereFilter {
-    fn evaluate(&self, input: &Value, context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, input: &dyn ValueView, context: &Context<'_>) -> Result<Value> {
         let args = self.args.evaluate(context)?;
         let property: &str = &args.property;
         let target_value: Option<&dyn ValueView> = args.target_value;
 
-        match &input {
-            Value::Array(array) => {
-                if !array.iter().all(Value::is_object) {
-                    return Ok(Value::Nil);
-                }
-            }
-            Value::Object(_) => (),
-            _ => {
-                return Err(invalid_input(
-                    "Array of objects or a single object expected",
-                ));
+        if let Some(array) = input.as_array() {
+            if !array.values().all(|v| v.is_object()) {
+                return Ok(Value::Nil);
             }
-        };
+        } else if !input.is_object() {
+            return Err(invalid_input(
+                "Array of objects or a single object expected",
+            ));
+        }
 
         let input = as_sequence(input);
         let array: Vec<_> = match target_value {
@@ -285,7 +281,7 @@ pub struct Uniq;
 struct UniqFilter;
 
 impl Filter for UniqFilter {
-    fn evaluate(&self, input: &Value, _context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, input: &dyn ValueView, _context: &Context<'_>) -> Result<Value> {
         // TODO(#267) optional property parameter
 
         let array = input
@@ -295,7 +291,7 @@ impl Filter for UniqFilter {
         for x in array.values() {
             if deduped
                 .iter()
-                .find(|v| ValueViewCmp::new(v.as_value()) == ValueViewCmp::new(x))
+                .find(|v| ValueViewCmp::new(v.as_view()) == ValueViewCmp::new(x))
                 .is_none()
             {
                 deduped.push(x.to_value())
@@ -318,7 +314,7 @@ pub struct Reverse;
 struct ReverseFilter;
 
 impl Filter for ReverseFilter {
-    fn evaluate(&self, input: &Value, _context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, input: &dyn ValueView, _context: &Context<'_>) -> Result<Value> {
         let mut array: Vec<_> = input
             .as_array()
             .ok_or_else(|| invalid_input("Array expected"))?
@@ -356,7 +352,7 @@ struct MapFilter {
 }
 
 impl Filter for MapFilter {
-    fn evaluate(&self, input: &Value, context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, input: &dyn ValueView, context: &Context<'_>) -> Result<Value> {
         let args = self.args.evaluate(context)?;
 
         let array = input
@@ -392,7 +388,7 @@ struct CompactFilter {
 }
 
 impl Filter for CompactFilter {
-    fn evaluate(&self, input: &Value, context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, input: &dyn ValueView, context: &Context<'_>) -> Result<Value> {
         let args = self.args.evaluate(context)?;
 
         let array = input
@@ -448,7 +444,7 @@ struct ConcatFilter {
 }
 
 impl Filter for ConcatFilter {
-    fn evaluate(&self, input: &Value, context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, input: &dyn ValueView, context: &Context<'_>) -> Result<Value> {
         let args = self.args.evaluate(context)?;
 
         let input = input
@@ -481,19 +477,21 @@ pub struct First;
 struct FirstFilter;
 
 impl Filter for FirstFilter {
-    fn evaluate(&self, input: &Value, _context: &Context<'_>) -> Result<Value> {
-        match *input {
-            Value::Scalar(ref x) => {
-                let c = x
-                    .to_kstr()
-                    .chars()
-                    .next()
-                    .map(|c| c.to_string())
-                    .unwrap_or_else(|| "".to_owned());
-                Ok(Value::scalar(c))
-            }
-            Value::Array(ref x) => Ok(x.first().cloned().unwrap_or_else(|| Value::Nil)),
-            _ => Err(invalid_input("String or Array expected")),
+    fn evaluate(&self, input: &dyn ValueView, _context: &Context<'_>) -> Result<Value> {
+        if let Some(x) = input.as_scalar() {
+            let c = x
+                .to_kstr()
+                .chars()
+                .next()
+                .map(|c| c.to_string())
+                .unwrap_or_else(|| "".to_owned());
+            Ok(Value::scalar(c))
+        } else if let Some(x) = input.as_array() {
+            Ok(x.first()
+                .map(|v| v.to_value())
+                .unwrap_or_else(|| Value::Nil))
+        } else {
+            Err(invalid_input("String or Array expected"))
         }
     }
 }
@@ -511,19 +509,19 @@ pub struct Last;
 struct LastFilter;
 
 impl Filter for LastFilter {
-    fn evaluate(&self, input: &Value, _context: &Context<'_>) -> Result<Value> {
-        match *input {
-            Value::Scalar(ref x) => {
-                let c = x
-                    .to_kstr()
-                    .chars()
-                    .last()
-                    .map(|c| c.to_string())
-                    .unwrap_or_else(|| "".to_owned());
-                Ok(Value::scalar(c))
-            }
-            Value::Array(ref x) => Ok(x.last().cloned().unwrap_or_else(|| Value::Nil)),
-            _ => Err(invalid_input("String or Array expected")),
+    fn evaluate(&self, input: &dyn ValueView, _context: &Context<'_>) -> Result<Value> {
+        if let Some(x) = input.as_scalar() {
+            let c = x
+                .to_kstr()
+                .chars()
+                .last()
+                .map(|c| c.to_string())
+                .unwrap_or_else(|| "".to_owned());
+            Ok(Value::scalar(c))
+        } else if let Some(x) = input.as_array() {
+            Ok(x.last().map(|v| v.to_value()).unwrap_or_else(|| Value::Nil))
+        } else {
+            Err(invalid_input("String or Array expected"))
         }
     }
 }
@@ -533,346 +531,228 @@ mod tests {
 
     use super::*;
 
-    macro_rules! unit {
-        ($a:ident, $b:expr) => {{
-            unit!($a, $b, )
-        }};
-        ($a:ident, $b:expr, $($c:expr),*) => {{
-            let positional = Box::new(vec![$(::liquid_core::interpreter::Expression::Literal($c)),*].into_iter());
-            let keyword = Box::new(Vec::new().into_iter());
-            let args = ::liquid_core::compiler::FilterArguments { positional, keyword };
-
-            let context = ::liquid_core::interpreter::Context::default();
-
-            let filter = ::liquid_core::compiler::ParseFilter::parse(&$a, args).unwrap();
-            ::liquid_core::compiler::Filter::evaluate(&*filter, &$b, &context).unwrap()
-        }};
-    }
-
-    macro_rules! failed {
-        ($a:ident, $b:expr) => {{
-            failed!($a, $b, )
-        }};
-        ($a:ident, $b:expr, $($c:expr),*) => {{
-            let positional = Box::new(vec![$(::liquid_core::interpreter::Expression::Literal($c)),*].into_iter());
-            let keyword = Box::new(Vec::new().into_iter());
-            let args = ::liquid_core::compiler::FilterArguments { positional, keyword };
-
-            let context = ::liquid_core::interpreter::Context::default();
-
-            ::liquid_core::compiler::ParseFilter::parse(&$a, args)
-                .and_then(|filter| ::liquid_core::compiler::Filter::evaluate(&*filter, &$b, &context))
-                .unwrap_err()
-        }};
-    }
-
-    macro_rules! tos {
-        ($a:expr) => {{
-            Value::scalar($a.to_owned())
-        }};
-    }
-
     #[test]
     fn unit_concat_nothing() {
-        let input = Value::Array(vec![Value::scalar(1f64), Value::scalar(2f64)]);
-        let result = Value::Array(vec![Value::scalar(1f64), Value::scalar(2f64)]);
-        assert_eq!(unit!(Concat, input, Value::Array(vec![])), result);
+        let input = liquid_core::value!([1f64, 2f64]);
+        let result = liquid_core::value!([1f64, 2f64]);
+        assert_eq!(
+            liquid_core::call_filter!(Concat, input, liquid_core::value!([])).unwrap(),
+            result
+        );
     }
 
     #[test]
     fn unit_concat_something() {
-        let input = Value::Array(vec![Value::scalar(1f64), Value::scalar(2f64)]);
-        let result = Value::Array(vec![
-            Value::scalar(1f64),
-            Value::scalar(2f64),
-            Value::scalar(3f64),
-            Value::scalar(4f64),
-        ]);
+        let input = liquid_core::value!([1f64, 2f64]);
+        let result = liquid_core::value!([1f64, 2f64, 3f64, 4f64]);
         assert_eq!(
-            unit!(
-                Concat,
-                input,
-                Value::Array(vec![Value::scalar(3f64), Value::scalar(4f64)])
-            ),
+            liquid_core::call_filter!(Concat, input, liquid_core::value!([3f64, 4f64])).unwrap(),
             result
         );
     }
 
     #[test]
     fn unit_concat_mixed() {
-        let input = Value::Array(vec![Value::scalar(1f64), Value::scalar(2f64)]);
-        let result = Value::Array(vec![
-            Value::scalar(1f64),
-            Value::scalar(2f64),
-            Value::scalar(3f64),
-            Value::scalar("a"),
-        ]);
+        let input = liquid_core::value!([1f64, 2f64]);
+        let result = liquid_core::value!([1f64, 2f64, 3f64, "a"]);
         assert_eq!(
-            unit!(
-                Concat,
-                input,
-                Value::Array(vec![Value::scalar(3f64), Value::scalar("a")])
-            ),
+            liquid_core::call_filter!(Concat, input, liquid_core::value!([3f64, "a"])).unwrap(),
             result
         );
     }
 
     #[test]
     fn unit_concat_wrong_type() {
-        let input = Value::Array(vec![Value::scalar(1f64), Value::scalar(2f64)]);
-        failed!(Concat, input, Value::scalar(1f64));
+        let input = liquid_core::value!([1f64, 2f64]);
+        liquid_core::call_filter!(Concat, input, 1f64).unwrap_err();
     }
 
     #[test]
     fn unit_concat_no_args() {
-        let input = Value::Array(vec![Value::scalar(1f64), Value::scalar(2f64)]);
-        failed!(Concat, input);
+        let input = liquid_core::value!([1f64, 2f64]);
+        liquid_core::call_filter!(Concat, input).unwrap_err();
     }
 
     #[test]
     fn unit_concat_extra_args() {
-        let input = Value::Array(vec![Value::scalar(1f64), Value::scalar(2f64)]);
-        failed!(
-            Concat,
-            input,
-            Value::Array(vec![Value::scalar(3f64), Value::scalar("a")]),
-            Value::scalar(2f64)
-        );
+        let input = liquid_core::value!([1f64, 2f64]);
+        liquid_core::call_filter!(Concat, input, liquid_core::value!([3f64, "a"]), 2f64)
+            .unwrap_err();
     }
 
     #[test]
     fn unit_first() {
         assert_eq!(
-            unit!(
-                First,
-                Value::Array(vec![
-                    Value::scalar(0f64),
-                    Value::scalar(1f64),
-                    Value::scalar(2f64),
-                    Value::scalar(3f64),
-                    Value::scalar(4f64),
-                ])
-            ),
-            Value::scalar(0f64)
+            liquid_core::call_filter!(First, liquid_core::value!([0f64, 1f64, 2f64, 3f64, 4f64,]))
+                .unwrap(),
+            0f64
         );
         assert_eq!(
-            unit!(First, Value::Array(vec![tos!("test"), tos!("two")])),
-            tos!("test")
+            liquid_core::call_filter!(First, liquid_core::value!(["test", "two"])).unwrap(),
+            liquid_core::value!("test")
+        );
+        assert_eq!(
+            liquid_core::call_filter!(First, liquid_core::value!([])).unwrap(),
+            Value::Nil
         );
-        assert_eq!(unit!(First, Value::Array(vec![])), Value::Nil);
     }
 
     #[test]
     fn unit_join() {
-        let input = Value::Array(vec![tos!("a"), tos!("b"), tos!("c")]);
-        assert_eq!(unit!(Join, input, tos!(",")), tos!("a,b,c"));
+        let input = liquid_core::value!(["a", "b", "c"]);
+        assert_eq!(
+            liquid_core::call_filter!(Join, input, ",").unwrap(),
+            liquid_core::value!("a,b,c")
+        );
     }
 
     #[test]
     fn unit_join_bad_input() {
-        let input = tos!("a");
-        failed!(Join, input, tos!(","));
+        let input = "a";
+        liquid_core::call_filter!(Join, input, ",").unwrap_err();
     }
 
     #[test]
     fn unit_join_bad_join_string() {
-        let input = Value::Array(vec![tos!("a"), tos!("b"), tos!("c")]);
-        assert_eq!(unit!(Join, input, Value::scalar(1f64)), tos!("a1b1c"));
+        let input = liquid_core::value!(["a", "b", "c"]);
+        assert_eq!(
+            liquid_core::call_filter!(Join, input, 1f64).unwrap(),
+            "a1b1c"
+        );
     }
 
     #[test]
     fn unit_join_no_args() {
-        let input = Value::Array(vec![tos!("a"), tos!("b"), tos!("c")]);
-        assert_eq!(unit!(Join, input), tos!("a b c"));
+        let input = liquid_core::value!(["a", "b", "c"]);
+        assert_eq!(liquid_core::call_filter!(Join, input).unwrap(), "a b c");
     }
 
     #[test]
     fn unit_join_non_string_element() {
-        let input = Value::Array(vec![tos!("a"), Value::scalar(1f64), tos!("c")]);
-        assert_eq!(unit!(Join, input, tos!(",")), tos!("a,1,c"));
+        let input = liquid_core::value!(["a", 1f64, "c"]);
+        assert_eq!(
+            liquid_core::call_filter!(Join, input, ",").unwrap(),
+            liquid_core::value!("a,1,c")
+        );
     }
 
     #[test]
     fn unit_sort() {
-        let input = &Value::Array(vec![tos!("Z"), tos!("b"), tos!("c"), tos!("a")]);
-        let desired_result = Value::Array(vec![tos!("Z"), tos!("a"), tos!("b"), tos!("c")]);
-        assert_eq!(unit!(Sort, input), desired_result);
+        let input = &liquid_core::value!(["Z", "b", "c", "a"]);
+        let desired_result = liquid_core::value!(["Z", "a", "b", "c"]);
+        assert_eq!(
+            liquid_core::call_filter!(Sort, input).unwrap(),
+            desired_result
+        );
     }
 
     #[test]
     fn unit_sort_natural() {
-        let input = &Value::Array(vec![tos!("Z"), tos!("b"), tos!("c"), tos!("a")]);
-        let desired_result = Value::Array(vec![tos!("a"), tos!("b"), tos!("c"), tos!("Z")]);
-        assert_eq!(unit!(SortNatural, input), desired_result);
+        let input = &liquid_core::value!(["Z", "b", "c", "a"]);
+        let desired_result = liquid_core::value!(["a", "b", "c", "Z"]);
+        assert_eq!(
+            liquid_core::call_filter!(SortNatural, input).unwrap(),
+            desired_result
+        );
     }
 
     #[test]
     fn unit_last() {
         assert_eq!(
-            unit!(
-                Last,
-                Value::Array(vec![
-                    Value::scalar(0f64),
-                    Value::scalar(1f64),
-                    Value::scalar(2f64),
-                    Value::scalar(3f64),
-                    Value::scalar(4f64),
-                ])
-            ),
-            Value::scalar(4f64)
+            liquid_core::call_filter!(Last, liquid_core::value!([0f64, 1f64, 2f64, 3f64, 4f64,]))
+                .unwrap(),
+            4f64
+        );
+        assert_eq!(
+            liquid_core::call_filter!(Last, liquid_core::value!(["test", "last"])).unwrap(),
+            liquid_core::value!("last")
         );
         assert_eq!(
-            unit!(Last, Value::Array(vec![tos!("test"), tos!("last")])),
-            tos!("last")
+            liquid_core::call_filter!(Last, liquid_core::value!([])).unwrap(),
+            Value::Nil
         );
-        assert_eq!(unit!(Last, Value::Array(vec![])), Value::Nil);
     }
 
     #[test]
     fn unit_reverse_apples_oranges_peaches_plums() {
         // First example from https://shopify.github.io/liquid/filters/reverse/
-        let input = &Value::Array(vec![
-            tos!("apples"),
-            tos!("oranges"),
-            tos!("peaches"),
-            tos!("plums"),
-        ]);
-        let desired_result = Value::Array(vec![
-            tos!("plums"),
-            tos!("peaches"),
-            tos!("oranges"),
-            tos!("apples"),
-        ]);
-        assert_eq!(unit!(Reverse, input), desired_result);
+        let input = liquid_core::value!(["apples", "oranges", "peaches", "plums"]);
+        let desired_result = liquid_core::value!(["plums", "peaches", "oranges", "apples"]);
+        assert_eq!(
+            liquid_core::call_filter!(Reverse, input).unwrap(),
+            desired_result
+        );
     }
 
     #[test]
     fn unit_reverse_array() {
-        let input = &Value::Array(vec![
-            Value::scalar(3f64),
-            Value::scalar(1f64),
-            Value::scalar(2f64),
-        ]);
-        let desired_result = Value::Array(vec![
-            Value::scalar(2f64),
-            Value::scalar(1f64),
-            Value::scalar(3f64),
-        ]);
-        assert_eq!(unit!(Reverse, input), desired_result);
+        let input = liquid_core::value!([3f64, 1f64, 2f64]);
+        let desired_result = liquid_core::value!([2f64, 1f64, 3f64]);
+        assert_eq!(
+            liquid_core::call_filter!(Reverse, input).unwrap(),
+            desired_result
+        );
     }
 
     #[test]
     fn unit_reverse_array_extra_args() {
-        let input = &Value::Array(vec![
-            Value::scalar(3f64),
-            Value::scalar(1f64),
-            Value::scalar(2f64),
-        ]);
-        failed!(Reverse, input, Value::scalar(0f64));
+        let input = liquid_core::value!([3f64, 1f64, 2f64]);
+        liquid_core::call_filter!(Reverse, input, 0f64).unwrap_err();
     }
 
     #[test]
     fn unit_reverse_ground_control_major_tom() {
         // Second example from https://shopify.github.io/liquid/filters/reverse/
-        let input = &Value::Array(vec![
-            tos!("G"),
-            tos!("r"),
-            tos!("o"),
-            tos!("u"),
-            tos!("n"),
-            tos!("d"),
-            tos!(" "),
-            tos!("c"),
-            tos!("o"),
-            tos!("n"),
-            tos!("t"),
-            tos!("r"),
-            tos!("o"),
-            tos!("l"),
-            tos!(" "),
-            tos!("t"),
-            tos!("o"),
-            tos!(" "),
-            tos!("M"),
-            tos!("a"),
-            tos!("j"),
-            tos!("o"),
-            tos!("r"),
-            tos!(" "),
-            tos!("T"),
-            tos!("o"),
-            tos!("m"),
-            tos!("."),
+        let input = liquid_core::value!([
+            "G", "r", "o", "u", "n", "d", " ", "c", "o", "n", "t", "r", "o", "l", " ", "t", "o",
+            " ", "M", "a", "j", "o", "r", " ", "T", "o", "m", ".",
         ]);
-        let desired_result = Value::Array(vec![
-            tos!("."),
-            tos!("m"),
-            tos!("o"),
-            tos!("T"),
-            tos!(" "),
-            tos!("r"),
-            tos!("o"),
-            tos!("j"),
-            tos!("a"),
-            tos!("M"),
-            tos!(" "),
-            tos!("o"),
-            tos!("t"),
-            tos!(" "),
-            tos!("l"),
-            tos!("o"),
-            tos!("r"),
-            tos!("t"),
-            tos!("n"),
-            tos!("o"),
-            tos!("c"),
-            tos!(" "),
-            tos!("d"),
-            tos!("n"),
-            tos!("u"),
-            tos!("o"),
-            tos!("r"),
-            tos!("G"),
+        let desired_result = liquid_core::value!([
+            ".", "m", "o", "T", " ", "r", "o", "j", "a", "M", " ", "o", "t", " ", "l", "o", "r",
+            "t", "n", "o", "c", " ", "d", "n", "u", "o", "r", "G",
         ]);
-        assert_eq!(unit!(Reverse, input), desired_result);
+        assert_eq!(
+            liquid_core::call_filter!(Reverse, input).unwrap(),
+            desired_result
+        );
     }
 
     #[test]
     fn unit_reverse_string() {
-        let input = &tos!("abc");
-        failed!(Reverse, input);
+        let input = "abc";
+        liquid_core::call_filter!(Reverse, input).unwrap_err();
     }
 
     #[test]
     fn unit_uniq() {
-        let input = &Value::Array(vec![tos!("a"), tos!("b"), tos!("a")]);
-        let desired_result = Value::Array(vec![tos!("a"), tos!("b")]);
-        assert_eq!(unit!(Uniq, input), desired_result);
+        let input = liquid_core::value!(["a", "b", "a"]);
+        let desired_result = liquid_core::value!(["a", "b"]);
+        assert_eq!(
+            liquid_core::call_filter!(Uniq, input).unwrap(),
+            desired_result
+        );
     }
 
     #[test]
     fn unit_uniq_non_array() {
-        let input = &Value::scalar(0f64);
-        failed!(Uniq, input);
+        let input = 0f64;
+        liquid_core::call_filter!(Uniq, input).unwrap_err();
     }
 
     #[test]
     fn unit_uniq_one_argument() {
-        let input = &Value::Array(vec![tos!("a"), tos!("b"), tos!("a")]);
-        failed!(Uniq, input, Value::scalar(0f64));
+        let input = liquid_core::value!(["a", "b", "a"]);
+        liquid_core::call_filter!(Uniq, input, 0f64).unwrap_err();
     }
 
     #[test]
     fn unit_uniq_shopify_liquid() {
         // Test from https://shopify.github.io/liquid/filters/uniq/
-        let input = &Value::Array(vec![
-            tos!("ants"),
-            tos!("bugs"),
-            tos!("bees"),
-            tos!("bugs"),
-            tos!("ants"),
-        ]);
-        let desired_result = Value::Array(vec![tos!("ants"), tos!("bugs"), tos!("bees")]);
-        assert_eq!(unit!(Uniq, input), desired_result);
+        let input = liquid_core::value!(["ants", "bugs", "bees", "bugs", "ants",]);
+        let desired_result = liquid_core::value!(["ants", "bugs", "bees"]);
+        assert_eq!(
+            liquid_core::call_filter!(Uniq, input).unwrap(),
+            desired_result
+        );
     }
 }
diff --git a/src/filters/std/date.rs b/src/filters/std/date.rs
index fecd3d9..f107479 100644
--- a/src/filters/std/date.rs
+++ b/src/filters/std/date.rs
@@ -29,7 +29,7 @@ struct DateFilter {
 }
 
 impl Filter for DateFilter {
-    fn evaluate(&self, input: &Value, context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, input: &dyn ValueView, context: &Context<'_>) -> Result<Value> {
         let args = self.args.evaluate(context)?;
 
         let date = input.as_scalar().and_then(|s| s.to_date_time());
@@ -37,75 +37,35 @@ impl Filter for DateFilter {
             Some(date) if !args.format.is_empty() => {
                 Ok(Value::scalar(date.format(args.format.as_str()).to_string()))
             }
-            _ => Ok(input.clone()),
+            _ => Ok(input.to_value()),
         }
     }
 }
 
 #[cfg(test)]
 mod tests {
-
     use super::*;
 
-    macro_rules! unit {
-        ($a:ident, $b:expr) => {{
-            unit!($a, $b, )
-        }};
-        ($a:ident, $b:expr, $($c:expr),*) => {{
-            let positional = Box::new(vec![$(::liquid_core::interpreter::Expression::Literal($c)),*].into_iter());
-            let keyword = Box::new(Vec::new().into_iter());
-            let args = ::liquid_core::compiler::FilterArguments { positional, keyword };
-
-            let context = ::liquid_core::interpreter::Context::default();
-
-            let filter = ::liquid_core::compiler::ParseFilter::parse(&$a, args).unwrap();
-            ::liquid_core::compiler::Filter::evaluate(&*filter, &$b, &context).unwrap()
-        }};
-    }
-
-    macro_rules! failed {
-        ($a:ident, $b:expr) => {{
-            failed!($a, $b, )
-        }};
-        ($a:ident, $b:expr, $($c:expr),*) => {{
-            let positional = Box::new(vec![$(::liquid_core::interpreter::Expression::Literal($c)),*].into_iter());
-            let keyword = Box::new(Vec::new().into_iter());
-            let args = ::liquid_core::compiler::FilterArguments { positional, keyword };
-
-            let context = ::liquid_core::interpreter::Context::default();
-
-            ::liquid_core::compiler::ParseFilter::parse(&$a, args)
-                .and_then(|filter| ::liquid_core::compiler::Filter::evaluate(&*filter, &$b, &context))
-                .unwrap_err()
-        }};
-    }
-
-    macro_rules! tos {
-        ($a:expr) => {{
-            Value::scalar($a.to_owned())
-        }};
-    }
-
     #[test]
     fn unit_date() {
         assert_eq!(
-            unit!(Date, tos!("13 Jun 2016 02:30:00 +0300"), tos!("%Y-%m-%d")),
-            tos!("2016-06-13")
+            liquid_core::call_filter!(Date, "13 Jun 2016 02:30:00 +0300", "%Y-%m-%d").unwrap(),
+            liquid_core::value!("2016-06-13")
         );
     }
 
     #[test]
     fn unit_date_cobalt_format() {
         assert_eq!(
-            unit!(Date, tos!("2016-06-13 02:30:00 +0300"), tos!("%Y-%m-%d")),
-            tos!("2016-06-13")
+            liquid_core::call_filter!(Date, "2016-06-13 02:30:00 +0300", "%Y-%m-%d").unwrap(),
+            liquid_core::value!("2016-06-13")
         );
     }
 
     #[test]
     fn unit_date_bad_input_type() {
         assert_eq!(
-            unit!(Date, Value::scalar(0f64), tos!("%Y-%m-%d")),
+            liquid_core::call_filter!(Date, 0f64, "%Y-%m-%d").unwrap(),
             Value::scalar(0f64)
         );
     }
@@ -113,47 +73,34 @@ mod tests {
     #[test]
     fn unit_date_bad_input_format() {
         assert_eq!(
-            unit!(Date, tos!("blah blah blah"), tos!("%Y-%m-%d")),
-            tos!("blah blah blah")
+            liquid_core::call_filter!(Date, "blah blah blah", "%Y-%m-%d").unwrap(),
+            liquid_core::value!("blah blah blah")
         );
     }
 
     #[test]
     fn unit_date_format_empty() {
         assert_eq!(
-            unit!(
-                Date,
-                tos!("13 Jun 2016 02:30:00 +0300"),
-                Value::scalar("".to_owned())
-            ),
-            tos!("13 Jun 2016 02:30:00 +0300")
+            liquid_core::call_filter!(Date, "13 Jun 2016 02:30:00 +0300", "").unwrap(),
+            liquid_core::value!("13 Jun 2016 02:30:00 +0300")
         );
     }
 
     #[test]
     fn unit_date_bad_format_type() {
         assert_eq!(
-            unit!(
-                Date,
-                tos!("13 Jun 2016 02:30:00 +0300"),
-                Value::scalar(0f64)
-            ),
-            tos!("0")
+            liquid_core::call_filter!(Date, "13 Jun 2016 02:30:00 +0300", 0f64).unwrap(),
+            liquid_core::value!("0")
         );
     }
 
     #[test]
     fn unit_date_missing_format() {
-        failed!(Date, tos!("13 Jun 2016 02:30:00 +0300"));
+        liquid_core::call_filter!(Date, "13 Jun 2016 02:30:00 +0300").unwrap_err();
     }
 
     #[test]
     fn unit_date_extra_param() {
-        failed!(
-            Date,
-            tos!("13 Jun 2016 02:30:00 +0300"),
-            Value::scalar(0f64),
-            Value::scalar(1f64)
-        );
+        liquid_core::call_filter!(Date, "13 Jun 2016 02:30:00 +0300", 0f64, 1f64).unwrap_err();
     }
 }
diff --git a/src/filters/std/html.rs b/src/filters/std/html.rs
index 7fa46ca..58bbaa0 100644
--- a/src/filters/std/html.rs
+++ b/src/filters/std/html.rs
@@ -17,7 +17,7 @@ fn nr_escaped(text: &str) -> usize {
 // The code is adapted from
 // https://github.com/rust-lang/rust/blob/master/src/librustdoc/html/escape.rs
 // Retrieved 2016-11-19.
-fn escape(input: &Value, once_p: bool) -> Result<Value> {
+fn escape(input: &dyn ValueView, once_p: bool) -> Result<Value> {
     if input.is_nil() {
         return Ok(Value::Nil);
     }
@@ -75,7 +75,7 @@ pub struct Escape;
 struct EscapeFilter;
 
 impl Filter for EscapeFilter {
-    fn evaluate(&self, input: &Value, _context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, input: &dyn ValueView, _context: &Context<'_>) -> Result<Value> {
         escape(input, false)
     }
 }
@@ -93,7 +93,7 @@ pub struct EscapeOnce;
 struct EscapeOnceFilter;
 
 impl Filter for EscapeOnceFilter {
-    fn evaluate(&self, input: &Value, _context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, input: &dyn ValueView, _context: &Context<'_>) -> Result<Value> {
         escape(input, true)
     }
 }
@@ -120,7 +120,7 @@ static MATCHERS: once_cell::sync::Lazy<[Regex; 4]> = once_cell::sync::Lazy::new(
 });
 
 impl Filter for StripHtmlFilter {
-    fn evaluate(&self, input: &Value, _context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, input: &dyn ValueView, _context: &Context<'_>) -> Result<Value> {
         let input = input.to_kstr().into_string();
 
         let result = MATCHERS.iter().fold(input, |acc, matcher| {
@@ -143,7 +143,7 @@ pub struct NewlineToBr;
 struct NewlineToBrFilter;
 
 impl Filter for NewlineToBrFilter {
-    fn evaluate(&self, input: &Value, _context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, input: &dyn ValueView, _context: &Context<'_>) -> Result<Value> {
         // TODO handle windows line endings
         let input = input.to_kstr();
         Ok(Value::scalar(input.replace("\n", "<br />\n")))
@@ -152,138 +152,108 @@ impl Filter for NewlineToBrFilter {
 
 #[cfg(test)]
 mod tests {
-
     use super::*;
 
-    macro_rules! unit {
-        ($a:ident, $b:expr) => {{
-            unit!($a, $b, )
-        }};
-        ($a:ident, $b:expr, $($c:expr),*) => {{
-            let positional = Box::new(vec![$(::liquid_core::interpreter::Expression::Literal($c)),*].into_iter());
-            let keyword = Box::new(Vec::new().into_iter());
-            let args = ::liquid_core::compiler::FilterArguments { positional, keyword };
-
-            let context = ::liquid_core::interpreter::Context::default();
-
-            let filter = ::liquid_core::compiler::ParseFilter::parse(&$a, args).unwrap();
-            ::liquid_core::compiler::Filter::evaluate(&*filter, &$b, &context).unwrap()
-        }};
-    }
-
-    macro_rules! failed {
-        ($a:ident, $b:expr) => {{
-            failed!($a, $b, )
-        }};
-        ($a:ident, $b:expr, $($c:expr),*) => {{
-            let positional = Box::new(vec![$(::liquid_core::interpreter::Expression::Literal($c)),*].into_iter());
-            let keyword = Box::new(Vec::new().into_iter());
-            let args = ::liquid_core::compiler::FilterArguments { positional, keyword };
-
-            let context = ::liquid_core::interpreter::Context::default();
-
-            ::liquid_core::compiler::ParseFilter::parse(&$a, args)
-                .and_then(|filter| ::liquid_core::compiler::Filter::evaluate(&*filter, &$b, &context))
-                .unwrap_err()
-        }};
-    }
-
-    macro_rules! tos {
-        ($a:expr) => {{
-            Value::scalar($a.to_owned())
-        }};
-    }
-
     #[test]
     fn unit_escape() {
         assert_eq!(
-            unit!(Escape, tos!("Have you read 'James & the Giant Peach'?")),
-            tos!("Have you read &#39;James &amp; the Giant Peach&#39;?")
+            liquid_core::call_filter!(Escape, "Have you read 'James & the Giant Peach'?").unwrap(),
+            liquid_core::value!("Have you read &#39;James &amp; the Giant Peach&#39;?")
         );
         assert_eq!(
-            unit!(Escape, tos!("Tetsuro Takara")),
-            tos!("Tetsuro Takara")
+            liquid_core::call_filter!(Escape, "Tetsuro Takara").unwrap(),
+            liquid_core::value!("Tetsuro Takara")
         );
     }
 
     #[test]
     fn unit_escape_non_ascii() {
         assert_eq!(
-            unit!(Escape, tos!("word¹ <br> word¹")),
-            tos!("word¹ &lt;br&gt; word¹")
+            liquid_core::call_filter!(Escape, "word¹ <br> word¹").unwrap(),
+            liquid_core::value!("word¹ &lt;br&gt; word¹")
         );
     }
 
     #[test]
     fn unit_escape_once() {
         assert_eq!(
-            unit!(EscapeOnce, tos!("1 < 2 & 3")),
-            tos!("1 &lt; 2 &amp; 3")
+            liquid_core::call_filter!(EscapeOnce, "1 < 2 & 3").unwrap(),
+            liquid_core::value!("1 &lt; 2 &amp; 3")
         );
         assert_eq!(
-            unit!(EscapeOnce, tos!("1 &lt; 2 &amp; 3")),
-            tos!("1 &lt; 2 &amp; 3")
+            liquid_core::call_filter!(EscapeOnce, "1 &lt; 2 &amp; 3").unwrap(),
+            liquid_core::value!("1 &lt; 2 &amp; 3")
         );
         assert_eq!(
-            unit!(EscapeOnce, tos!("&lt;&gt;&amp;&#39;&quot;&xyz;")),
-            tos!("&lt;&gt;&amp;&#39;&quot;&amp;xyz;")
+            liquid_core::call_filter!(EscapeOnce, "&lt;&gt;&amp;&#39;&quot;&xyz;").unwrap(),
+            liquid_core::value!("&lt;&gt;&amp;&#39;&quot;&amp;xyz;")
         );
     }
 
     #[test]
     fn unit_strip_html() {
         assert_eq!(
-            unit!(
+            liquid_core::call_filter!(
                 StripHtml,
-                tos!("<script type=\"text/javascript\">alert('Hi!');</script>"),
-            ),
-            tos!("")
+                "<script type=\"text/javascript\">alert('Hi!';</script>",
+            )
+            .unwrap(),
+            liquid_core::value!("")
         );
         assert_eq!(
-            unit!(
+            liquid_core::call_filter!(
                 StripHtml,
-                tos!("<SCRIPT type=\"text/javascript\">alert('Hi!');</SCRIPT>"),
-            ),
-            tos!("")
+                "<SCRIPT type=\"text/javascript\">alert('Hi!';</SCRIPT>",
+            )
+            .unwrap(),
+            liquid_core::value!("")
         );
-        assert_eq!(unit!(StripHtml, tos!("<p>test</p>")), tos!("test"));
-        assert_eq!(unit!(StripHtml, tos!("<p id='xxx'>test</p>")), tos!("test"));
         assert_eq!(
-            unit!(
-                StripHtml,
-                tos!("<style type=\"text/css\">cool style</style>"),
-            ),
-            tos!("")
+            liquid_core::call_filter!(StripHtml, "<p>test</p>").unwrap(),
+            liquid_core::value!("test")
+        );
+        assert_eq!(
+            liquid_core::call_filter!(StripHtml, "<p id='xxx'>test</p>").unwrap(),
+            liquid_core::value!("test")
         );
         assert_eq!(
-            unit!(StripHtml, tos!("<p\nclass='loooong'>test</p>")),
-            tos!("test")
+            liquid_core::call_filter!(StripHtml, "<style type=\"text/css\">cool style</style>",)
+                .unwrap(),
+            liquid_core::value!("")
         );
         assert_eq!(
-            unit!(StripHtml, tos!("<!--\n\tcomment\n-->test")),
-            tos!("test")
+            liquid_core::call_filter!(StripHtml, "<p\nclass='loooong'>test</p>").unwrap(),
+            liquid_core::value!("test")
+        );
+        assert_eq!(
+            liquid_core::call_filter!(StripHtml, "<!--\n\tcomment\n-->test").unwrap(),
+            liquid_core::value!("test")
+        );
+        assert_eq!(
+            liquid_core::call_filter!(StripHtml, "").unwrap(),
+            liquid_core::value!("")
         );
-        assert_eq!(unit!(StripHtml, tos!("")), tos!(""));
     }
 
     #[test]
     fn unit_newline_to_br() {
-        let input = &tos!("a\nb");
-        let desired_result = tos!("a<br />\nb");
-        assert_eq!(unit!(NewlineToBr, input), desired_result);
+        assert_eq!(
+            liquid_core::call_filter!(NewlineToBr, "a\nb").unwrap(),
+            liquid_core::value!("a<br />\nb")
+        );
     }
 
     #[test]
     fn unit_newline_to_br_hello_world() {
         // First example from https://shopify.github.io/liquid/filters/newline_to_br/
-        let input = &tos!("\nHello\nWorld\n");
-        let desired_result = tos!("<br />\nHello<br />\nWorld<br />\n");
-        assert_eq!(unit!(NewlineToBr, input), desired_result);
+        assert_eq!(
+            liquid_core::call_filter!(NewlineToBr, "\nHello\nWorld\n").unwrap(),
+            liquid_core::value!("<br />\nHello<br />\nWorld<br />\n")
+        );
     }
 
     #[test]
     fn unit_newline_to_br_one_argument() {
-        let input = &tos!("a\nb");
-        failed!(NewlineToBr, input, Value::scalar(0f64));
+        liquid_core::call_filter!(NewlineToBr, "a\nb", 0f64).unwrap_err();
     }
 }
diff --git a/src/filters/std/math.rs b/src/filters/std/math.rs
index 1a9392d..1f30df0 100644
--- a/src/filters/std/math.rs
+++ b/src/filters/std/math.rs
@@ -21,15 +21,15 @@ pub struct Abs;
 struct AbsFilter;
 
 impl Filter for AbsFilter {
-    fn evaluate(&self, input: &Value, _context: &Context<'_>) -> Result<Value> {
-        match *input {
-            Value::Scalar(ref s) => s
-                .to_integer()
-                .map(|i| Value::scalar(i.abs()))
-                .or_else(|| s.to_float().map(|i| Value::scalar(i.abs())))
-                .ok_or_else(|| invalid_input("Number expected")),
-            _ => invalid_input("Number expected").into_err(),
-        }
+    fn evaluate(&self, input: &dyn ValueView, _context: &Context<'_>) -> Result<Value> {
+        let input = input
+            .as_scalar()
+            .ok_or_else(|| invalid_input("Number expected"))?;
+        input
+            .to_integer()
+            .map(|i| Value::scalar(i.abs()))
+            .or_else(|| input.to_float().map(|i| Value::scalar(i.abs())))
+            .ok_or_else(|| invalid_input("Number expected"))
     }
 }
 
@@ -56,7 +56,7 @@ struct AtLeastFilter {
 }
 
 impl Filter for AtLeastFilter {
-    fn evaluate(&self, input: &Value, context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, input: &dyn ValueView, context: &Context<'_>) -> Result<Value> {
         let args = self.args.evaluate(context)?;
 
         let input = input
@@ -105,7 +105,7 @@ struct AtMostFilter {
 }
 
 impl Filter for AtMostFilter {
-    fn evaluate(&self, input: &Value, context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, input: &dyn ValueView, context: &Context<'_>) -> Result<Value> {
         let args = self.args.evaluate(context)?;
 
         let input = input
@@ -154,7 +154,7 @@ struct PlusFilter {
 }
 
 impl Filter for PlusFilter {
-    fn evaluate(&self, input: &Value, context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, input: &dyn ValueView, context: &Context<'_>) -> Result<Value> {
         let args = self.args.evaluate(context)?;
 
         let input = input
@@ -203,7 +203,7 @@ struct MinusFilter {
 }
 
 impl Filter for MinusFilter {
-    fn evaluate(&self, input: &Value, context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, input: &dyn ValueView, context: &Context<'_>) -> Result<Value> {
         let args = self.args.evaluate(context)?;
 
         let input = input
@@ -252,7 +252,7 @@ struct TimesFilter {
 }
 
 impl Filter for TimesFilter {
-    fn evaluate(&self, input: &Value, context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, input: &dyn ValueView, context: &Context<'_>) -> Result<Value> {
         let args = self.args.evaluate(context)?;
 
         let input = input
@@ -301,7 +301,7 @@ struct DividedByFilter {
 }
 
 impl Filter for DividedByFilter {
-    fn evaluate(&self, input: &Value, context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, input: &dyn ValueView, context: &Context<'_>) -> Result<Value> {
         let args = self.args.evaluate(context)?;
 
         let input = input
@@ -350,7 +350,7 @@ struct ModuloFilter {
 }
 
 impl Filter for ModuloFilter {
-    fn evaluate(&self, input: &Value, context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, input: &dyn ValueView, context: &Context<'_>) -> Result<Value> {
         let args = self.args.evaluate(context)?;
 
         let input = input
@@ -402,7 +402,7 @@ struct RoundFilter {
 }
 
 impl Filter for RoundFilter {
-    fn evaluate(&self, input: &Value, context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, input: &dyn ValueView, context: &Context<'_>) -> Result<Value> {
         let args = self.args.evaluate(context)?;
 
         let n = args.decimal_places.unwrap_or(0);
@@ -439,7 +439,7 @@ pub struct Ceil;
 struct CeilFilter;
 
 impl Filter for CeilFilter {
-    fn evaluate(&self, input: &Value, _context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, input: &dyn ValueView, _context: &Context<'_>) -> Result<Value> {
         let n = input
             .as_scalar()
             .and_then(|s| s.to_float())
@@ -461,7 +461,7 @@ pub struct Floor;
 struct FloorFilter;
 
 impl Filter for FloorFilter {
-    fn evaluate(&self, input: &Value, _context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, input: &dyn ValueView, _context: &Context<'_>) -> Result<Value> {
         let n = input
             .as_scalar()
             .and_then(|s| s.to_float())
@@ -472,127 +472,96 @@ impl Filter for FloorFilter {
 
 #[cfg(test)]
 mod tests {
-
     use super::*;
 
-    macro_rules! unit {
-        ($a:ident, $b:expr) => {{
-            unit!($a, $b, )
-        }};
-        ($a:ident, $b:expr, $($c:expr),*) => {{
-            let positional = Box::new(vec![$(::liquid_core::interpreter::Expression::Literal($c)),*].into_iter());
-            let keyword = Box::new(Vec::new().into_iter());
-            let args = ::liquid_core::compiler::FilterArguments { positional, keyword };
-
-            let context = ::liquid_core::interpreter::Context::default();
-
-            let filter = ::liquid_core::compiler::ParseFilter::parse(&$a, args).unwrap();
-            ::liquid_core::compiler::Filter::evaluate(&*filter, &$b, &context).unwrap()
-        }};
-    }
-
-    macro_rules! failed {
-        ($a:ident, $b:expr) => {{
-            failed!($a, $b, )
-        }};
-        ($a:ident, $b:expr, $($c:expr),*) => {{
-            let positional = Box::new(vec![$(::liquid_core::interpreter::Expression::Literal($c)),*].into_iter());
-            let keyword = Box::new(Vec::new().into_iter());
-            let args = ::liquid_core::compiler::FilterArguments { positional, keyword };
-
-            let context = ::liquid_core::interpreter::Context::default();
-
-            ::liquid_core::compiler::ParseFilter::parse(&$a, args)
-                .and_then(|filter| ::liquid_core::compiler::Filter::evaluate(&*filter, &$b, &context))
-                .unwrap_err()
-        }};
-    }
-
-    macro_rules! tos {
-        ($a:expr) => {{
-            Value::scalar($a.to_owned())
-        }};
-    }
-
     #[test]
     fn unit_abs() {
-        let input = Value::scalar(-1f64);
-        let desired_result = Value::scalar(1f64);
-        assert_eq!(unit!(Abs, input), desired_result);
+        assert_eq!(
+            liquid_core::call_filter!(Abs, -1f64).unwrap(),
+            Value::scalar(1f64)
+        );
     }
 
     #[test]
     fn unit_abs_positive_in_string() {
-        let input = &tos!("42");
-        let desired_result = Value::scalar(42f64);
-        assert_eq!(unit!(Abs, input), desired_result);
+        assert_eq!(
+            liquid_core::call_filter!(Abs, "42").unwrap(),
+            Value::scalar(42f64)
+        );
     }
 
     #[test]
     fn unit_abs_not_number_or_string() {
-        let input = &Value::scalar(true);
-        failed!(Abs, input);
+        liquid_core::call_filter!(Abs, true).unwrap_err();
     }
 
     #[test]
     fn unit_abs_one_argument() {
-        let input = &Value::scalar(-1f64);
-        failed!(Abs, input, Value::scalar(0f64));
+        liquid_core::call_filter!(Abs, -1f64, 0f64).unwrap_err();
     }
 
     #[test]
     fn unit_abs_shopify_liquid() {
         // Three tests from https://shopify.github.io/liquid/filters/abs/
-        assert_eq!(unit!(Abs, Value::scalar(-17f64)), Value::scalar(17f64));
-        assert_eq!(unit!(Abs, Value::scalar(4f64)), Value::scalar(4f64));
-        assert_eq!(unit!(Abs, tos!("-19.86")), Value::scalar(19.86f64));
+        assert_eq!(
+            liquid_core::call_filter!(Abs, -17f64).unwrap(),
+            Value::scalar(17f64)
+        );
+        assert_eq!(
+            liquid_core::call_filter!(Abs, 4f64).unwrap(),
+            Value::scalar(4f64)
+        );
+        assert_eq!(
+            liquid_core::call_filter!(Abs, "-19.86").unwrap(),
+            Value::scalar(19.86f64)
+        );
     }
     #[test]
     fn unit_at_least() {
         assert_eq!(
-            unit!(AtLeast, Value::scalar(4f64), Value::scalar(5f64)),
+            liquid_core::call_filter!(AtLeast, 4f64, 5f64).unwrap(),
             Value::scalar(5f64)
         );
         assert_eq!(
-            unit!(AtLeast, Value::scalar(4f64), Value::scalar(3f64)),
+            liquid_core::call_filter!(AtLeast, 4f64, 3f64).unwrap(),
             Value::scalar(4f64)
         );
         assert_eq!(
-            unit!(AtLeast, Value::scalar(21.5), Value::scalar(2.25)),
+            liquid_core::call_filter!(AtLeast, 21.5, 2.25).unwrap(),
             Value::scalar(21.5)
         );
         assert_eq!(
-            unit!(AtLeast, Value::scalar(21.5), Value::scalar(42.25)),
+            liquid_core::call_filter!(AtLeast, 21.5, 42.25).unwrap(),
             Value::scalar(42.25)
         );
     }
     #[test]
     fn unit_at_most() {
         assert_eq!(
-            unit!(AtMost, Value::scalar(4f64), Value::scalar(5f64)),
+            liquid_core::call_filter!(AtMost, 4f64, 5f64).unwrap(),
             Value::scalar(4f64)
         );
         assert_eq!(
-            unit!(AtMost, Value::scalar(4f64), Value::scalar(3f64)),
+            liquid_core::call_filter!(AtMost, 4f64, 3f64).unwrap(),
             Value::scalar(3f64)
         );
         assert_eq!(
-            unit!(AtMost, Value::scalar(21.5), Value::scalar(2.25)),
+            liquid_core::call_filter!(AtMost, 21.5, 2.25).unwrap(),
             Value::scalar(2.25)
         );
         assert_eq!(
-            unit!(AtMost, Value::scalar(21.5), Value::scalar(42.25)),
+            liquid_core::call_filter!(AtMost, 21.5, 42.25).unwrap(),
             Value::scalar(21.5)
         );
     }
     #[test]
     fn unit_plus() {
         assert_eq!(
-            unit!(Plus, Value::scalar(2f64), Value::scalar(1f64)),
+            liquid_core::call_filter!(Plus, 2f64, 1f64).unwrap(),
             Value::scalar(3f64)
         );
         assert_eq!(
-            unit!(Plus, Value::scalar(21.5), Value::scalar(2.25)),
+            liquid_core::call_filter!(Plus, 21.5, 2.25).unwrap(),
             Value::scalar(23.75)
         );
     }
@@ -600,11 +569,11 @@ mod tests {
     #[test]
     fn unit_minus() {
         assert_eq!(
-            unit!(Minus, Value::scalar(2f64), Value::scalar(1f64)),
+            liquid_core::call_filter!(Minus, 2f64, 1f64).unwrap(),
             Value::scalar(1f64)
         );
         assert_eq!(
-            unit!(Minus, Value::scalar(21.5), Value::scalar(1.25)),
+            liquid_core::call_filter!(Minus, 21.5, 1.25).unwrap(),
             Value::scalar(20.25)
         );
     }
@@ -612,34 +581,34 @@ mod tests {
     #[test]
     fn unit_times() {
         assert_eq!(
-            unit!(Times, Value::scalar(2f64), Value::scalar(3f64)),
+            liquid_core::call_filter!(Times, 2f64, 3f64).unwrap(),
             Value::scalar(6f64)
         );
         assert_eq!(
-            unit!(Times, Value::scalar(8.5), Value::scalar(0.5)),
+            liquid_core::call_filter!(Times, 8.5, 0.5).unwrap(),
             Value::scalar(4.25)
         );
-        failed!(Times, Value::scalar(true), Value::scalar(8.5));
-        failed!(Times, Value::scalar(2.5), Value::scalar(true));
-        failed!(Times, Value::scalar(2.5));
+        liquid_core::call_filter!(Times, true, 8.5).unwrap_err();
+        liquid_core::call_filter!(Times, 2.5, true).unwrap_err();
+        liquid_core::call_filter!(Times, 2.5).unwrap_err();
     }
 
     #[test]
     fn unit_modulo() {
         assert_eq!(
-            unit!(Modulo, Value::scalar(3_f64), Value::scalar(2_f64)),
+            liquid_core::call_filter!(Modulo, 3_f64, 2_f64).unwrap(),
             Value::scalar(1_f64)
         );
         assert_eq!(
-            unit!(Modulo, Value::scalar(3_f64), Value::scalar(3.0)),
+            liquid_core::call_filter!(Modulo, 3_f64, 3.0).unwrap(),
             Value::scalar(0_f64)
         );
         assert_eq!(
-            unit!(Modulo, Value::scalar(24_f64), Value::scalar(7_f64)),
+            liquid_core::call_filter!(Modulo, 24_f64, 7_f64).unwrap(),
             Value::scalar(3_f64)
         );
         assert_eq!(
-            unit!(Modulo, Value::scalar(183.357), Value::scalar(12_f64)),
+            liquid_core::call_filter!(Modulo, 183.357, 12_f64).unwrap(),
             Value::scalar(3.3569999999999993)
         );
     }
@@ -647,52 +616,73 @@ mod tests {
     #[test]
     fn unit_divided_by() {
         assert_eq!(
-            unit!(DividedBy, Value::scalar(4f64), Value::scalar(2f64)),
+            liquid_core::call_filter!(DividedBy, 4f64, 2f64).unwrap(),
             Value::scalar(2f64)
         );
         assert_eq!(
-            unit!(DividedBy, Value::scalar(5f64), Value::scalar(2f64)),
+            liquid_core::call_filter!(DividedBy, 5f64, 2f64).unwrap(),
             Value::scalar(2.5f64)
         );
-        failed!(DividedBy, Value::scalar(true), Value::scalar(8.5));
-        failed!(DividedBy, Value::scalar(2.5), Value::scalar(true));
-        failed!(DividedBy, Value::scalar(2.5));
+        liquid_core::call_filter!(DividedBy, true, 8.5).unwrap_err();
+        liquid_core::call_filter!(DividedBy, 2.5, true).unwrap_err();
+        liquid_core::call_filter!(DividedBy, 2.5).unwrap_err();
     }
 
     #[test]
     fn unit_ceil() {
-        assert_eq!(unit!(Ceil, Value::scalar(1.1f64)), Value::scalar(2f64));
-        assert_eq!(unit!(Ceil, Value::scalar(1f64)), Value::scalar(1f64));
-        failed!(Ceil, Value::scalar(true));
+        assert_eq!(
+            liquid_core::call_filter!(Ceil, 1.1f64).unwrap(),
+            Value::scalar(2f64)
+        );
+        assert_eq!(
+            liquid_core::call_filter!(Ceil, 1f64).unwrap(),
+            Value::scalar(1f64)
+        );
+        liquid_core::call_filter!(Ceil, true).unwrap_err();
     }
 
     #[test]
     fn unit_floor() {
-        assert_eq!(unit!(Floor, Value::scalar(1.1f64)), Value::scalar(1f64));
-        assert_eq!(unit!(Floor, Value::scalar(1f64)), Value::scalar(1f64));
-        failed!(Floor, Value::scalar(true));
+        assert_eq!(
+            liquid_core::call_filter!(Floor, 1.1f64).unwrap(),
+            Value::scalar(1f64)
+        );
+        assert_eq!(
+            liquid_core::call_filter!(Floor, 1f64).unwrap(),
+            Value::scalar(1f64)
+        );
+        liquid_core::call_filter!(Floor, true).unwrap_err();
     }
 
     #[test]
     fn unit_round() {
-        assert_eq!(unit!(Round, Value::scalar(1.1f64)), Value::scalar(1i32));
-        assert_eq!(unit!(Round, Value::scalar(1.5f64)), Value::scalar(2i32));
-        assert_eq!(unit!(Round, Value::scalar(2f64)), Value::scalar(2i32));
-        failed!(Round, Value::scalar(true));
+        assert_eq!(
+            liquid_core::call_filter!(Round, 1.1f64).unwrap(),
+            Value::scalar(1i32)
+        );
+        assert_eq!(
+            liquid_core::call_filter!(Round, 1.5f64).unwrap(),
+            Value::scalar(2i32)
+        );
+        assert_eq!(
+            liquid_core::call_filter!(Round, 2f64).unwrap(),
+            Value::scalar(2i32)
+        );
+        liquid_core::call_filter!(Round, true).unwrap_err();
     }
 
     #[test]
     fn unit_round_precision() {
         assert_eq!(
-            unit!(Round, Value::scalar(1.1f64), Value::scalar(0i32)),
+            liquid_core::call_filter!(Round, 1.1f64, 0i32).unwrap(),
             Value::scalar(1f64)
         );
         assert_eq!(
-            unit!(Round, Value::scalar(1.5f64), Value::scalar(1i32)),
+            liquid_core::call_filter!(Round, 1.5f64, 1i32).unwrap(),
             Value::scalar(1.5f64)
         );
         assert_eq!(
-            unit!(Round, Value::scalar(3.14159f64), Value::scalar(3i32)),
+            liquid_core::call_filter!(Round, 3.14159f64, 3i32).unwrap(),
             Value::scalar(3.142f64)
         );
     }
diff --git a/src/filters/std/mod.rs b/src/filters/std/mod.rs
index 4c29854..48f6a1a 100644
--- a/src/filters/std/mod.rs
+++ b/src/filters/std/mod.rs
@@ -43,12 +43,15 @@ pub struct Size;
 struct SizeFilter;
 
 impl Filter for SizeFilter {
-    fn evaluate(&self, input: &Value, _context: &Context<'_>) -> Result<Value> {
-        match *input {
-            Value::Scalar(ref x) => Ok(Value::scalar(x.to_kstr().len() as i32)),
-            Value::Array(ref x) => Ok(Value::scalar(x.len() as i32)),
-            Value::Object(ref x) => Ok(Value::scalar(x.len() as i32)),
-            _ => Ok(Value::scalar(0i32)),
+    fn evaluate(&self, input: &dyn ValueView, _context: &Context<'_>) -> Result<Value> {
+        if let Some(x) = input.as_scalar() {
+            Ok(Value::scalar(x.to_kstr().len() as i32))
+        } else if let Some(x) = input.as_array() {
+            Ok(Value::scalar(x.size()))
+        } else if let Some(x) = input.as_object() {
+            Ok(Value::scalar(x.size()))
+        } else {
+            Ok(Value::scalar(0i32))
         }
     }
 }
@@ -76,54 +79,34 @@ struct DefaultFilter {
 }
 
 impl Filter for DefaultFilter {
-    fn evaluate(&self, input: &Value, context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, input: &dyn ValueView, context: &Context<'_>) -> Result<Value> {
         let args = self.args.evaluate(context)?;
 
         if input.query_state(liquid_core::value::State::DefaultValue) {
             Ok(args.default.to_value())
         } else {
-            Ok(input.clone())
+            Ok(input.to_value())
         }
     }
 }
 
 #[cfg(test)]
 mod tests {
-
     use super::*;
     use liquid_core::Object;
 
-    macro_rules! unit {
-        ($a:ident, $b:expr) => {{
-            unit!($a, $b, )
-        }};
-        ($a:ident, $b:expr, $($c:expr),*) => {{
-            let positional = Box::new(vec![$(::liquid_core::interpreter::Expression::Literal($c)),*].into_iter());
-            let keyword = Box::new(Vec::new().into_iter());
-            let args = ::liquid_core::compiler::FilterArguments { positional, keyword };
-
-            let context = ::liquid_core::interpreter::Context::default();
-
-            let filter = ::liquid_core::compiler::ParseFilter::parse(&$a, args).unwrap();
-            ::liquid_core::compiler::Filter::evaluate(&*filter, &$b, &context).unwrap()
-        }};
-    }
-
-    macro_rules! tos {
-        ($a:expr) => {{
-            Value::scalar($a.to_owned())
-        }};
-    }
-
     #[test]
     fn unit_size() {
-        assert_eq!(unit!(Size, tos!("abc")), Value::scalar(3f64));
         assert_eq!(
-            unit!(Size, tos!("this has 22 characters")),
+            liquid_core::call_filter!(Size, "abc").unwrap(),
+            Value::scalar(3f64)
+        );
+        assert_eq!(
+            liquid_core::call_filter!(Size, "this has 22 characters").unwrap(),
             Value::scalar(22f64)
         );
         assert_eq!(
-            unit!(
+            liquid_core::call_filter!(
                 Size,
                 Value::Array(vec![
                     Value::scalar(0f64),
@@ -132,37 +115,44 @@ mod tests {
                     Value::scalar(3f64),
                     Value::scalar(4f64),
                 ])
-            ),
+            )
+            .unwrap(),
             Value::scalar(5f64)
         );
     }
 
     #[test]
     fn unit_default() {
-        assert_eq!(unit!(Default, tos!(""), tos!("bar")), tos!("bar"));
-        assert_eq!(unit!(Default, tos!("foo"), tos!("bar")), tos!("foo"));
         assert_eq!(
-            unit!(Default, Value::scalar(0_f64), tos!("bar")),
+            liquid_core::call_filter!(Default, "", "bar").unwrap(),
+            liquid_core::value!("bar")
+        );
+        assert_eq!(
+            liquid_core::call_filter!(Default, "foo", "bar").unwrap(),
+            liquid_core::value!("foo")
+        );
+        assert_eq!(
+            liquid_core::call_filter!(Default, 0_f64, "bar").unwrap(),
             Value::scalar(0_f64)
         );
         assert_eq!(
-            unit!(Default, Value::Array(vec![]), Value::scalar(1_f64)),
+            liquid_core::call_filter!(Default, liquid_core::value!([]), 1_f64).unwrap(),
             Value::scalar(1_f64)
         );
         assert_eq!(
-            unit!(Default, Value::Array(vec![tos!("")]), Value::scalar(1_f64)),
-            Value::Array(vec![tos!("")])
+            liquid_core::call_filter!(Default, liquid_core::value!([""]), 1_f64).unwrap(),
+            liquid_core::value!([""])
         );
         assert_eq!(
-            unit!(Default, Value::Object(Object::new()), Value::scalar(1_f64)),
+            liquid_core::call_filter!(Default, Object::new(), 1_f64).unwrap(),
             Value::scalar(1_f64)
         );
         assert_eq!(
-            unit!(Default, Value::scalar(false), Value::scalar(1_f64)),
+            liquid_core::call_filter!(Default, false, Value::scalar(1_f64)).unwrap(),
             Value::scalar(1_f64)
         );
         assert_eq!(
-            unit!(Default, Value::scalar(true), Value::scalar(1_f64)),
+            liquid_core::call_filter!(Default, true, Value::scalar(1_f64)).unwrap(),
             Value::scalar(true)
         );
     }
diff --git a/src/filters/std/slice.rs b/src/filters/std/slice.rs
index b560e6a..5c75180 100644
--- a/src/filters/std/slice.rs
+++ b/src/filters/std/slice.rs
@@ -62,7 +62,7 @@ struct SliceFilter {
 }
 
 impl Filter for SliceFilter {
-    fn evaluate(&self, input: &Value, context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, input: &dyn ValueView, context: &Context<'_>) -> Result<Value> {
         let args = self.args.evaluate(context)?;
 
         let offset = args.offset as isize;
@@ -72,10 +72,14 @@ impl Filter for SliceFilter {
             return invalid_argument("length", "Positive number expected").into_err();
         }
 
-        if let Value::Array(input) = input {
-            let (offset, length) = canonicalize_slice(offset, length, input.len());
+        if let Some(input) = input.as_array() {
+            let (offset, length) = canonicalize_slice(offset, length, input.size() as usize);
             Ok(Value::array(
-                input.iter().skip(offset).take(length).cloned(),
+                input
+                    .values()
+                    .skip(offset)
+                    .take(length)
+                    .map(|s| s.to_value()),
             ))
         } else {
             let input = input.to_kstr();
@@ -89,74 +93,64 @@ impl Filter for SliceFilter {
 
 #[cfg(test)]
 mod tests {
-
     use super::*;
 
-    macro_rules! unit {
-        ($a:ident, $b:expr) => {{
-            unit!($a, $b, )
-        }};
-        ($a:ident, $b:expr, $($c:expr),*) => {{
-            let positional = Box::new(vec![$(::liquid_core::interpreter::Expression::Literal($c)),*].into_iter());
-            let keyword = Box::new(Vec::new().into_iter());
-            let args = ::liquid_core::compiler::FilterArguments { positional, keyword };
-
-            let context = ::liquid_core::interpreter::Context::default();
-
-            let filter = ::liquid_core::compiler::ParseFilter::parse(&$a, args).unwrap();
-            ::liquid_core::compiler::Filter::evaluate(&*filter, &$b, &context).unwrap()
-        }};
-    }
-
-    macro_rules! failed {
-        ($a:ident, $b:expr) => {{
-            failed!($a, $b, )
-        }};
-        ($a:ident, $b:expr, $($c:expr),*) => {{
-            let positional = Box::new(vec![$(::liquid_core::interpreter::Expression::Literal($c)),*].into_iter());
-            let keyword = Box::new(Vec::new().into_iter());
-            let args = ::liquid_core::compiler::FilterArguments { positional, keyword };
-
-            let context = ::liquid_core::interpreter::Context::default();
-
-            ::liquid_core::compiler::ParseFilter::parse(&$a, args)
-                .and_then(|filter| ::liquid_core::compiler::Filter::evaluate(&*filter, &$b, &context))
-                .unwrap_err()
-        }};
-    }
-
-    macro_rules! tos {
-        ($a:expr) => {{
-            Value::scalar($a.to_owned())
-        }};
-    }
-
     #[test]
     fn unit_slice() {
-        let input = &tos!("I often quote myself.  It adds spice to my conversation.");
-        let desired_result = tos!("ot");
-        assert_eq!(unit!(Slice, input, tos!(10), tos!(2)), desired_result);
+        assert_eq!(
+            liquid_core::call_filter!(
+                Slice,
+                "I often quote myself.  It adds spice to my conversation.",
+                10,
+                2
+            )
+            .unwrap(),
+            liquid_core::value!("ot")
+        );
     }
 
     #[test]
-    fn unit_slice_no_lenght_specified() {
-        let input = &tos!("I often quote myself.  It adds spice to my conversation.");
-        let desired_result = tos!("t");
-        assert_eq!(unit!(Slice, input, tos!(4)), desired_result);
+    fn unit_slice_no_length_specified() {
+        assert_eq!(
+            liquid_core::call_filter!(
+                Slice,
+                "I often quote myself.  It adds spice to my conversation.",
+                4
+            )
+            .unwrap(),
+            liquid_core::value!("t")
+        );
     }
 
     #[test]
     fn unit_slice_negative_offset() {
-        let input = &tos!("I often quote myself.  It adds spice to my conversation.");
-        let desired_result = tos!("ver");
-        assert_eq!(unit!(Slice, input, tos!(-10), tos!(3)), desired_result);
+        assert_eq!(
+            liquid_core::call_filter!(
+                Slice,
+                "I often quote myself.  It adds spice to my conversation.",
+                -10,
+                3
+            )
+            .unwrap(),
+            liquid_core::value!("ver")
+        );
     }
 
     #[test]
-    fn unit_slice_non_positive_lenght() {
-        let input = &tos!("I often quote myself.  It adds spice to my conversation.");
-
-        failed!(Slice, input, tos!(-10), tos!(0));
-        failed!(Slice, input, tos!(-10), tos!(-1));
+    fn unit_slice_non_positive_length() {
+        liquid_core::call_filter!(
+            Slice,
+            "I often quote myself.  It adds spice to my conversation.",
+            -10,
+            0
+        )
+        .unwrap_err();
+        liquid_core::call_filter!(
+            Slice,
+            "I often quote myself.  It adds spice to my conversation.",
+            -10,
+            -1
+        )
+        .unwrap_err();
     }
 }
diff --git a/src/filters/std/string/case.rs b/src/filters/std/string/case.rs
index 97bdca1..250651c 100644
--- a/src/filters/std/string/case.rs
+++ b/src/filters/std/string/case.rs
@@ -16,7 +16,7 @@ pub struct Downcase;
 struct DowncaseFilter;
 
 impl Filter for DowncaseFilter {
-    fn evaluate(&self, input: &Value, _context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, input: &dyn ValueView, _context: &Context<'_>) -> Result<Value> {
         let s = input.to_kstr();
         Ok(Value::scalar(s.to_lowercase()))
     }
@@ -35,7 +35,7 @@ pub struct Upcase;
 struct UpcaseFilter;
 
 impl Filter for UpcaseFilter {
-    fn evaluate(&self, input: &Value, _context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, input: &dyn ValueView, _context: &Context<'_>) -> Result<Value> {
         let s = input.to_kstr();
         Ok(Value::scalar(s.to_uppercase()))
     }
@@ -54,7 +54,7 @@ pub struct Capitalize;
 struct CapitalizeFilter;
 
 impl Filter for CapitalizeFilter {
-    fn evaluate(&self, input: &Value, _context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, input: &dyn ValueView, _context: &Context<'_>) -> Result<Value> {
         let s = input.to_kstr().to_owned();
         let mut chars = s.chars();
         let capitalized = match chars.next() {
@@ -68,67 +68,59 @@ impl Filter for CapitalizeFilter {
 
 #[cfg(test)]
 mod tests {
-
     use super::*;
 
-    macro_rules! unit {
-        ($a:ident, $b:expr) => {{
-            unit!($a, $b, )
-        }};
-        ($a:ident, $b:expr, $($c:expr),*) => {{
-            let positional = Box::new(vec![$(::liquid_core::interpreter::Expression::Literal($c)),*].into_iter());
-            let keyword = Box::new(Vec::new().into_iter());
-            let args = ::liquid_core::compiler::FilterArguments { positional, keyword };
-
-            let context = ::liquid_core::interpreter::Context::default();
-
-            let filter = ::liquid_core::compiler::ParseFilter::parse(&$a, args).unwrap();
-            ::liquid_core::compiler::Filter::evaluate(&*filter, &$b, &context).unwrap()
-        }};
-    }
-
-    macro_rules! tos {
-        ($a:expr) => {{
-            Value::scalar($a.to_owned())
-        }};
-    }
-
     #[test]
     fn unit_capitalize() {
-        assert_eq!(unit!(Capitalize, tos!("abc")), tos!("Abc"));
         assert_eq!(
-            unit!(Capitalize, tos!("hello world 21")),
-            tos!("Hello world 21")
+            liquid_core::call_filter!(Capitalize, "abc").unwrap(),
+            liquid_core::value!("Abc")
+        );
+        assert_eq!(
+            liquid_core::call_filter!(Capitalize, "hello world 21").unwrap(),
+            liquid_core::value!("Hello world 21")
         );
 
         // sure that Umlauts work
         assert_eq!(
-            unit!(Capitalize, tos!("über ètat, y̆es?")),
-            tos!("Über ètat, y̆es?")
+            liquid_core::call_filter!(Capitalize, "über ètat, y̆es?").unwrap(),
+            liquid_core::value!("Über ètat, y̆es?")
         );
 
         // Weird UTF-8 White space is kept – this is a no-break whitespace!
-        assert_eq!(unit!(Capitalize, tos!("hello world​")), tos!("Hello world​"));
+        assert_eq!(
+            liquid_core::call_filter!(Capitalize, "hello world​").unwrap(),
+            liquid_core::value!("Hello world​")
+        );
 
         // The uppercase version of some character are more than one character long
-        assert_eq!(unit!(Capitalize, tos!("ßß")), tos!("SSß"));
+        assert_eq!(
+            liquid_core::call_filter!(Capitalize, "ßß").unwrap(),
+            liquid_core::value!("SSß")
+        );
     }
 
     #[test]
     fn unit_downcase() {
-        assert_eq!(unit!(Downcase, tos!("Abc")), tos!("abc"));
         assert_eq!(
-            unit!(Downcase, tos!("Hello World 21")),
-            tos!("hello world 21")
+            liquid_core::call_filter!(Downcase, "Abc").unwrap(),
+            liquid_core::value!("abc")
+        );
+        assert_eq!(
+            liquid_core::call_filter!(Downcase, "Hello World 21").unwrap(),
+            liquid_core::value!("hello world 21")
         );
     }
 
     #[test]
     fn unit_upcase() {
-        assert_eq!(unit!(Upcase, tos!("abc")), tos!("ABC"));
         assert_eq!(
-            unit!(Upcase, tos!("Hello World 21")),
-            tos!("HELLO WORLD 21")
+            liquid_core::call_filter!(Upcase, "abc").unwrap(),
+            liquid_core::value!("ABC")
+        );
+        assert_eq!(
+            liquid_core::call_filter!(Upcase, "Hello World 21").unwrap(),
+            liquid_core::value!("HELLO WORLD 21")
         );
     }
 }
diff --git a/src/filters/std/string/mod.rs b/src/filters/std/string/mod.rs
index 8217159..0d14d11 100644
--- a/src/filters/std/string/mod.rs
+++ b/src/filters/std/string/mod.rs
@@ -37,7 +37,7 @@ struct SplitFilter {
 }
 
 impl Filter for SplitFilter {
-    fn evaluate(&self, input: &Value, context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, input: &dyn ValueView, context: &Context<'_>) -> Result<Value> {
         let args = self.args.evaluate(context)?;
 
         let input = input.to_kstr();
@@ -54,70 +54,30 @@ impl Filter for SplitFilter {
 
 #[cfg(test)]
 mod tests {
-
     use super::*;
 
-    macro_rules! unit {
-        ($a:ident, $b:expr) => {{
-            unit!($a, $b, )
-        }};
-        ($a:ident, $b:expr, $($c:expr),*) => {{
-            let positional = Box::new(vec![$(::liquid_core::interpreter::Expression::Literal($c)),*].into_iter());
-            let keyword = Box::new(Vec::new().into_iter());
-            let args = ::liquid_core::compiler::FilterArguments { positional, keyword };
-
-            let context = ::liquid_core::interpreter::Context::default();
-
-            let filter = ::liquid_core::compiler::ParseFilter::parse(&$a, args).unwrap();
-            ::liquid_core::compiler::Filter::evaluate(&*filter, &$b, &context).unwrap()
-        }};
-    }
-
-    macro_rules! failed {
-        ($a:ident, $b:expr) => {{
-            failed!($a, $b, )
-        }};
-        ($a:ident, $b:expr, $($c:expr),*) => {{
-            let positional = Box::new(vec![$(::liquid_core::interpreter::Expression::Literal($c)),*].into_iter());
-            let keyword = Box::new(Vec::new().into_iter());
-            let args = ::liquid_core::compiler::FilterArguments { positional, keyword };
-
-            let context = ::liquid_core::interpreter::Context::default();
-
-            ::liquid_core::compiler::ParseFilter::parse(&$a, args)
-                .and_then(|filter| ::liquid_core::compiler::Filter::evaluate(&*filter, &$b, &context))
-                .unwrap_err()
-        }};
-    }
-
-    macro_rules! tos {
-        ($a:expr) => {{
-            Value::scalar($a.to_owned())
-        }};
-    }
-
     #[test]
     fn unit_split() {
         assert_eq!(
-            unit!(Split, tos!("a, b, c"), tos!(", ")),
-            Value::Array(vec![tos!("a"), tos!("b"), tos!("c")])
+            liquid_core::call_filter!(Split, "a, b, c", ", ").unwrap(),
+            liquid_core::value!(["a", "b", "c"])
         );
         assert_eq!(
-            unit!(Split, tos!("a~b"), tos!("~")),
-            Value::Array(vec![tos!("a"), tos!("b")])
+            liquid_core::call_filter!(Split, "a~b", "~").unwrap(),
+            liquid_core::value!(["a", "b"])
         );
     }
 
     #[test]
     fn unit_split_bad_split_string() {
-        let input = tos!("a,b,c");
-        let desired_result = Value::Array(vec![tos!("a,b,c")]);
-        assert_eq!(unit!(Split, input, Value::scalar(1f64)), desired_result);
+        assert_eq!(
+            liquid_core::call_filter!(Split, "a,b,c", 1f64).unwrap(),
+            liquid_core::value!(["a,b,c"])
+        );
     }
 
     #[test]
     fn unit_split_no_args() {
-        let input = tos!("a,b,c");
-        failed!(Split, input);
+        liquid_core::call_filter!(Split, "a,b,c").unwrap_err();
     }
 }
diff --git a/src/filters/std/string/operate.rs b/src/filters/std/string/operate.rs
index cd47267..a19eaa4 100644
--- a/src/filters/std/string/operate.rs
+++ b/src/filters/std/string/operate.rs
@@ -34,7 +34,7 @@ struct ReplaceFilter {
 }
 
 impl Filter for ReplaceFilter {
-    fn evaluate(&self, input: &Value, context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, input: &dyn ValueView, context: &Context<'_>) -> Result<Value> {
         let args = self.args.evaluate(context)?;
 
         let input = input.to_kstr();
@@ -75,7 +75,7 @@ struct ReplaceFirstFilter {
 }
 
 impl Filter for ReplaceFirstFilter {
-    fn evaluate(&self, input: &Value, context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, input: &dyn ValueView, context: &Context<'_>) -> Result<Value> {
         let args = self.args.evaluate(context)?;
 
         let input = input.to_kstr();
@@ -117,7 +117,7 @@ struct RemoveFilter {
 }
 
 impl Filter for RemoveFilter {
-    fn evaluate(&self, input: &Value, context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, input: &dyn ValueView, context: &Context<'_>) -> Result<Value> {
         let args = self.args.evaluate(context)?;
 
         let input = input.to_kstr();
@@ -149,7 +149,7 @@ struct RemoveFirstFilter {
 }
 
 impl Filter for RemoveFirstFilter {
-    fn evaluate(&self, input: &Value, context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, input: &dyn ValueView, context: &Context<'_>) -> Result<Value> {
         let args = self.args.evaluate(context)?;
 
         let input = input.to_kstr();
@@ -183,7 +183,7 @@ struct AppendFilter {
 }
 
 impl Filter for AppendFilter {
-    fn evaluate(&self, input: &Value, context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, input: &dyn ValueView, context: &Context<'_>) -> Result<Value> {
         let args = self.args.evaluate(context)?;
 
         let mut input = input.to_kstr().into_string();
@@ -216,7 +216,7 @@ struct PrependFilter {
 }
 
 impl Filter for PrependFilter {
-    fn evaluate(&self, input: &Value, context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, input: &dyn ValueView, context: &Context<'_>) -> Result<Value> {
         let args = self.args.evaluate(context)?;
 
         let input = input.to_kstr();
@@ -229,81 +229,85 @@ impl Filter for PrependFilter {
 
 #[cfg(test)]
 mod tests {
-
     use super::*;
 
-    macro_rules! unit {
-        ($a:ident, $b:expr) => {{
-            unit!($a, $b, )
-        }};
-        ($a:ident, $b:expr, $($c:expr),*) => {{
-            let positional = Box::new(vec![$(::liquid_core::interpreter::Expression::Literal($c)),*].into_iter());
-            let keyword = Box::new(Vec::new().into_iter());
-            let args = ::liquid_core::compiler::FilterArguments { positional, keyword };
-
-            let context = ::liquid_core::interpreter::Context::default();
-
-            let filter = ::liquid_core::compiler::ParseFilter::parse(&$a, args).unwrap();
-            ::liquid_core::compiler::Filter::evaluate(&*filter, &$b, &context).unwrap()
-        }};
-    }
-
-    macro_rules! tos {
-        ($a:expr) => {{
-            Value::scalar($a.to_owned())
-        }};
-    }
-
     #[test]
     fn unit_append() {
-        assert_eq!(unit!(Append, tos!("sam"), tos!("son")), tos!("samson"));
+        assert_eq!(
+            liquid_core::call_filter!(Append, "sam", "son").unwrap(),
+            liquid_core::value!("samson")
+        );
     }
 
     #[test]
     fn unit_prepend() {
         assert_eq!(
-            unit!(Prepend, tos!("barbar"), tos!("foo")),
-            tos!("foobarbar")
+            liquid_core::call_filter!(Prepend, "barbar", "foo").unwrap(),
+            liquid_core::value!("foobarbar")
         );
     }
 
     #[test]
     fn unit_remove() {
-        assert_eq!(unit!(Remove, tos!("barbar"), tos!("bar")), tos!(""));
-        assert_eq!(unit!(Remove, tos!("barbar"), tos!("")), tos!("barbar"));
-        assert_eq!(unit!(Remove, tos!("barbar"), tos!("barbar")), tos!(""));
-        assert_eq!(unit!(Remove, tos!("barbar"), tos!("a")), tos!("brbr"));
+        assert_eq!(
+            liquid_core::call_filter!(Remove, "barbar", "bar").unwrap(),
+            liquid_core::value!("")
+        );
+        assert_eq!(
+            liquid_core::call_filter!(Remove, "barbar", "").unwrap(),
+            liquid_core::value!("barbar")
+        );
+        assert_eq!(
+            liquid_core::call_filter!(Remove, "barbar", "barbar").unwrap(),
+            liquid_core::value!("")
+        );
+        assert_eq!(
+            liquid_core::call_filter!(Remove, "barbar", "a").unwrap(),
+            liquid_core::value!("brbr")
+        );
     }
 
     #[test]
     fn unit_remove_first() {
-        assert_eq!(unit!(RemoveFirst, tos!("barbar"), tos!("bar")), tos!("bar"));
-        assert_eq!(unit!(RemoveFirst, tos!("barbar"), tos!("")), tos!("barbar"));
-        assert_eq!(unit!(RemoveFirst, tos!("barbar"), tos!("barbar")), tos!(""));
-        assert_eq!(unit!(RemoveFirst, tos!("barbar"), tos!("a")), tos!("brbar"));
+        assert_eq!(
+            liquid_core::call_filter!(RemoveFirst, "barbar", "bar").unwrap(),
+            liquid_core::value!("bar")
+        );
+        assert_eq!(
+            liquid_core::call_filter!(RemoveFirst, "barbar", "").unwrap(),
+            liquid_core::value!("barbar")
+        );
+        assert_eq!(
+            liquid_core::call_filter!(RemoveFirst, "barbar", "barbar").unwrap(),
+            liquid_core::value!("")
+        );
+        assert_eq!(
+            liquid_core::call_filter!(RemoveFirst, "barbar", "a").unwrap(),
+            liquid_core::value!("brbar")
+        );
     }
 
     #[test]
     fn unit_replace() {
         assert_eq!(
-            unit!(Replace, tos!("barbar"), tos!("bar"), tos!("foo")),
-            tos!("foofoo")
+            liquid_core::call_filter!(Replace, "barbar", "bar", "foo").unwrap(),
+            liquid_core::value!("foofoo")
         );
     }
 
     #[test]
     fn unit_replace_first() {
         assert_eq!(
-            unit!(ReplaceFirst, tos!("barbar"), tos!("bar"), tos!("foo")),
-            tos!("foobar")
+            liquid_core::call_filter!(ReplaceFirst, "barbar", "bar", "foo").unwrap(),
+            liquid_core::value!("foobar")
         );
         assert_eq!(
-            unit!(ReplaceFirst, tos!("barxoxo"), tos!("xo"), tos!("foo")),
-            tos!("barfooxo")
+            liquid_core::call_filter!(ReplaceFirst, "barxoxo", "xo", "foo").unwrap(),
+            liquid_core::value!("barfooxo")
         );
         assert_eq!(
-            unit!(ReplaceFirst, tos!(""), tos!("bar"), tos!("foo")),
-            tos!("")
+            liquid_core::call_filter!(ReplaceFirst, "", "bar", "foo").unwrap(),
+            liquid_core::value!("")
         );
     }
 }
diff --git a/src/filters/std/string/strip.rs b/src/filters/std/string/strip.rs
index 675013a..b7e6eba 100644
--- a/src/filters/std/string/strip.rs
+++ b/src/filters/std/string/strip.rs
@@ -23,7 +23,7 @@ pub struct Strip;
 struct StripFilter;
 
 impl Filter for StripFilter {
-    fn evaluate(&self, input: &Value, _context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, input: &dyn ValueView, _context: &Context<'_>) -> Result<Value> {
         let input = input.to_kstr();
         Ok(Value::scalar(input.trim().to_owned()))
     }
@@ -48,7 +48,7 @@ pub struct Lstrip;
 struct LstripFilter;
 
 impl Filter for LstripFilter {
-    fn evaluate(&self, input: &Value, _context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, input: &dyn ValueView, _context: &Context<'_>) -> Result<Value> {
         let input = input.to_kstr();
         Ok(Value::scalar(input.trim_start().to_owned()))
     }
@@ -73,7 +73,7 @@ pub struct Rstrip;
 struct RstripFilter;
 
 impl Filter for RstripFilter {
-    fn evaluate(&self, input: &Value, _context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, input: &dyn ValueView, _context: &Context<'_>) -> Result<Value> {
         let input = input.to_kstr();
         Ok(Value::scalar(input.trim_end().to_owned()))
     }
@@ -92,7 +92,7 @@ pub struct StripNewlines;
 struct StripNewlinesFilter;
 
 impl Filter for StripNewlinesFilter {
-    fn evaluate(&self, input: &Value, _context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, input: &dyn ValueView, _context: &Context<'_>) -> Result<Value> {
         let input = input.to_kstr();
         Ok(Value::scalar(
             input
@@ -105,220 +105,200 @@ impl Filter for StripNewlinesFilter {
 
 #[cfg(test)]
 mod tests {
-
     use super::*;
 
-    macro_rules! unit {
-        ($a:ident, $b:expr) => {{
-            unit!($a, $b, )
-        }};
-        ($a:ident, $b:expr, $($c:expr),*) => {{
-            let positional = Box::new(vec![$(::liquid_core::interpreter::Expression::Literal($c)),*].into_iter());
-            let keyword = Box::new(Vec::new().into_iter());
-            let args = ::liquid_core::compiler::FilterArguments { positional, keyword };
-
-            let context = ::liquid_core::interpreter::Context::default();
-
-            let filter = ::liquid_core::compiler::ParseFilter::parse(&$a, args).unwrap();
-            ::liquid_core::compiler::Filter::evaluate(&*filter, &$b, &context).unwrap()
-        }};
-    }
-
-    macro_rules! failed {
-        ($a:ident, $b:expr) => {{
-            failed!($a, $b, )
-        }};
-        ($a:ident, $b:expr, $($c:expr),*) => {{
-            let positional = Box::new(vec![$(::liquid_core::interpreter::Expression::Literal($c)),*].into_iter());
-            let keyword = Box::new(Vec::new().into_iter());
-            let args = ::liquid_core::compiler::FilterArguments { positional, keyword };
-
-            let context = ::liquid_core::interpreter::Context::default();
-
-            ::liquid_core::compiler::ParseFilter::parse(&$a, args)
-                .and_then(|filter| ::liquid_core::compiler::Filter::evaluate(&*filter, &$b, &context))
-                .unwrap_err()
-        }};
-    }
-
-    macro_rules! tos {
-        ($a:expr) => {{
-            Value::scalar($a.to_owned())
-        }};
-    }
-
     #[test]
     fn unit_lstrip() {
-        let input = &tos!(" 	 \n \r test");
-        let desired_result = tos!("test");
-        assert_eq!(unit!(Lstrip, input), desired_result);
+        assert_eq!(
+            liquid_core::call_filter!(Lstrip, " 	 \n \r test").unwrap(),
+            liquid_core::value!("test")
+        );
     }
 
     #[test]
     fn unit_lstrip_non_string() {
-        let input = &Value::scalar(0f64);
-        let desired_result = tos!("0");
-        assert_eq!(unit!(Lstrip, input), desired_result);
+        assert_eq!(
+            liquid_core::call_filter!(Lstrip, 0f64).unwrap(),
+            liquid_core::value!("0")
+        );
     }
 
     #[test]
     fn unit_lstrip_one_argument() {
-        let input = &tos!(" 	 \n \r test");
-        failed!(Lstrip, input, Value::scalar(0f64));
+        liquid_core::call_filter!(Lstrip, " 	 \n \r test", 0f64).unwrap_err();
     }
 
     #[test]
     fn unit_lstrip_shopify_liquid() {
         // One test from https://shopify.github.io/liquid/filters/lstrip/
-        let input = &tos!("          So much room for activities!          ");
-        let desired_result = tos!("So much room for activities!          ");
-        assert_eq!(unit!(Lstrip, input), desired_result);
+        assert_eq!(
+            liquid_core::call_filter!(Lstrip, "          So much room for activities!          ")
+                .unwrap(),
+            liquid_core::value!("So much room for activities!          ")
+        );
     }
 
     #[test]
     fn unit_lstrip_trailing_sequence() {
-        let input = &tos!(" 	 \n \r test 	 \n \r ");
-        let desired_result = tos!("test 	 \n \r ");
-        assert_eq!(unit!(Lstrip, input), desired_result);
+        assert_eq!(
+            liquid_core::call_filter!(Lstrip, " 	 \n \r test 	 \n \r ").unwrap(),
+            liquid_core::value!("test 	 \n \r ")
+        );
     }
 
     #[test]
     fn unit_lstrip_trailing_sequence_only() {
-        let input = &tos!("test 	 \n \r ");
-        let desired_result = tos!("test 	 \n \r ");
-        assert_eq!(unit!(Lstrip, input), desired_result);
+        assert_eq!(
+            liquid_core::call_filter!(Lstrip, "test 	 \n \r ").unwrap(),
+            liquid_core::value!("test 	 \n \r ")
+        );
     }
 
     #[test]
     fn unit_rstrip() {
-        let input = &tos!("test 	 \n \r ");
-        let desired_result = tos!("test");
-        assert_eq!(unit!(Rstrip, input), desired_result);
+        assert_eq!(
+            liquid_core::call_filter!(Rstrip, "test 	 \n \r ").unwrap(),
+            liquid_core::value!("test")
+        );
     }
 
     #[test]
     fn unit_rstrip_leading_sequence() {
-        let input = &tos!(" 	 \n \r test 	 \n \r ");
-        let desired_result = tos!(" 	 \n \r test");
-        assert_eq!(unit!(Rstrip, input), desired_result);
+        assert_eq!(
+            liquid_core::call_filter!(Rstrip, " 	 \n \r test 	 \n \r ").unwrap(),
+            liquid_core::value!(" 	 \n \r test")
+        );
     }
 
     #[test]
     fn unit_rstrip_leading_sequence_only() {
-        let input = &tos!(" 	 \n \r test");
-        let desired_result = tos!(" 	 \n \r test");
-        assert_eq!(unit!(Rstrip, input), desired_result);
+        assert_eq!(
+            liquid_core::call_filter!(Rstrip, " 	 \n \r test").unwrap(),
+            liquid_core::value!(" 	 \n \r test")
+        );
     }
 
     #[test]
     fn unit_rstrip_non_string() {
-        let input = &Value::scalar(0f64);
-        let desired_result = tos!("0");
-        assert_eq!(unit!(Rstrip, input), desired_result);
+        assert_eq!(
+            liquid_core::call_filter!(Rstrip, 0f64).unwrap(),
+            liquid_core::value!("0")
+        );
     }
 
     #[test]
     fn unit_rstrip_one_argument() {
-        let input = &tos!(" 	 \n \r test");
-        failed!(Rstrip, input, Value::scalar(0f64));
+        liquid_core::call_filter!(Rstrip, " 	 \n \r test", 0f64).unwrap_err();
     }
 
     #[test]
     fn unit_rstrip_shopify_liquid() {
         // One test from https://shopify.github.io/liquid/filters/rstrip/
-        let input = &tos!("          So much room for activities!          ");
-        let desired_result = tos!("          So much room for activities!");
-        assert_eq!(unit!(Rstrip, input), desired_result);
+        assert_eq!(
+            liquid_core::call_filter!(Rstrip, "          So much room for activities!          ")
+                .unwrap(),
+            liquid_core::value!("          So much room for activities!")
+        );
     }
 
     #[test]
     fn unit_strip() {
-        let input = &tos!(" 	 \n \r test 	 \n \r ");
-        let desired_result = tos!("test");
-        assert_eq!(unit!(Strip, input), desired_result);
+        assert_eq!(
+            liquid_core::call_filter!(Strip, " 	 \n \r test 	 \n \r ").unwrap(),
+            liquid_core::value!("test")
+        );
     }
 
     #[test]
     fn unit_strip_leading_sequence_only() {
-        let input = &tos!(" 	 \n \r test");
-        let desired_result = tos!("test");
-        assert_eq!(unit!(Strip, input), desired_result);
+        assert_eq!(
+            liquid_core::call_filter!(Strip, " 	 \n \r test").unwrap(),
+            liquid_core::value!("test")
+        );
     }
 
     #[test]
     fn unit_strip_non_string() {
-        let input = &Value::scalar(0f64);
-        let desired_result = tos!("0");
-        assert_eq!(unit!(Strip, input), desired_result);
+        assert_eq!(
+            liquid_core::call_filter!(Strip, 0f64).unwrap(),
+            liquid_core::value!("0")
+        );
     }
 
     #[test]
     fn unit_strip_one_argument() {
-        let input = &tos!(" 	 \n \r test 	 \n \r ");
-        failed!(Strip, input, Value::scalar(0f64));
+        liquid_core::call_filter!(Strip, " 	 \n \r test 	 \n \r ", 0f64).unwrap_err();
     }
 
     #[test]
     fn unit_strip_shopify_liquid() {
         // One test from https://shopify.github.io/liquid/filters/strip/
-        let input = &tos!("          So much room for activities!          ");
-        let desired_result = tos!("So much room for activities!");
-        assert_eq!(unit!(Strip, input), desired_result);
+        assert_eq!(
+            liquid_core::call_filter!(Strip, "          So much room for activities!          ")
+                .unwrap(),
+            liquid_core::value!("So much room for activities!")
+        );
     }
 
     #[test]
     fn unit_strip_trailing_sequence_only() {
-        let input = &tos!("test 	 \n \r ");
-        let desired_result = tos!("test");
-        assert_eq!(unit!(Strip, input), desired_result);
+        assert_eq!(
+            liquid_core::call_filter!(Strip, "test 	 \n \r ").unwrap(),
+            liquid_core::value!("test")
+        );
     }
 
     #[test]
     fn unit_strip_newlines() {
-        let input = &tos!("a\nb\n");
-        let desired_result = tos!("ab");
-        assert_eq!(unit!(StripNewlines, input), desired_result);
+        assert_eq!(
+            liquid_core::call_filter!(StripNewlines, "a\nb\n").unwrap(),
+            liquid_core::value!("ab")
+        );
     }
 
     #[test]
     fn unit_strip_newlines_between_only() {
-        let input = &tos!("a\nb");
-        let desired_result = tos!("ab");
-        assert_eq!(unit!(StripNewlines, input), desired_result);
+        assert_eq!(
+            liquid_core::call_filter!(StripNewlines, "a\nb").unwrap(),
+            liquid_core::value!("ab")
+        );
     }
 
     #[test]
     fn unit_strip_newlines_leading_only() {
-        let input = &tos!("\nab");
-        let desired_result = tos!("ab");
-        assert_eq!(unit!(StripNewlines, input), desired_result);
+        assert_eq!(
+            liquid_core::call_filter!(StripNewlines, "\nab").unwrap(),
+            liquid_core::value!("ab")
+        );
     }
 
     #[test]
     fn unit_strip_newlines_non_string() {
-        let input = &Value::scalar(0f64);
-        let desired_result = tos!("0");
-        assert_eq!(unit!(StripNewlines, input), desired_result);
+        assert_eq!(
+            liquid_core::call_filter!(StripNewlines, 0f64).unwrap(),
+            liquid_core::value!("0")
+        );
     }
 
     #[test]
     fn unit_strip_newlines_one_argument() {
-        let input = &tos!("ab\n");
-        failed!(StripNewlines, input, Value::scalar(0f64));
+        liquid_core::call_filter!(StripNewlines, "ab\n", 0f64).unwrap_err();
     }
 
     #[test]
     fn unit_strip_newlines_shopify_liquid() {
         // Test from https://shopify.github.io/liquid/filters/strip_newlines/
-        let input = &tos!("\nHello\nthere\n");
-        let desired_result = tos!("Hellothere");
-        assert_eq!(unit!(StripNewlines, input), desired_result);
+        assert_eq!(
+            liquid_core::call_filter!(StripNewlines, "\nHello\nthere\n").unwrap(),
+            liquid_core::value!("Hellothere")
+        );
     }
 
     #[test]
     fn unit_strip_newlines_trailing_only() {
-        let input = &tos!("ab\n");
-        let desired_result = tos!("ab");
-        assert_eq!(unit!(StripNewlines, input), desired_result);
+        assert_eq!(
+            liquid_core::call_filter!(StripNewlines, "ab\n").unwrap(),
+            liquid_core::value!("ab")
+        );
     }
 }
diff --git a/src/filters/std/string/truncate.rs b/src/filters/std/string/truncate.rs
index 89ed304..8b8f16e 100644
--- a/src/filters/std/string/truncate.rs
+++ b/src/filters/std/string/truncate.rs
@@ -68,7 +68,7 @@ struct TruncateFilter {
 }
 
 impl Filter for TruncateFilter {
-    fn evaluate(&self, input: &Value, context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, input: &dyn ValueView, context: &Context<'_>) -> Result<Value> {
         let args = self.args.evaluate(context)?;
 
         let lenght = args.lenght.unwrap_or(50) as usize;
@@ -88,7 +88,7 @@ impl Filter for TruncateFilter {
                 + truncate_string.as_str();
             Value::scalar(result)
         } else {
-            input.clone()
+            input.to_value()
         };
         Ok(result)
     }
@@ -126,7 +126,7 @@ struct TruncateWordsFilter {
 }
 
 impl Filter for TruncateWordsFilter {
-    fn evaluate(&self, input: &Value, context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, input: &dyn ValueView, context: &Context<'_>) -> Result<Value> {
         let args = self.args.evaluate(context)?;
 
         let words = args.lenght.unwrap_or(50) as usize;
@@ -142,7 +142,7 @@ impl Filter for TruncateWordsFilter {
             let result = itertools::join(word_list.iter().take(l), " ") + truncate_string.as_str();
             Value::scalar(result)
         } else {
-            input.clone()
+            input.to_value()
         };
         Ok(result)
     }
@@ -150,103 +150,73 @@ impl Filter for TruncateWordsFilter {
 
 #[cfg(test)]
 mod tests {
-
     use super::*;
 
-    macro_rules! unit {
-        ($a:ident, $b:expr) => {{
-            unit!($a, $b, )
-        }};
-        ($a:ident, $b:expr, $($c:expr),*) => {{
-            let positional = Box::new(vec![$(::liquid_core::interpreter::Expression::Literal($c)),*].into_iter());
-            let keyword = Box::new(Vec::new().into_iter());
-            let args = ::liquid_core::compiler::FilterArguments { positional, keyword };
-
-            let context = ::liquid_core::interpreter::Context::default();
-
-            let filter = ::liquid_core::compiler::ParseFilter::parse(&$a, args).unwrap();
-            ::liquid_core::compiler::Filter::evaluate(&*filter, &$b, &context).unwrap()
-        }};
-    }
-
-    macro_rules! failed {
-        ($a:ident, $b:expr) => {{
-            failed!($a, $b, )
-        }};
-        ($a:ident, $b:expr, $($c:expr),*) => {{
-            let positional = Box::new(vec![$(::liquid_core::interpreter::Expression::Literal($c)),*].into_iter());
-            let keyword = Box::new(Vec::new().into_iter());
-            let args = ::liquid_core::compiler::FilterArguments { positional, keyword };
-
-            let context = ::liquid_core::interpreter::Context::default();
-
-            ::liquid_core::compiler::ParseFilter::parse(&$a, args)
-                .and_then(|filter| ::liquid_core::compiler::Filter::evaluate(&*filter, &$b, &context))
-                .unwrap_err()
-        }};
-    }
-
-    macro_rules! tos {
-        ($a:expr) => {{
-            Value::scalar($a.to_owned())
-        }};
-    }
-
     #[test]
     fn unit_truncate() {
-        let input = &tos!("I often quote myself.  It adds spice to my conversation.");
-        let desired_result = tos!("I often quote ...");
-        assert_eq!(unit!(Truncate, input, Value::scalar(17i32)), desired_result);
+        assert_eq!(
+            liquid_core::call_filter!(
+                Truncate,
+                "I often quote myself.  It adds spice to my conversation.",
+                17i32
+            )
+            .unwrap(),
+            liquid_core::value!("I often quote ...")
+        );
     }
 
     #[test]
     fn unit_truncate_negative_length() {
-        let input = &tos!("I often quote myself.  It adds spice to my conversation.");
-        let desired_result = tos!("I often quote myself.  It adds spice to my conversation.");
         assert_eq!(
-            unit!(Truncate, input, Value::scalar(-17i32)),
-            desired_result
+            liquid_core::call_filter!(
+                Truncate,
+                "I often quote myself.  It adds spice to my conversation.",
+                -17i32
+            )
+            .unwrap(),
+            liquid_core::value!("I often quote myself.  It adds spice to my conversation.")
         );
     }
 
     #[test]
     fn unit_truncate_non_string() {
-        let input = &Value::scalar(10000000f64);
-        let desired_result = tos!("10...");
-        assert_eq!(unit!(Truncate, input, Value::scalar(5i32)), desired_result);
+        assert_eq!(
+            liquid_core::call_filter!(Truncate, 10000000f64, 5i32).unwrap(),
+            liquid_core::value!("10...")
+        );
     }
 
     #[test]
     fn unit_truncate_shopify_liquid() {
         // Tests from https://shopify.github.io/liquid/filters/truncate/
-        let input = &tos!("Ground control to Major Tom.");
+        let input = "Ground control to Major Tom.";
 
-        let desired_result = tos!("Ground control to...");
-        assert_eq!(unit!(Truncate, input, Value::scalar(20i32)), desired_result);
+        assert_eq!(
+            liquid_core::call_filter!(Truncate, input, 20i32).unwrap(),
+            liquid_core::value!("Ground control to...")
+        );
 
-        let desired_result = tos!("Ground control, and so on");
         assert_eq!(
-            unit!(Truncate, input, Value::scalar(25i32), tos!(", and so on")),
-            desired_result
+            liquid_core::call_filter!(Truncate, input, 25i32, ", and so on").unwrap(),
+            liquid_core::value!("Ground control, and so on")
         );
 
-        let desired_result = tos!("Ground control to Ma");
         assert_eq!(
-            unit!(Truncate, input, Value::scalar(20i32), tos!("")),
-            desired_result
+            liquid_core::call_filter!(Truncate, input, 20i32, "").unwrap(),
+            liquid_core::value!("Ground control to Ma")
         );
     }
 
     #[test]
     fn unit_truncate_three_arguments() {
-        let input = &tos!("I often quote myself.  It adds spice to my conversation.");
-        failed!(
+        liquid_core::call_filter!(
             Truncate,
-            input,
-            Value::scalar(17i32),
-            tos!("..."),
-            Value::scalar(0i32)
-        );
+            "I often quote myself.  It adds spice to my conversation.",
+            17i32,
+            "...",
+            0i32
+        )
+        .unwrap_err();
     }
 
     #[test]
@@ -258,69 +228,76 @@ mod tests {
         //
         // Note that the accents applied to each letter are treated as part of the single grapheme
         // cluster for the applicable letter.
-        let input = &tos!("Here is an a\u{310}, e\u{301}, and o\u{308}\u{332}.");
-        let desired_result = tos!("Here is an a\u{310}, e\u{301}, ...");
-        assert_eq!(unit!(Truncate, input, Value::scalar(20i32)), desired_result);
+        assert_eq!(
+            liquid_core::call_filter!(
+                Truncate,
+                "Here is an a\u{310}, e\u{301}, and o\u{308}\u{332}.",
+                20i32
+            )
+            .unwrap(),
+            liquid_core::value!("Here is an a\u{310}, e\u{301}, ...")
+        );
 
         // Note that the 🇷🇺🇸🇹 is treated as a single grapheme cluster.
-        let input = &tos!("Here is a RUST: 🇷🇺🇸🇹.");
-        let desired_result = tos!("Here is a RUST: 🇷🇺...");
-        assert_eq!(unit!(Truncate, input, Value::scalar(20i32)), desired_result);
+        assert_eq!(
+            liquid_core::call_filter!(Truncate, "Here is a RUST: 🇷🇺🇸🇹.", 20i32).unwrap(),
+            liquid_core::value!("Here is a RUST: 🇷🇺...")
+        );
     }
 
     #[test]
     fn unit_truncate_zero_arguments() {
-        let input = &tos!("I often quote myself.  It adds spice to my conversation.");
-        let desired_result = tos!("I often quote myself.  It adds spice to my conv...");
-        assert_eq!(unit!(Truncate, input), desired_result);
+        assert_eq!(
+            liquid_core::call_filter!(
+                Truncate,
+                "I often quote myself.  It adds spice to my conversation."
+            )
+            .unwrap(),
+            liquid_core::value!("I often quote myself.  It adds spice to my conv...")
+        );
     }
 
     #[test]
     fn unit_truncatewords_negative_length() {
         assert_eq!(
-            unit!(TruncateWords, tos!("one two three"), Value::scalar(-1_i32)),
-            tos!("one two three")
+            liquid_core::call_filter!(TruncateWords, "one two three", -1_i32).unwrap(),
+            liquid_core::value!("one two three")
         );
     }
 
     #[test]
     fn unit_truncatewords_zero_length() {
         assert_eq!(
-            unit!(TruncateWords, tos!("one two three"), Value::scalar(0_i32)),
-            tos!("...")
+            liquid_core::call_filter!(TruncateWords, "one two three", 0_i32).unwrap(),
+            liquid_core::value!("...")
         );
     }
 
     #[test]
     fn unit_truncatewords_no_truncation() {
         assert_eq!(
-            unit!(TruncateWords, tos!("one two three"), Value::scalar(4_i32)),
-            tos!("one two three")
+            liquid_core::call_filter!(TruncateWords, "one two three", 4_i32).unwrap(),
+            liquid_core::value!("one two three")
         );
     }
 
     #[test]
     fn unit_truncatewords_truncate() {
         assert_eq!(
-            unit!(TruncateWords, tos!("one two three"), Value::scalar(2_i32)),
-            tos!("one two...")
+            liquid_core::call_filter!(TruncateWords, "one two three", 2_i32).unwrap(),
+            liquid_core::value!("one two...")
         );
         assert_eq!(
-            unit!(
-                TruncateWords,
-                tos!("one two three"),
-                Value::scalar(2_i32),
-                Value::scalar(1_i32)
-            ),
-            tos!("one two1")
+            liquid_core::call_filter!(TruncateWords, "one two three", 2_i32, 1_i32).unwrap(),
+            liquid_core::value!("one two1")
         );
     }
 
     #[test]
     fn unit_truncatewords_empty_string() {
         assert_eq!(
-            unit!(TruncateWords, tos!(""), Value::scalar(1_i32)),
-            tos!("")
+            liquid_core::call_filter!(TruncateWords, "", 1_i32).unwrap(),
+            liquid_core::value!("")
         );
     }
 }
diff --git a/src/filters/std/url.rs b/src/filters/std/url.rs
index 2652d83..9f5e534 100644
--- a/src/filters/std/url.rs
+++ b/src/filters/std/url.rs
@@ -48,7 +48,7 @@ static URL_ENCODE_SET: once_cell::sync::Lazy<UrlEncodeSet> =
     once_cell::sync::Lazy::new(|| UrlEncodeSet("".to_owned()));
 
 impl Filter for UrlEncodeFilter {
-    fn evaluate(&self, input: &Value, _context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, input: &dyn ValueView, _context: &Context<'_>) -> Result<Value> {
         if input.is_nil() {
             return Ok(Value::Nil);
         }
@@ -74,7 +74,7 @@ pub struct UrlDecode;
 struct UrlDecodeFilter;
 
 impl Filter for UrlDecodeFilter {
-    fn evaluate(&self, input: &Value, _context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, input: &dyn ValueView, _context: &Context<'_>) -> Result<Value> {
         if input.is_nil() {
             return Ok(Value::Nil);
         }
@@ -91,48 +91,34 @@ impl Filter for UrlDecodeFilter {
 
 #[cfg(test)]
 mod tests {
-
     use super::*;
 
-    macro_rules! unit {
-        ($a:ident, $b:expr) => {{
-            unit!($a, $b, )
-        }};
-        ($a:ident, $b:expr, $($c:expr),*) => {{
-            let positional = Box::new(vec![$(::liquid_core::interpreter::Expression::Literal($c)),*].into_iter());
-            let keyword = Box::new(Vec::new().into_iter());
-            let args = ::liquid_core::compiler::FilterArguments { positional, keyword };
-
-            let context = ::liquid_core::interpreter::Context::default();
-
-            let filter = ::liquid_core::compiler::ParseFilter::parse(&$a, args).unwrap();
-            ::liquid_core::compiler::Filter::evaluate(&*filter, &$b, &context).unwrap()
-        }};
-    }
-
-    macro_rules! tos {
-        ($a:expr) => {{
-            Value::scalar($a.to_owned())
-        }};
-    }
-
     #[test]
     fn unit_url_encode() {
-        assert_eq!(unit!(UrlEncode, tos!("foo bar")), tos!("foo%20bar"));
         assert_eq!(
-            unit!(UrlEncode, tos!("foo+1@example.com")),
-            tos!("foo%2B1%40example.com")
+            liquid_core::call_filter!(UrlEncode, "foo bar").unwrap(),
+            liquid_core::value!("foo%20bar")
+        );
+        assert_eq!(
+            liquid_core::call_filter!(UrlEncode, "foo+1@example.com").unwrap(),
+            liquid_core::value!("foo%2B1%40example.com")
         );
     }
 
     #[test]
     fn unit_url_decode() {
         // TODO Test case from shopify/liquid that we aren't handling:
-        // - assert_eq!(unit!(url_decode, tos!("foo+bar")), tos!("foo bar"));
-        assert_eq!(unit!(UrlDecode, tos!("foo%20bar")), tos!("foo bar"));
+        // - assert_eq!(
+        //      liquid_core::call_filter!(url_decode, "foo+bar").unwrap(),
+        //      liquid_core::value!("foo bar")
+        //  );
+        assert_eq!(
+            liquid_core::call_filter!(UrlDecode, "foo%20bar").unwrap(),
+            liquid_core::value!("foo bar")
+        );
         assert_eq!(
-            unit!(UrlDecode, tos!("foo%2B1%40example.com")),
-            tos!("foo+1@example.com")
+            liquid_core::call_filter!(UrlDecode, "foo%2B1%40example.com").unwrap(),
+            liquid_core::value!("foo+1@example.com")
         );
     }
 }
diff --git a/src/tags/assign_tag.rs b/src/tags/assign_tag.rs
index 86880b7..3bd11e1 100644
--- a/src/tags/assign_tag.rs
+++ b/src/tags/assign_tag.rs
@@ -25,7 +25,8 @@ impl Renderable for Assign {
         let value = self
             .src
             .evaluate(context)
-            .trace_with(|| self.trace().into())?;
+            .trace_with(|| self.trace().into())?
+            .into_owned();
         context.stack_mut().set_global(self.dst.to_owned(), value);
         Ok(())
     }
diff --git a/src/tags/for_block.rs b/src/tags/for_block.rs
index f76a1aa..7d021ae 100644
--- a/src/tags/for_block.rs
+++ b/src/tags/for_block.rs
@@ -876,7 +876,7 @@ mod test {
     pub struct ShoutFilter;
 
     impl Filter for ShoutFilter {
-        fn evaluate(&self, input: &Value, _context: &Context<'_>) -> Result<Value> {
+        fn evaluate(&self, input: &dyn ValueView, _context: &Context<'_>) -> Result<Value> {
             Ok(Value::scalar(input.to_kstr().to_uppercase()))
         }
     }
diff --git a/src/tags/include_tag.rs b/src/tags/include_tag.rs
index 8c065c3..28f1c3d 100644
--- a/src/tags/include_tag.rs
+++ b/src/tags/include_tag.rs
@@ -141,12 +141,15 @@ mod test {
     pub struct SizeFilter;
 
     impl Filter for SizeFilter {
-        fn evaluate(&self, input: &Value, _context: &Context<'_>) -> Result<Value> {
-            match *input {
-                Value::Scalar(ref x) => Ok(Value::scalar(x.to_kstr().len() as i32)),
-                Value::Array(ref x) => Ok(Value::scalar(x.len() as i32)),
-                Value::Object(ref x) => Ok(Value::scalar(x.len() as i32)),
-                _ => Ok(Value::scalar(0i32)),
+        fn evaluate(&self, input: &dyn ValueView, _context: &Context<'_>) -> Result<Value> {
+            if let Some(x) = input.as_scalar() {
+                Ok(Value::scalar(x.to_kstr().len() as i32))
+            } else if let Some(x) = input.as_array() {
+                Ok(Value::scalar(x.size()))
+            } else if let Some(x) = input.as_object() {
+                Ok(Value::scalar(x.size()))
+            } else {
+                Ok(Value::scalar(0i32))
             }
         }
     }


Test patch:
diff --git a/tests/conformance_jekyll/slugify_test.rs b/tests/conformance_jekyll/slugify_test.rs
index 460eaed..b724964 100644
--- a/tests/conformance_jekyll/slugify_test.rs
+++ b/tests/conformance_jekyll/slugify_test.rs
@@ -1,15 +1,20 @@
 #[test]
 fn test_slugify_default() {
     assert_eq!(
+        liquid_core::call_filter!(liquid::filters::jekyll::Slugify, " Q*bert says @!#@!").unwrap(),
         v!("q-bert-says"),
-        jekyll_filters!(Slugify, v!(" Q*bert says @!#@!"))
     );
 }
 
 #[test]
 fn test_slugify_pretty() {
     assert_eq!(
+        liquid_core::call_filter!(
+            liquid::filters::jekyll::Slugify,
+            " Q*bert says _@!#?@!",
+            "pretty"
+        )
+        .unwrap(),
         v!("q-bert-says-_@!-@!"),
-        jekyll_filters!(Slugify, v!(" Q*bert says _@!#?@!"), v!("pretty"))
     );
 }
diff --git a/tests/conformance_ruby/filter_test.rs b/tests/conformance_ruby/filter_test.rs
index ececc39..7d52526 100644
--- a/tests/conformance_ruby/filter_test.rs
+++ b/tests/conformance_ruby/filter_test.rs
@@ -12,7 +12,7 @@ pub struct MoneyFilterParser;
 pub struct MoneyFilter;
 
 impl Filter for MoneyFilter {
-    fn evaluate(&self, input: &Value, _context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, input: &dyn ValueView, _context: &Context<'_>) -> Result<Value> {
         Ok(Value::scalar(format!(" {}$ ", input.render())))
     }
 }
@@ -30,7 +30,7 @@ pub struct MoneyWithUnderscoreFilterParser;
 pub struct MoneyWithUnderscoreFilter;
 
 impl Filter for MoneyWithUnderscoreFilter {
-    fn evaluate(&self, input: &Value, _context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, input: &dyn ValueView, _context: &Context<'_>) -> Result<Value> {
         Ok(Value::scalar(format!(" {}$ ", input.render())))
     }
 }
@@ -56,7 +56,7 @@ pub struct SubstituteFilterParser;
 pub struct SubstituteFilter;
 
 impl Filter for SubstituteFilter {
-    fn evaluate(&self, input: &Value, _context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, input: &dyn ValueView, _context: &Context<'_>) -> Result<Value> {
         Ok(Value::scalar(format!(
             "No keyword argument support: {}",
             input.render()
diff --git a/tests/conformance_ruby/output_test.rs b/tests/conformance_ruby/output_test.rs
index b2a1b80..c530f13 100644
--- a/tests/conformance_ruby/output_test.rs
+++ b/tests/conformance_ruby/output_test.rs
@@ -17,7 +17,7 @@ pub struct MakeFunnyFilterParser;
 pub struct MakeFunnyFilter;
 
 impl Filter for MakeFunnyFilter {
-    fn evaluate(&self, _input: &Value, _context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, _input: &dyn ValueView, _context: &Context<'_>) -> Result<Value> {
         Ok(Value::scalar("LOL"))
     }
 }
@@ -35,7 +35,7 @@ pub struct CiteFunnyFilterParser;
 pub struct CiteFunnyFilter;
 
 impl Filter for CiteFunnyFilter {
-    fn evaluate(&self, input: &Value, _context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, input: &dyn ValueView, _context: &Context<'_>) -> Result<Value> {
         Ok(Value::scalar(format!("LOL: {}", input.render())))
     }
 }
@@ -63,7 +63,7 @@ pub struct AddSmileyFilter {
 }
 
 impl Filter for AddSmileyFilter {
-    fn evaluate(&self, input: &Value, context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, input: &dyn ValueView, context: &Context<'_>) -> Result<Value> {
         let args = self.args.evaluate(context)?;
         let smiley = args.smiley.unwrap_or(":-)".into()).to_string();
         Ok(Value::scalar(format!("{} {}", input.render(), smiley)))
@@ -96,7 +96,7 @@ pub struct AddTagFilter {
 }
 
 impl Filter for AddTagFilter {
-    fn evaluate(&self, input: &Value, context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, input: &dyn ValueView, context: &Context<'_>) -> Result<Value> {
         let args = self.args.evaluate(context)?;
 
         let tag = args.tag.unwrap_or("p".into()).to_string();
@@ -124,7 +124,7 @@ pub struct ParagraphFilterParser;
 pub struct ParagraphFilter;
 
 impl Filter for ParagraphFilter {
-    fn evaluate(&self, input: &Value, _context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, input: &dyn ValueView, _context: &Context<'_>) -> Result<Value> {
         Ok(Value::scalar(format!("<p>{}</p>", input.render())))
     }
 }
@@ -152,7 +152,7 @@ pub struct LinkToFilter {
 }
 
 impl Filter for LinkToFilter {
-    fn evaluate(&self, input: &Value, context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, input: &dyn ValueView, context: &Context<'_>) -> Result<Value> {
         let args = self.args.evaluate(context)?;
 
         let name = input;
diff --git a/tests/conformance_ruby/standard_filter_test.rs b/tests/conformance_ruby/standard_filter_test.rs
index aa3da0c..5ecbd64 100644
--- a/tests/conformance_ruby/standard_filter_test.rs
+++ b/tests/conformance_ruby/standard_filter_test.rs
@@ -2,100 +2,228 @@ use crate::test_helper::*;
 
 #[test]
 fn test_size() {
-    assert_eq!(v!(3), filters!(Size, v!([1, 2, 3])));
-    assert_eq!(v!(0), filters!(Size, v!([])));
-    assert_eq!(v!(0), filters!(Size, v!(nil)));
+    assert_eq!(
+        v!(3),
+        call_filter!(liquid::filters::std::Size, v!([1, 2, 3])).unwrap()
+    );
+    assert_eq!(
+        v!(0),
+        call_filter!(liquid::filters::std::Size, v!([])).unwrap()
+    );
+    assert_eq!(
+        v!(0),
+        call_filter!(liquid::filters::std::Size, v!(nil)).unwrap()
+    );
 }
 
 #[test]
 fn test_downcase() {
-    assert_eq!(v!("testing"), filters!(Downcase, v!("Testing")));
-    assert_eq!(v!(""), filters!(Downcase, Nil));
+    assert_eq!(
+        v!("testing"),
+        call_filter!(liquid::filters::std::Downcase, v!("Testing")).unwrap()
+    );
+    assert_eq!(
+        v!(""),
+        call_filter!(liquid::filters::std::Downcase, Nil).unwrap()
+    );
 }
 
 #[test]
 fn test_upcase() {
-    assert_eq!(v!("TESTING"), filters!(Upcase, v!("Testing")));
-    assert_eq!(v!(""), filters!(Upcase, Nil));
+    assert_eq!(
+        v!("TESTING"),
+        call_filter!(liquid::filters::std::Upcase, v!("Testing")).unwrap()
+    );
+    assert_eq!(
+        v!(""),
+        call_filter!(liquid::filters::std::Upcase, Nil).unwrap()
+    );
 }
 
 #[test]
 #[should_panic] // liquid-rust#261
 fn test_slice() {
-    assert_eq!(v!("oob"), filters!(Slice, v!("foobar"), v!(1), v!(3)));
-    assert_eq!(v!("oobar"), filters!(Slice, v!("foobar"), v!(1), v!(1000)));
-    assert_eq!(v!(""), filters!(Slice, v!("foobar"), v!(1), v!(0)));
-    assert_eq!(v!("o"), filters!(Slice, v!("foobar"), v!(1), v!(1)));
-    assert_eq!(v!("bar"), filters!(Slice, v!("foobar"), v!(3), v!(3)));
-    assert_eq!(v!("ar"), filters!(Slice, v!("foobar"), v!(-2), v!(2)));
-    assert_eq!(v!("ar"), filters!(Slice, v!("foobar"), v!(-2), v!(1000)));
-    assert_eq!(v!("r"), filters!(Slice, v!("foobar"), v!(-1)));
-    assert_eq!(v!(""), filters!(Slice, Nil, v!(0)));
-    assert_eq!(v!(""), filters!(Slice, v!("foobar"), v!(100), v!(10)));
-    assert_eq!(v!(""), filters!(Slice, v!("foobar"), v!(-100), v!(10)));
-    assert_eq!(v!("oob"), filters!(Slice, v!("foobar"), v!("1"), v!("3")));
-    filters_fail!(Slice, v!("foobar"), Nil);
-    filters_fail!(Slice, v!("foobar"), v!(0), v!(""));
+    assert_eq!(
+        v!("oob"),
+        call_filter!(liquid::filters::std::Slice, v!("foobar"), v!(1), v!(3)).unwrap()
+    );
+    assert_eq!(
+        v!("oobar"),
+        call_filter!(liquid::filters::std::Slice, v!("foobar"), v!(1), v!(1000)).unwrap()
+    );
+    assert_eq!(
+        v!(""),
+        call_filter!(liquid::filters::std::Slice, v!("foobar"), v!(1), v!(0)).unwrap()
+    );
+    assert_eq!(
+        v!("o"),
+        call_filter!(liquid::filters::std::Slice, v!("foobar"), v!(1), v!(1)).unwrap()
+    );
+    assert_eq!(
+        v!("bar"),
+        call_filter!(liquid::filters::std::Slice, v!("foobar"), v!(3), v!(3)).unwrap()
+    );
+    assert_eq!(
+        v!("ar"),
+        call_filter!(liquid::filters::std::Slice, v!("foobar"), v!(-2), v!(2)).unwrap()
+    );
+    assert_eq!(
+        v!("ar"),
+        call_filter!(liquid::filters::std::Slice, v!("foobar"), v!(-2), v!(1000)).unwrap()
+    );
+    assert_eq!(
+        v!("r"),
+        call_filter!(liquid::filters::std::Slice, v!("foobar"), v!(-1)).unwrap()
+    );
+    assert_eq!(
+        v!(""),
+        call_filter!(liquid::filters::std::Slice, Nil, v!(0)).unwrap()
+    );
+    assert_eq!(
+        v!(""),
+        call_filter!(liquid::filters::std::Slice, v!("foobar"), v!(100), v!(10)).unwrap()
+    );
+    assert_eq!(
+        v!(""),
+        call_filter!(liquid::filters::std::Slice, v!("foobar"), v!(-100), v!(10)).unwrap()
+    );
+    assert_eq!(
+        v!("oob"),
+        call_filter!(liquid::filters::std::Slice, v!("foobar"), v!("1"), v!("3")).unwrap()
+    );
+    liquid_core::call_filter!(liquid::filters::std::Slice, "foobar", Nil).unwrap_err();
+    liquid_core::call_filter!(liquid::filters::std::Slice, "foobar", 0, "").unwrap_err();
 }
 
 #[test]
 #[should_panic] // liquid-rust#261
 fn test_slice_on_arrays() {
     let input = v!(["f", "o", "o", "b", "a", "r"]);
-    assert_eq!(v!(["o", "o", "b"]), filters!(Slice, input, v!(1), v!(3)));
+    assert_eq!(
+        v!(["o", "o", "b"]),
+        call_filter!(liquid::filters::std::Slice, input.clone(), v!(1), v!(3)).unwrap()
+    );
     assert_eq!(
         v!(["o", "o", "b", "a", "r"]),
-        filters!(Slice, input, v!(1), v!(1000))
+        call_filter!(liquid::filters::std::Slice, input.clone(), v!(1), v!(1000)).unwrap()
+    );
+    assert_eq!(
+        v!([]),
+        call_filter!(liquid::filters::std::Slice, input.clone(), v!(1), v!(0)).unwrap()
+    );
+    assert_eq!(
+        v!(["o"]),
+        call_filter!(liquid::filters::std::Slice, input.clone(), v!(1), v!(1)).unwrap()
+    );
+    assert_eq!(
+        v!(["b", "a", "r"]),
+        call_filter!(liquid::filters::std::Slice, input.clone(), v!(3), v!(3)).unwrap()
+    );
+    assert_eq!(
+        v!(["a", "r"]),
+        call_filter!(liquid::filters::std::Slice, input.clone(), v!(-2), v!(2)).unwrap()
+    );
+    assert_eq!(
+        v!(["a", "r"]),
+        call_filter!(liquid::filters::std::Slice, input.clone(), v!(-2), v!(1000)).unwrap()
+    );
+    assert_eq!(
+        v!(["r"]),
+        call_filter!(liquid::filters::std::Slice, input.clone(), v!(-1)).unwrap()
+    );
+    assert_eq!(
+        v!([]),
+        call_filter!(liquid::filters::std::Slice, input.clone(), v!(100), v!(10)).unwrap()
+    );
+    assert_eq!(
+        v!([]),
+        call_filter!(liquid::filters::std::Slice, input.clone(), v!(-100), v!(10)).unwrap()
     );
-    assert_eq!(v!([]), filters!(Slice, input, v!(1), v!(0)));
-    assert_eq!(v!(["o"]), filters!(Slice, input, v!(1), v!(1)));
-    assert_eq!(v!(["b", "a", "r"]), filters!(Slice, input, v!(3), v!(3)));
-    assert_eq!(v!(["a", "r"]), filters!(Slice, input, v!(-2), v!(2)));
-    assert_eq!(v!(["a", "r"]), filters!(Slice, input, v!(-2), v!(1000)));
-    assert_eq!(v!(["r"]), filters!(Slice, input, v!(-1)));
-    assert_eq!(v!([]), filters!(Slice, input, v!(100), v!(10)));
-    assert_eq!(v!([]), filters!(Slice, input, v!(-100), v!(10)));
 }
 
 #[test]
 #[should_panic] // liquid-rust#264
 fn test_truncate() {
-    assert_eq!(v!("1234..."), filters!(Truncate, v!("1234567890"), v!(7)));
+    assert_eq!(
+        v!("1234..."),
+        call_filter!(liquid::filters::std::Truncate, v!("1234567890"), v!(7)).unwrap()
+    );
+    assert_eq!(
+        v!("1234567890"),
+        call_filter!(liquid::filters::std::Truncate, v!("1234567890"), v!(20)).unwrap()
+    );
+    assert_eq!(
+        v!("..."),
+        call_filter!(liquid::filters::std::Truncate, v!("1234567890"), v!(0)).unwrap()
+    );
     assert_eq!(
         v!("1234567890"),
-        filters!(Truncate, v!("1234567890"), v!(20))
+        call_filter!(liquid::filters::std::Truncate, v!("1234567890")).unwrap()
     );
-    assert_eq!(v!("..."), filters!(Truncate, v!("1234567890"), v!(0)));
-    assert_eq!(v!("1234567890"), filters!(Truncate, v!("1234567890")));
     assert_eq!(
         v!("测试..."),
-        filters!(Truncate, v!("测试测试测试测试"), v!(5))
+        call_filter!(
+            liquid::filters::std::Truncate,
+            v!("测试测试测试测试"),
+            v!(5)
+        )
+        .unwrap()
     );
     assert_eq!(
         v!("12341"),
-        filters!(Truncate, v!("1234567890"), v!(5), v!(1))
+        call_filter!(
+            liquid::filters::std::Truncate,
+            v!("1234567890"),
+            v!(5),
+            v!(1)
+        )
+        .unwrap()
     );
 }
 
 #[test]
 #[should_panic] // liquid-rust#263
 fn test_split() {
-    assert_eq!(v!(["12", "34"]), filters!(Split, v!("12~34"), v!("~")));
+    assert_eq!(
+        v!(["12", "34"]),
+        call_filter!(liquid::filters::std::Split, v!("12~34"), v!("~")).unwrap()
+    );
     assert_eq!(
         v!(["A? ", " ,Z"]),
-        filters!(Split, v!("A? ~ ~ ~ ,Z"), v!("~ ~ ~"))
+        call_filter!(liquid::filters::std::Split, v!("A? ~ ~ ~ ,Z"), v!("~ ~ ~")).unwrap()
+    );
+    assert_eq!(
+        v!(["A?Z"]),
+        call_filter!(liquid::filters::std::Split, v!("A?Z"), v!("~")).unwrap()
+    );
+    assert_eq!(
+        v!([]),
+        call_filter!(liquid::filters::std::Split, Nil, v!(" ")).unwrap()
+    );
+    assert_eq!(
+        v!(["A", "Z"]),
+        call_filter!(liquid::filters::std::Split, v!("A1Z"), v!(1)).unwrap()
     );
-    assert_eq!(v!(["A?Z"]), filters!(Split, v!("A?Z"), v!("~")));
-    assert_eq!(v!([]), filters!(Split, Nil, v!(" ")));
-    assert_eq!(v!(["A", "Z"]), filters!(Split, v!("A1Z"), v!(1)));
 }
 
 #[test]
 fn test_escape() {
-    assert_eq!(v!("&lt;strong&gt;"), filters!(Escape, v!("<strong>")));
-    assert_eq!(v!("1"), filters!(Escape, v!(1)));
-    assert_eq!(v!("2001-02-03"), filters!(Escape, date(2001, 2, 3)));
-    assert_eq!(Nil, filters!(Escape, Nil));
+    assert_eq!(
+        v!("&lt;strong&gt;"),
+        call_filter!(liquid::filters::std::Escape, v!("<strong>")).unwrap()
+    );
+    assert_eq!(
+        v!("1"),
+        call_filter!(liquid::filters::std::Escape, v!(1)).unwrap()
+    );
+    assert_eq!(
+        v!("2001-02-03"),
+        call_filter!(liquid::filters::std::Escape, date(2001, 2, 3)).unwrap()
+    );
+    assert_eq!(
+        Nil,
+        call_filter!(liquid::filters::std::Escape, Nil).unwrap()
+    );
 }
 
 #[test]
@@ -103,10 +231,10 @@ fn test_escape() {
 fn test_h() {
     panic!("Implement this filter");
     /*
-    assert_eq!(v!("&lt;strong&gt;"), filters!(h, v!("<strong>")));
-    assert_eq!(v!("1"), filters!(h, 1));
-    assert_eq!(v!("2001-02-03"), filters!(h, date(2001, 2, 3)));
-    assert_eq!(Nil, filters!(h, Nil));
+    assert_eq!(v!("&lt;strong&gt;"), call_filter!(liquid::filters::std::h, v!("<strong>")));
+    assert_eq!(v!("1"), call_filter!(liquid::filters::std::h, 1));
+    assert_eq!(v!("2001-02-03"), call_filter!(liquid::filters::std::h, date(2001, 2, 3)));
+    assert_eq!(Nil, call_filter!(liquid::filters::std::h, Nil));
     */
 }
 
@@ -114,7 +242,11 @@ fn test_h() {
 fn test_escape_once() {
     assert_eq!(
         v!("&lt;strong&gt;Hulk&lt;/strong&gt;"),
-        filters!(EscapeOnce, v!("&lt;strong&gt;Hulk</strong>"))
+        call_filter!(
+            liquid::filters::std::EscapeOnce,
+            v!("&lt;strong&gt;Hulk</strong>")
+        )
+        .unwrap()
     );
 }
 
@@ -122,113 +254,195 @@ fn test_escape_once() {
 fn test_url_encode() {
     assert_eq!(
         v!("foo%2B1%40example.com"),
-        filters!(UrlEncode, v!("foo+1@example.com"))
+        call_filter!(liquid::filters::std::UrlEncode, v!("foo+1@example.com")).unwrap()
+    );
+    assert_eq!(
+        v!("1"),
+        call_filter!(liquid::filters::std::UrlEncode, v!(1)).unwrap()
+    );
+    assert_eq!(
+        v!("2001-02-03"),
+        call_filter!(liquid::filters::std::UrlEncode, date(2001, 2, 3)).unwrap()
+    );
+    assert_eq!(
+        Nil,
+        call_filter!(liquid::filters::std::UrlEncode, Nil).unwrap()
     );
-    assert_eq!(v!("1"), filters!(UrlEncode, v!(1)));
-    assert_eq!(v!("2001-02-03"), filters!(UrlEncode, date(2001, 2, 3)));
-    assert_eq!(Nil, filters!(UrlEncode, Nil));
 }
 
 #[test]
 fn test_url_decode() {
-    assert_eq!(v!("foo bar"), filters!(UrlDecode, v!("foo+bar")));
-    assert_eq!(v!("foo bar"), filters!(UrlDecode, v!("foo%20bar")));
+    assert_eq!(
+        v!("foo bar"),
+        call_filter!(liquid::filters::std::UrlDecode, v!("foo+bar")).unwrap()
+    );
+    assert_eq!(
+        v!("foo bar"),
+        call_filter!(liquid::filters::std::UrlDecode, v!("foo%20bar")).unwrap()
+    );
     assert_eq!(
         v!("foo+1@example.com"),
-        filters!(UrlDecode, v!("foo%2B1%40example.com"))
+        call_filter!(liquid::filters::std::UrlDecode, v!("foo%2B1%40example.com")).unwrap()
+    );
+    assert_eq!(
+        v!("1"),
+        call_filter!(liquid::filters::std::UrlDecode, v!(1)).unwrap()
+    );
+    assert_eq!(
+        v!("2001-02-03"),
+        call_filter!(liquid::filters::std::UrlDecode, date(2001, 2, 3)).unwrap()
+    );
+    assert_eq!(
+        Nil,
+        call_filter!(liquid::filters::std::UrlDecode, Nil).unwrap()
     );
-    assert_eq!(v!("1"), filters!(UrlDecode, v!(1)));
-    assert_eq!(v!("2001-02-03"), filters!(UrlDecode, date(2001, 2, 3)));
-    assert_eq!(Nil, filters!(UrlDecode, Nil));
 }
 
 #[test]
 fn test_truncatewords() {
     assert_eq!(
         v!("one two three"),
-        filters!(TruncateWords, v!("one two three"), v!(4))
+        call_filter!(
+            liquid::filters::std::TruncateWords,
+            v!("one two three"),
+            v!(4)
+        )
+        .unwrap()
     );
     assert_eq!(
         v!("one two..."),
-        filters!(TruncateWords, v!("one two three"), v!(2))
+        call_filter!(
+            liquid::filters::std::TruncateWords,
+            v!("one two three"),
+            v!(2)
+        )
+        .unwrap()
     );
     assert_eq!(
         v!("one two three"),
-        filters!(TruncateWords, v!("one two three"))
+        call_filter!(liquid::filters::std::TruncateWords, v!("one two three")).unwrap()
     );
-    assert_eq!(v!("Two small (13&#8221; x 5.5&#8221; x 10&#8221; high) baskets fit inside one large basket (13&#8221;..."), filters!(TruncateWords, v!("Two small (13&#8221; x 5.5&#8221; x 10&#8221; high) baskets fit inside one large basket (13&#8221; x 16&#8221; x 10.5&#8221; high) with cover."), v!(15)));
+    assert_eq!(v!("Two small (13&#8221; x 5.5&#8221; x 10&#8221; high) baskets fit inside one large basket (13&#8221;..."), call_filter!(liquid::filters::std::TruncateWords, v!("Two small (13&#8221; x 5.5&#8221; x 10&#8221; high) baskets fit inside one large basket (13&#8221; x 16&#8221; x 10.5&#8221; high) with cover."), v!(15)).unwrap());
     assert_eq!(
         v!("测试测试测试测试"),
-        filters!(TruncateWords, v!("测试测试测试测试"), v!(5))
+        call_filter!(
+            liquid::filters::std::TruncateWords,
+            v!("测试测试测试测试"),
+            v!(5)
+        )
+        .unwrap()
     );
     assert_eq!(
         v!("one two1"),
-        filters!(TruncateWords, v!("one two three"), v!(2), v!(1))
+        call_filter!(
+            liquid::filters::std::TruncateWords,
+            v!("one two three"),
+            v!(2),
+            v!(1)
+        )
+        .unwrap()
     );
 }
 
 #[test]
 fn test_strip_html() {
-    assert_eq!(v!("test"), filters!(StripHtml, v!(r#"<div>test</div>"#)));
     assert_eq!(
         v!("test"),
-        filters!(StripHtml, v!(r#"<div id="test">test</div>"#))
+        call_filter!(liquid::filters::std::StripHtml, v!(r#"<div>test</div>"#)).unwrap()
+    );
+    assert_eq!(
+        v!("test"),
+        call_filter!(
+            liquid::filters::std::StripHtml,
+            v!(r#"<div id="test">test</div>"#)
+        )
+        .unwrap()
     );
     assert_eq!(
         v!(""),
-        filters!(
-            StripHtml,
+        call_filter!(
+            liquid::filters::std::StripHtml,
             v!(r#"<script type="text/javascript">document.write"some stuff";</script>"#)
         )
+        .unwrap()
     );
     assert_eq!(
         v!(""),
-        filters!(StripHtml, v!(r#"<style type="text/css">foo bar</style>"#))
+        call_filter!(
+            liquid::filters::std::StripHtml,
+            v!(r#"<style type="text/css">foo bar</style>"#)
+        )
+        .unwrap()
     );
     assert_eq!(
         v!("test"),
-        filters!(StripHtml, v!(r#"<div\nclass="multiline">test</div>"#))
+        call_filter!(
+            liquid::filters::std::StripHtml,
+            v!(r#"<div\nclass="multiline">test</div>"#)
+        )
+        .unwrap()
     );
     assert_eq!(
         v!("test"),
-        filters!(StripHtml, v!(r#"<!-- foo bar \n test -->test"#))
+        call_filter!(
+            liquid::filters::std::StripHtml,
+            v!(r#"<!-- foo bar \n test -->test"#)
+        )
+        .unwrap()
+    );
+    assert_eq!(
+        v!(""),
+        call_filter!(liquid::filters::std::StripHtml, Nil).unwrap()
     );
-    assert_eq!(v!(""), filters!(StripHtml, Nil));
 }
 
 #[test]
 fn test_join() {
-    assert_eq!(v!("1 2 3 4"), filters!(Join, v!([1, 2, 3, 4])));
+    assert_eq!(
+        v!("1 2 3 4"),
+        call_filter!(liquid::filters::std::Join, v!([1, 2, 3, 4])).unwrap()
+    );
     assert_eq!(
         v!("1 - 2 - 3 - 4"),
-        filters!(Join, v!([1, 2, 3, 4]), v!(" - "))
+        call_filter!(liquid::filters::std::Join, v!([1, 2, 3, 4]), v!(" - ")).unwrap()
+    );
+    assert_eq!(
+        v!("1121314"),
+        call_filter!(liquid::filters::std::Join, v!([1, 2, 3, 4]), v!(1)).unwrap()
     );
-    assert_eq!(v!("1121314"), filters!(Join, v!([1, 2, 3, 4]), v!(1)));
 }
 
 #[test]
 fn test_sort() {
-    assert_eq!(v!([1, 2, 3, 4]), filters!(Sort, v!([4, 3, 2, 1])));
+    assert_eq!(
+        v!([1, 2, 3, 4]),
+        call_filter!(liquid::filters::std::Sort, v!([4, 3, 2, 1])).unwrap()
+    );
     assert_eq!(
         v!([{ "a": 1 }, { "a": 2 }, { "a": 3 }, { "a": 4 }]),
-        filters!(
-            Sort,
+        call_filter!(
+            liquid::filters::std::Sort,
             v!([{ "a": 4 }, { "a": 3 }, { "a": 1 }, { "a": 2 }]),
-            v!("a")
+            "a"
         )
+        .unwrap()
     );
 }
 
 #[test]
 fn test_sort_with_nils() {
-    assert_eq!(v!([1, 2, 3, 4, nil]), filters!(Sort, v!([nil, 4, 3, 2, 1])));
+    assert_eq!(
+        v!([1, 2, 3, 4, nil]),
+        call_filter!(liquid::filters::std::Sort, v!([nil, 4, 3, 2, 1])).unwrap()
+    );
     assert_eq!(
         v!([{ "a": 1 }, { "a": 2 }, { "a": 3 }, { "a": 4 }, {}]),
-        filters!(
-            Sort,
+        call_filter!(
+            liquid::filters::std::Sort,
             v!([{ "a": 4 }, { "a": 3 }, {}, { "a": 1 }, { "a": 2 }]),
-            v!("a")
+            "a"
         )
+        .unwrap()
     );
 }
 
@@ -254,7 +468,7 @@ fn test_sort_when_property_is_sometimes_missing_puts_nils_last() {
         vec![v!({ "handle": "delta" }), v!({ "handle": "beta" })],
         vec![v!({ "handle": "beta" }), v!({ "handle": "delta" })],
     );
-    let result = filters!(Sort, input, v!("price"));
+    let result = call_filter!(liquid::filters::std::Sort, input, v!("price")).unwrap();
     let result = result.into_array();
     assert!(result.is_some());
     let result = result.unwrap();
@@ -266,15 +480,16 @@ fn test_sort_when_property_is_sometimes_missing_puts_nils_last() {
 fn test_sort_natural() {
     assert_eq!(
         v!(["a", "B", "c", "D"]),
-        filters!(SortNatural, v!(["c", "D", "a", "B"]))
+        call_filter!(liquid::filters::std::SortNatural, v!(["c", "D", "a", "B"])).unwrap()
     );
     assert_eq!(
         v!([{ "a": "a" }, { "a": "B" }, { "a": "c" }, { "a": "D" }]),
-        filters!(
-            SortNatural,
+        call_filter!(
+            liquid::filters::std::SortNatural,
             v!([{ "a": "D" }, { "a": "c" }, { "a": "a" }, { "a": "B" }]),
-            v!("a")
+            "a"
         )
+        .unwrap()
     );
 }
 
@@ -282,15 +497,20 @@ fn test_sort_natural() {
 fn test_sort_natural_with_nils() {
     assert_eq!(
         v!(["a", "B", "c", "D", nil]),
-        filters!(SortNatural, v!([nil, "c", "D", "a", "B"]))
+        call_filter!(
+            liquid::filters::std::SortNatural,
+            v!([nil, "c", "D", "a", "B"])
+        )
+        .unwrap()
     );
     assert_eq!(
         v!([{ "a": "a" }, { "a": "B" }, { "a": "c" }, { "a": "D" }, {}]),
-        filters!(
-            SortNatural,
+        call_filter!(
+            liquid::filters::std::SortNatural,
             v!([{ "a": "D" }, { "a": "c" }, {}, { "a": "a" }, { "a": "B" }]),
-            v!("a")
+            "a"
         )
+        .unwrap()
     );
 }
 
@@ -316,7 +536,7 @@ fn test_sort_natural_when_property_is_sometimes_missing_puts_nils_last() {
         vec![v!({ "handle": "delta" }), v!({ "handle": "beta" })],
         vec![v!({ "handle": "beta" }), v!({ "handle": "delta" })],
     );
-    let result = filters!(SortNatural, input, v!("price"));
+    let result = call_filter!(liquid::filters::std::SortNatural, input, v!("price")).unwrap();
     let result = result.into_array();
     assert!(result.is_some());
     let result = result.unwrap();
@@ -344,60 +564,97 @@ fn test_sort_natural_case_check() {
       { "key": "Z" },
       { "fake": "t" }
     ]);
-    assert_eq!(expectation, filters!(SortNatural, input, v!("key")));
+    assert_eq!(
+        expectation,
+        call_filter!(liquid::filters::std::SortNatural, input, "key").unwrap()
+    );
     assert_eq!(
         v!(["a", "b", "c", "X", "Y", "Z"]),
-        filters!(SortNatural, v!(["X", "Y", "Z", "a", "b", "c"]))
+        call_filter!(
+            liquid::filters::std::SortNatural,
+            v!(["X", "Y", "Z", "a", "b", "c"])
+        )
+        .unwrap()
     );
 }
 
 #[test]
 fn test_sort_empty_array() {
-    assert_eq!(v!([]), filters!(Sort, v!([]), v!("a")));
+    assert_eq!(
+        v!([]),
+        call_filter!(liquid::filters::std::Sort, v!([]), v!("a")).unwrap()
+    );
 }
 
 #[test]
 fn test_sort_natural_empty_array() {
-    assert_eq!(v!([]), filters!(SortNatural, v!([]), v!("a")));
+    assert_eq!(
+        v!([]),
+        call_filter!(liquid::filters::std::SortNatural, v!([]), v!("a")).unwrap()
+    );
 }
 
 #[test]
 fn test_legacy_sort_hash() {
     assert_eq!(
         v!([{ "a": 1, "b": 2 }]),
-        filters!(Sort, v!({ "a": 1, "b": 2 }))
+        call_filter!(liquid::filters::std::Sort, v!({ "a": 1, "b": 2 })).unwrap()
     );
 }
 
 #[test]
 fn test_numerical_vs_lexicographical_sort() {
-    assert_eq!(v!([2, 10]), filters!(Sort, v!([10, 2])));
+    assert_eq!(
+        v!([2, 10]),
+        call_filter!(liquid::filters::std::Sort, v!([10, 2])).unwrap()
+    );
     assert_eq!(
         v!([{ "a": 2 }, { "a": 10 }]),
-        filters!(Sort, v!([{ "a": 10 }, { "a": 2 }]), v!("a"))
+        call_filter!(
+            liquid::filters::std::Sort,
+            v!([{ "a": 10 }, { "a": 2 }]),
+            "a"
+        )
+        .unwrap()
+    );
+    assert_eq!(
+        v!(["10", "2"]),
+        call_filter!(liquid::filters::std::Sort, v!(["10", "2"])).unwrap()
     );
-    assert_eq!(v!(["10", "2"]), filters!(Sort, v!(["10", "2"])));
     assert_eq!(
         v!([{ "a": "10" }, { "a": "2" }]),
-        filters!(Sort, v!([{ "a": "10" }, { "a": "2" }]), v!("a"))
+        call_filter!(
+            liquid::filters::std::Sort,
+            v!([{ "a": "10" }, { "a": "2" }]),
+            "a"
+        )
+        .unwrap()
     );
 }
 
 #[test]
 #[should_panic] // liquid-rust#266
 fn test_uniq() {
-    assert_eq!(v!(["foo"]), filters!(Uniq, v!("foo")));
+    assert_eq!(
+        v!(["foo"]),
+        call_filter!(liquid::filters::std::Uniq, v!("foo")).unwrap()
+    );
     assert_eq!(
         v!([1, 3, 2, 4]),
-        filters!(Uniq, v!([1, 1, 3, 2, 3, 1, 4, 3, 2, 1]))
+        call_filter!(
+            liquid::filters::std::Uniq,
+            v!([1, 1, 3, 2, 3, 1, 4, 3, 2, 1])
+        )
+        .unwrap()
     );
     assert_eq!(
         v!([{ "a": 1 }, { "a": 3 }, { "a": 2 }]),
-        filters!(
-            Uniq,
+        call_filter!(
+            liquid::filters::std::Uniq,
             v!([{ "a": 1 }, { "a": 3 }, { "a": 1 }, { "a": 2 }]),
-            v!("a")
+            "a"
         )
+        .unwrap()
     );
     //testdrop: Implementation specific: Drops
 }
@@ -405,24 +662,33 @@ fn test_uniq() {
 #[test]
 #[should_panic] // liquid-rust#267
 fn test_uniq_empty_array() {
-    assert_eq!(v!([]), filters!(Uniq, v!([]), v!("a")));
+    assert_eq!(
+        v!([]),
+        call_filter!(liquid::filters::std::Uniq, v!([]), v!("a")).unwrap()
+    );
 }
 
 #[test]
 fn test_compact_empty_array() {
-    assert_eq!(v!([]), filters!(Compact, v!([]), v!("a")));
+    assert_eq!(
+        v!([]),
+        call_filter!(liquid::filters::std::Compact, v!([]), v!("a")).unwrap()
+    );
 }
 
 #[test]
 fn test_compact_invalid_property() {
     let input = v!([[1], [2], [3]]);
 
-    filters_fail!(Compact, input, v!("bar"));
+    liquid_core::call_filter!(liquid::filters::std::Compact, input, "bar").unwrap_err();
 }
 
 #[test]
 fn test_reverse() {
-    assert_eq!(v!([4, 3, 2, 1]), filters!(Reverse, v!([1, 2, 3, 4])));
+    assert_eq!(
+        v!([4, 3, 2, 1]),
+        call_filter!(liquid::filters::std::Reverse, v!([1, 2, 3, 4])).unwrap()
+    );
 }
 
 #[test]
@@ -430,7 +696,7 @@ fn test_reverse() {
 fn test_legacy_reverse_hash() {
     assert_eq!(
         v!([{ "a": 1, "b": 2 }]),
-        filters!(Reverse, v!({"a": 1, "b": 2}))
+        call_filter!(liquid::filters::std::Reverse, v!({"a": 1, "b": 2})).unwrap()
     );
 }
 
@@ -438,11 +704,12 @@ fn test_legacy_reverse_hash() {
 fn test_map() {
     assert_eq!(
         v!([1, 2, 3, 4]),
-        filters!(
-            Map,
+        call_filter!(
+            liquid::filters::std::Map,
             v!([{ "a": 1 }, { "a": 2 }, { "a": 3 }, { "a": 4 }]),
-            v!("a")
+            "a"
         )
+        .unwrap()
     );
     assert_template_result!(
         "abc",
@@ -528,109 +795,164 @@ fn test_truncate_calls_to_liquid() {
 fn test_date() {
     assert_eq!(
         v!("May"),
-        filters!(Date, with_time("2006-05-05 10:00:00"), v!("%B"))
+        call_filter!(
+            liquid::filters::std::Date,
+            with_time("2006-05-05 10:00:00"),
+            "%B"
+        )
+        .unwrap()
     );
     assert_eq!(
         v!("June"),
-        filters!(Date, with_time("2006-06-05 10:00:00"), v!("%B"))
+        call_filter!(
+            liquid::filters::std::Date,
+            with_time("2006-06-05 10:00:00"),
+            "%B"
+        )
+        .unwrap()
     );
     assert_eq!(
         v!("July"),
-        filters!(Date, with_time("2006-07-05 10:00:00"), v!("%B"))
+        call_filter!(
+            liquid::filters::std::Date,
+            with_time("2006-07-05 10:00:00"),
+            "%B"
+        )
+        .unwrap()
     );
 
     assert_eq!(
         v!("May"),
-        filters!(Date, v!("2006-05-05 10:00:00"), v!("%B"))
+        call_filter!(liquid::filters::std::Date, "2006-05-05 10:00:00", "%B").unwrap()
     );
     assert_eq!(
         v!("June"),
-        filters!(Date, v!("2006-06-05 10:00:00"), v!("%B"))
+        call_filter!(liquid::filters::std::Date, "2006-06-05 10:00:00", "%B").unwrap()
     );
     assert_eq!(
         v!("July"),
-        filters!(Date, v!("2006-07-05 10:00:00"), v!("%B"))
+        call_filter!(liquid::filters::std::Date, "2006-07-05 10:00:00", "%B").unwrap()
     );
 
     assert_eq!(
         v!("2006-07-05 10:00:00"),
-        filters!(Date, v!("2006-07-05 10:00:00"), v!(""))
+        call_filter!(liquid::filters::std::Date, "2006-07-05 10:00:00", "").unwrap()
     );
     assert_eq!(
         v!("2006-07-05 10:00:00"),
-        filters!(Date, v!("2006-07-05 10:00:00"), v!(""))
+        call_filter!(liquid::filters::std::Date, "2006-07-05 10:00:00", "").unwrap()
     );
     assert_eq!(
         v!("2006-07-05 10:00:00"),
-        filters!(Date, v!("2006-07-05 10:00:00"), v!(""))
+        call_filter!(liquid::filters::std::Date, "2006-07-05 10:00:00", "").unwrap()
     );
     assert_eq!(
         v!("2006-07-05 10:00:00"),
-        filters!(Date, v!("2006-07-05 10:00:00"), Nil)
+        call_filter!(liquid::filters::std::Date, "2006-07-05 10:00:00", Nil).unwrap()
     );
 
     assert_eq!(
         v!("07/05/2006"),
-        filters!(Date, v!("2006-07-05 10:00:00"), v!("%m/%d/%Y"))
+        call_filter!(
+            liquid::filters::std::Date,
+            "2006-07-05 10:00:00",
+            "%m/%d/%Y"
+        )
+        .unwrap()
     );
 
     assert_eq!(
         v!("07/16/2004"),
-        filters!(Date, v!("Fri Jul 16 01:00:00 2004"), v!("%m/%d/%Y"))
+        call_filter!(
+            liquid::filters::std::Date,
+            "Fri Jul 16 01:00:00 2004",
+            "%m/%d/%Y"
+        )
+        .unwrap()
     );
     assert_eq!(
         v!("#{Date.today.year}"),
-        filters!(Date, v!("now"), v!("%Y"))
+        call_filter!(liquid::filters::std::Date, "now", "%Y").unwrap()
     );
     assert_eq!(
         v!("#{Date.today.year}"),
-        filters!(Date, v!("today"), v!("%Y"))
+        call_filter!(liquid::filters::std::Date, "today", "%Y").unwrap()
     );
     assert_eq!(
         v!("#{Date.today.year}"),
-        filters!(Date, v!("Today"), v!("%Y"))
+        call_filter!(liquid::filters::std::Date, "Today", "%Y").unwrap()
     );
 
-    assert_eq!(Nil, filters!(Date, Nil, v!("%B")));
+    assert_eq!(
+        Nil,
+        call_filter!(liquid::filters::std::Date, Nil, "%B").unwrap()
+    );
 
-    assert_eq!(v!(""), filters!(Date, v!(""), v!("%B")));
+    assert_eq!(
+        v!(""),
+        call_filter!(liquid::filters::std::Date, "", "%B").unwrap()
+    );
 
     // Limited in value because we can't change the timezone
     assert_eq!(
         v!("07/05/2006"),
-        filters!(Date, v!(1152098955), v!("%m/%d/%Y"))
+        call_filter!(liquid::filters::std::Date, 1152098955, "%m/%d/%Y").unwrap()
     );
     assert_eq!(
         v!("07/05/2006"),
-        filters!(Date, v!("1152098955"), v!("%m/%d/%Y"))
+        call_filter!(liquid::filters::std::Date, "1152098955", "%m/%d/%Y").unwrap()
     );
 }
 
 #[test]
 fn test_first_last() {
-    assert_eq!(v!(1), filters!(First, v!([1, 2, 3])));
-    assert_eq!(v!(3), filters!(Last, v!([1, 2, 3])));
-    assert_eq!(Nil, filters!(First, v!([])));
-    assert_eq!(Nil, filters!(Last, v!([])));
+    assert_eq!(
+        v!(1),
+        call_filter!(liquid::filters::std::First, v!([1, 2, 3])).unwrap()
+    );
+    assert_eq!(
+        v!(3),
+        call_filter!(liquid::filters::std::Last, v!([1, 2, 3])).unwrap()
+    );
+    assert_eq!(
+        Nil,
+        call_filter!(liquid::filters::std::First, v!([])).unwrap()
+    );
+    assert_eq!(
+        Nil,
+        call_filter!(liquid::filters::std::Last, v!([])).unwrap()
+    );
 }
 
 #[test]
 fn test_replace() {
     assert_eq!(
         v!("2 2 2 2"),
-        filters!(Replace, v!("1 1 1 1"), v!("1"), v!(2))
+        call_filter!(liquid::filters::std::Replace, v!("1 1 1 1"), v!("1"), v!(2)).unwrap()
     );
     assert_eq!(
         v!("2 2 2 2"),
-        filters!(Replace, v!("1 1 1 1"), v!(1), v!(2))
+        call_filter!(liquid::filters::std::Replace, v!("1 1 1 1"), v!(1), v!(2)).unwrap()
     );
     assert_eq!(
         v!("2 1 1 1"),
-        filters!(ReplaceFirst, v!("1 1 1 1"), v!("1"), v!(2))
+        call_filter!(
+            liquid::filters::std::ReplaceFirst,
+            v!("1 1 1 1"),
+            v!("1"),
+            v!(2)
+        )
+        .unwrap()
     );
     assert_eq!(
         v!("2 1 1 1"),
-        filters!(ReplaceFirst, v!("1 1 1 1"), v!(1), v!(2))
+        call_filter!(
+            liquid::filters::std::ReplaceFirst,
+            v!("1 1 1 1"),
+            v!(1),
+            v!(2)
+        )
+        .unwrap()
     );
     assert_template_result!(
         "2 1 1 1",
@@ -641,10 +963,22 @@ fn test_replace() {
 
 #[test]
 fn test_remove() {
-    assert_eq!(v!("   "), filters!(Remove, v!("a a a a"), v!("a")));
-    assert_eq!(v!("   "), filters!(Remove, v!("1 1 1 1"), v!(1)));
-    assert_eq!(v!("a a a"), filters!(RemoveFirst, v!("a a a a"), v!("a ")));
-    assert_eq!(v!(" 1 1 1"), filters!(RemoveFirst, v!("1 1 1 1"), v!(1)));
+    assert_eq!(
+        v!("   "),
+        call_filter!(liquid::filters::std::Remove, v!("a a a a"), v!("a")).unwrap()
+    );
+    assert_eq!(
+        v!("   "),
+        call_filter!(liquid::filters::std::Remove, v!("1 1 1 1"), v!(1)).unwrap()
+    );
+    assert_eq!(
+        v!("a a a"),
+        call_filter!(liquid::filters::std::RemoveFirst, v!("a a a a"), v!("a ")).unwrap()
+    );
+    assert_eq!(
+        v!(" 1 1 1"),
+        call_filter!(liquid::filters::std::RemoveFirst, v!("1 1 1 1"), v!(1)).unwrap()
+    );
     assert_template_result!("a a a", r#"{{ "a a a a" | remove_first: "a " }}"#);
 }
 
@@ -841,11 +1175,20 @@ fn test_append() {
 
 #[test]
 fn test_concat() {
-    assert_eq!(v!([1, 2, 3, 4]), filters!(Concat, v!([1, 2]), v!([3, 4])));
-    assert_eq!(v!([1, 2, "a"]), filters!(Concat, v!([1, 2]), v!(["a"])));
-    assert_eq!(v!([1, 2, 10]), filters!(Concat, v!([1, 2]), v!([10])));
+    assert_eq!(
+        v!([1, 2, 3, 4]),
+        call_filter!(liquid::filters::std::Concat, v!([1, 2]), v!([3, 4])).unwrap()
+    );
+    assert_eq!(
+        v!([1, 2, "a"]),
+        call_filter!(liquid::filters::std::Concat, v!([1, 2]), v!(["a"])).unwrap()
+    );
+    assert_eq!(
+        v!([1, 2, 10]),
+        call_filter!(liquid::filters::std::Concat, v!([1, 2]), v!([10])).unwrap()
+    );
 
-    filters_fail!(Concat, v!([1, 2]), v!(10));
+    liquid_core::call_filter!(liquid::filters::std::Concat, v!([1, 2]), 10).unwrap_err();
 }
 
 #[test]
@@ -857,12 +1200,30 @@ fn test_prepend() {
 
 #[test]
 fn test_default() {
-    assert_eq!(v!("foo"), filters!(Default, v!("foo"), v!("bar")));
-    assert_eq!(v!("bar"), filters!(Default, Nil, v!("bar")));
-    assert_eq!(v!("bar"), filters!(Default, v!(""), v!("bar")));
-    assert_eq!(v!("bar"), filters!(Default, v!(false), v!("bar")));
-    assert_eq!(v!("bar"), filters!(Default, v!([]), v!("bar")));
-    assert_eq!(v!("bar"), filters!(Default, v!({}), v!("bar")));
+    assert_eq!(
+        v!("foo"),
+        call_filter!(liquid::filters::std::Default, v!("foo"), v!("bar")).unwrap()
+    );
+    assert_eq!(
+        v!("bar"),
+        call_filter!(liquid::filters::std::Default, Nil, v!("bar")).unwrap()
+    );
+    assert_eq!(
+        v!("bar"),
+        call_filter!(liquid::filters::std::Default, v!(""), v!("bar")).unwrap()
+    );
+    assert_eq!(
+        v!("bar"),
+        call_filter!(liquid::filters::std::Default, v!(false), v!("bar")).unwrap()
+    );
+    assert_eq!(
+        v!("bar"),
+        call_filter!(liquid::filters::std::Default, v!([]), v!("bar")).unwrap()
+    );
+    assert_eq!(
+        v!("bar"),
+        call_filter!(liquid::filters::std::Default, v!({}), v!("bar")).unwrap()
+    );
 }
 
 #[test]
@@ -891,8 +1252,20 @@ fn test_where() {
       { "handle": "delta", "ok": true }
     ]);
 
-    assert_eq!(expectation, filters!(Where, input, v!("ok"), v!(true)));
-    assert_eq!(expectation, filters!(Where, input, v!("ok")));
+    assert_eq!(
+        expectation,
+        call_filter!(
+            liquid::filters::std::Where,
+            input.clone(),
+            v!("ok"),
+            v!(true)
+        )
+        .unwrap()
+    );
+    assert_eq!(
+        expectation,
+        call_filter!(liquid::filters::std::Where, input.clone(), v!("ok")).unwrap()
+    );
 }
 
 #[test]
@@ -909,26 +1282,50 @@ fn test_where_no_key_set() {
       { "handle": "delta", "ok": true }
     ]);
 
-    assert_eq!(expectation, filters!(Where, input, v!("ok"), v!(true)));
-    assert_eq!(expectation, filters!(Where, input, v!("ok")));
+    assert_eq!(
+        expectation,
+        call_filter!(
+            liquid::filters::std::Where,
+            input.clone(),
+            v!("ok"),
+            v!(true)
+        )
+        .unwrap()
+    );
+    assert_eq!(
+        expectation,
+        call_filter!(liquid::filters::std::Where, input.clone(), v!("ok")).unwrap()
+    );
 }
 
 #[test]
 fn test_where_non_array_map_input() {
     assert_eq!(
         v!([{ "a": "ok" }]),
-        filters!(Where, v!({ "a": "ok" }), v!("a"), v!("ok"))
+        call_filter!(
+            liquid::filters::std::Where,
+            v!({ "a": "ok" }),
+            v!("a"),
+            v!("ok")
+        )
+        .unwrap()
     );
     assert_eq!(
         v!([]),
-        filters!(Where, v!({ "a": "not ok" }), v!("a"), v!("ok"))
+        call_filter!(
+            liquid::filters::std::Where,
+            v!({ "a": "not ok" }),
+            v!("a"),
+            v!("ok")
+        )
+        .unwrap()
     );
 }
 
 #[test]
 fn test_where_indexable_but_non_map_value() {
-    filters_fail!(Where, v!(1), v!("ok"), v!(true));
-    filters_fail!(Where, v!(1), v!("ok"));
+    liquid_core::call_filter!(liquid::filters::std::Where, 1, "ok", true).unwrap_err();
+    liquid_core::call_filter!(liquid::filters::std::Where, 1, "ok").unwrap_err();
 }
 
 #[test]
@@ -941,22 +1338,46 @@ fn test_where_non_boolean_value() {
 
     assert_eq!(
         v!([{ "message": "Bonjour!", "language": "French" }]),
-        filters!(Where, input, v!("language"), v!("French"))
+        call_filter!(
+            liquid::filters::std::Where,
+            input.clone(),
+            "language",
+            "French"
+        )
+        .unwrap()
     );
     assert_eq!(
         v!([{ "message": "Hallo!", "language": "German" }]),
-        filters!(Where, input, v!("language"), v!("German"))
+        call_filter!(
+            liquid::filters::std::Where,
+            input.clone(),
+            v!("language"),
+            v!("German")
+        )
+        .unwrap()
     );
     assert_eq!(
         v!([{ "message": "Hello!", "language": "English" }]),
-        filters!(Where, input, v!("language"), v!("English"))
+        call_filter!(
+            liquid::filters::std::Where,
+            input.clone(),
+            "language",
+            "English"
+        )
+        .unwrap()
     );
 }
 
 #[test]
 fn test_where_array_of_only_unindexable_values() {
-    assert_eq!(Nil, filters!(Where, v!([Nil]), v!("ok"), v!(true)));
-    assert_eq!(Nil, filters!(Where, v!([Nil]), v!("ok")));
+    assert_eq!(
+        Nil,
+        call_filter!(liquid::filters::std::Where, v!([Nil]), "ok", true).unwrap()
+    );
+    assert_eq!(
+        Nil,
+        call_filter!(liquid::filters::std::Where, v!([Nil]), "ok").unwrap()
+    );
 }
 
 #[test]
@@ -970,6 +1391,6 @@ fn test_where_no_target_value() {
 
     assert_eq!(
         v!([{ "foo": true }, { "foo": "for sure" }]),
-        filters!(Where, input, v!("foo"))
+        call_filter!(liquid::filters::std::Where, input, v!("foo")).unwrap()
     );
 }
diff --git a/tests/derive_macros_test_filters/keyword.rs b/tests/derive_macros_test_filters/keyword.rs
index 90032b9..d13ee39 100644
--- a/tests/derive_macros_test_filters/keyword.rs
+++ b/tests/derive_macros_test_filters/keyword.rs
@@ -1,10 +1,10 @@
 use liquid_core::Context;
 use liquid_core::Expression;
 use liquid_core::Result;
-use liquid_core::Value;
 use liquid_core::{
     Display_filter, Filter, FilterParameters, FilterReflection, FromFilterParameters, ParseFilter,
 };
+use liquid_core::{Value, ValueView};
 
 #[derive(Debug, FilterParameters)]
 struct TestKeywordFilterParameters {
@@ -40,7 +40,7 @@ pub struct TestKeywordFilter {
 }
 
 impl Filter for TestKeywordFilter {
-    fn evaluate(&self, _input: &Value, context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, _input: &dyn ValueView, context: &Context<'_>) -> Result<Value> {
         let args = self.args.evaluate(context)?;
 
         let required = args.required;
diff --git a/tests/derive_macros_test_filters/mixed.rs b/tests/derive_macros_test_filters/mixed.rs
index ce72ad4..60c618f 100644
--- a/tests/derive_macros_test_filters/mixed.rs
+++ b/tests/derive_macros_test_filters/mixed.rs
@@ -1,10 +1,10 @@
 use liquid_core::Context;
 use liquid_core::Expression;
 use liquid_core::Result;
-use liquid_core::Value;
 use liquid_core::{
     Display_filter, Filter, FilterParameters, FilterReflection, FromFilterParameters, ParseFilter,
 };
+use liquid_core::{Value, ValueView};
 
 // Colision with FilterParameters' evaluated struct.
 #[allow(dead_code)]
@@ -52,7 +52,7 @@ pub struct TestMixedFilter {
 }
 
 impl Filter for TestMixedFilter {
-    fn evaluate(&self, _input: &Value, context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, _input: &dyn ValueView, context: &Context<'_>) -> Result<Value> {
         let args = self.args.evaluate(context)?;
 
         let a = args.a.map(|i| i.to_string()).unwrap_or("None".to_string());
diff --git a/tests/derive_macros_test_filters/parameterless.rs b/tests/derive_macros_test_filters/parameterless.rs
index a62ef75..b90217b 100644
--- a/tests/derive_macros_test_filters/parameterless.rs
+++ b/tests/derive_macros_test_filters/parameterless.rs
@@ -1,7 +1,7 @@
 use liquid_core::Context;
 use liquid_core::Result;
-use liquid_core::Value;
 use liquid_core::{Display_filter, Filter, FilterReflection, ParseFilter};
+use liquid_core::{Value, ValueView};
 
 #[derive(Clone, ParseFilter, FilterReflection)]
 #[filter(
@@ -16,7 +16,7 @@ pub struct TestParameterlessFilterParser;
 pub struct TestParameterlessFilter;
 
 impl Filter for TestParameterlessFilter {
-    fn evaluate(&self, _input: &Value, _context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, _input: &dyn ValueView, _context: &Context<'_>) -> Result<Value> {
         let result = "<>";
 
         Ok(Value::scalar(result))
diff --git a/tests/derive_macros_test_filters/positional.rs b/tests/derive_macros_test_filters/positional.rs
index c9f9380..4a5301a 100644
--- a/tests/derive_macros_test_filters/positional.rs
+++ b/tests/derive_macros_test_filters/positional.rs
@@ -1,10 +1,10 @@
 use liquid_core::Context;
 use liquid_core::Expression;
 use liquid_core::Result;
-use liquid_core::Value;
 use liquid_core::{
     Display_filter, Filter, FilterParameters, FilterReflection, FromFilterParameters, ParseFilter,
 };
+use liquid_core::{Value, ValueView};
 
 #[derive(Debug, FilterParameters)]
 struct TestPositionalFilterParameters {
@@ -35,7 +35,7 @@ pub struct TestPositionalFilter {
 }
 
 impl Filter for TestPositionalFilter {
-    fn evaluate(&self, _input: &Value, context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, _input: &dyn ValueView, context: &Context<'_>) -> Result<Value> {
         let args = self.args.evaluate(context)?;
 
         let pos1 = args.pos1.to_kstr();
diff --git a/tests/derive_macros_test_filters/stateful.rs b/tests/derive_macros_test_filters/stateful.rs
index 31b5b6f..bcbe8b6 100644
--- a/tests/derive_macros_test_filters/stateful.rs
+++ b/tests/derive_macros_test_filters/stateful.rs
@@ -2,8 +2,8 @@ use liquid_core::compiler::FilterArguments;
 use liquid_core::Context;
 use liquid_core::Expression;
 use liquid_core::Result;
-use liquid_core::Value;
 use liquid_core::{Display_filter, Filter, FilterParameters, FilterReflection, ParseFilter};
+use liquid_core::{Value, ValueView};
 
 #[derive(Clone, Copy, Debug)]
 enum Mood {
@@ -66,7 +66,7 @@ pub struct TestStatefulFilter {
 }
 
 impl Filter for TestStatefulFilter {
-    fn evaluate(&self, _input: &Value, context: &Context<'_>) -> Result<Value> {
+    fn evaluate(&self, _input: &dyn ValueView, context: &Context<'_>) -> Result<Value> {
         let args = self.args.evaluate(context)?;
 
         let result = match self.state {
diff --git a/tests/test_helper.rs b/tests/test_helper.rs
index cfcb1d8..daf3dc0 100644
--- a/tests/test_helper.rs
+++ b/tests/test_helper.rs
@@ -136,79 +136,3 @@ macro_rules! assert_render_error {
         template.render(&$assigns).unwrap_err();
     };
 }
-
-#[allow(unused_macros)]
-#[macro_export]
-macro_rules! filters {
-    ($a:ident, $b:expr) => {{
-        filters!($a, $b, )
-    }};
-    ($a:ident, $b:expr, $($c:expr),*) => {{
-        let positional = Box::new(vec![$(::liquid_core::Expression::Literal($c)),*].into_iter());
-        let keyword = Box::new(Vec::new().into_iter());
-        let args = ::liquid_core::compiler::FilterArguments { positional, keyword };
-
-        let context = ::liquid_core::Context::default();
-
-        let filter = ::liquid_core::ParseFilter::parse(&::liquid::filters::std::$a, args).unwrap();
-        ::liquid_core::Filter::evaluate(&*filter, &$b, &context).unwrap()
-    }};
-}
-
-#[allow(unused_macros)]
-#[macro_export]
-macro_rules! filters_fail {
-    ($a:ident, $b:expr) => {{
-        filters_fail!($a, $b, )
-    }};
-    ($a:ident, $b:expr, $($c:expr),*) => {{
-        let positional = Box::new(vec![$(::liquid_core::Expression::Literal($c)),*].into_iter());
-        let keyword = Box::new(Vec::new().into_iter());
-        let args = ::liquid_core::compiler::FilterArguments { positional, keyword };
-
-        let context = ::liquid_core::Context::default();
-
-        ::liquid_core::ParseFilter::parse(&::liquid::filters::std::$a, args)
-            .and_then(|filter| ::liquid_core::Filter::evaluate(&*filter, &$b, &context))
-            .unwrap_err()
-    }};
-}
-
-#[cfg(feature = "jekyll-filters")]
-#[allow(unused_macros)]
-#[macro_export]
-macro_rules! jekyll_filters {
-    ($a:ident, $b:expr) => {{
-        jekyll_filters!($a, $b, )
-    }};
-    ($a:ident, $b:expr, $($c:expr),*) => {{
-        let positional = Box::new(vec![$(::liquid_core::Expression::Literal($c)),*].into_iter());
-        let keyword = Box::new(Vec::new().into_iter());
-        let args = ::liquid_core::compiler::FilterArguments { positional, keyword };
-
-        let context = ::liquid_core::Context::default();
-
-        let filter = ::liquid_core::ParseFilter::parse(&::liquid::filters::jekyll::$a, args).unwrap();
-        ::liquid_core::Filter::evaluate(&*filter, &$b, &context).unwrap()
-    }};
-}
-
-#[cfg(feature = "jekyll-filters")]
-#[allow(unused_macros)]
-#[macro_export]
-macro_rules! jekyll_filters_fail {
-    ($a:ident, $b:expr) => {{
-        jekyll_filters_fail!($a, $b, )
-    }};
-    ($a:ident, $b:expr, $($c:expr),*) => {{
-        let positional = Box::new(vec![$(::liquid_core::Expression::Literal($c)),*].into_iter());
-        let keyword = Box::new(Vec::new().into_iter());
-        let args = ::liquid_core::compiler::FilterArguments { positional, keyword };
-
-        let context = ::liquid_core::Context::default();
-
-        ::liquid_core::ParseFilter::parse(&::liquid::filters::jekyll::$a, args)
-            .and_then(|filter| ::liquid_core::Filter::evaluate(&*filter, &$b, &context))
-            .unwrap_err()
-    }};
-}