Skip to content

Instantly share code, notes, and snippets.

@jswrenn
Created June 3, 2017 17:01
Show Gist options
  • Save jswrenn/68820991567ff0abfae0104f94ba1b43 to your computer and use it in GitHub Desktop.
Save jswrenn/68820991567ff0abfae0104f94ba1b43 to your computer and use it in GitHub Desktop.
/// Inspired by https://github.com/nastevens/predicates-rs/
#![feature(conservative_impl_trait)]
use std::ops::*;
#[inline(always)]
fn partial<F, A, B, R>(f : F, b : B)
-> impl Fn(A) -> R
where F: Fn(&A, &B) -> R
{ move |a| f(&a, &b) }
/// In a function-oriented approach, we declare a series of functions (`lt`,
/// `gt`, etc.) that partially apply their respective comparison functions.
/// Since the definitions for `PartialEq` and `PartialOrd` comparison functions
/// follow an identical template, these function definitions can be templated
/// and with a macro:
macro_rules! impl_cmp {
($trait:ident, $($name:ident),+) => (
$(pub fn $name<L, R>(r : R)
-> impl Fn(L) -> bool
where R: $trait<L>,
L: $trait<R>
{ partial($trait::$name, r) })*
)
}
impl_cmp!(PartialEq , eq, ne);
impl_cmp!(PartialOrd, lt, le, gt, ge);
pub fn and<I, L, R>(l : L, r : R)
-> impl Fn(I) -> bool
where I: Copy,
L: Fn(I) -> bool,
R: Fn(I) -> bool
{ move | i | l(i) && r(i) }
pub fn or<I, L, R>(l : L, r : R)
-> impl Fn(I) -> bool
where I: Copy,
L: Fn(I) -> bool,
R: Fn(I) -> bool
{ move | i | l(i) || r(i) }
////////////////////////////////////////////////////////////////////////////////
// However, this approach requires the nightly-only `impl Trait` feature.
// We can leverage the type inferencing available to closures in a macro-based
// approach, in which a series of macros (`lt!`, `gt!`, etc.) expand to
// closures that compare their argument against the argument of the macro:
#[macro_export] macro_rules! lt {($b:expr) => (|a| a < $b)}
#[macro_export] macro_rules! le {($b:expr) => (|a| a >= $b)}
#[macro_export] macro_rules! gt {($b:expr) => (|a| a < $b)}
#[macro_export] macro_rules! ge {($b:expr) => (|a| a >= $b)}
macro_rules! boolean_binop {
($v:ident $op:tt $e:expr) => {$e($v)};
($v:ident $op:tt $e:expr, $($es:expr),+) => {$e($v) $op boolean_binop!($v, $($es),*)}
}
/// In this approach, the logical `and` and `or` operations can be variadic.
#[macro_export] macro_rules! and {($($es:expr),+) => {|v| boolean_binop!(v && $($es),+)}}
#[macro_export] macro_rules! or {($($es:expr),+) => {|v| boolean_binop!(v || $($es),+)}}
////////////////////////////////////////////////////////////////////////////////
fn main() {
let foo = or![and![lt!(5), gt(3)], eq(6)];
println!("{}", foo(3));
println!("{}", foo(4));
println!("{}", foo(5));
println!("{}", foo(6));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment