So I was writing (attempting) some variadic macros to try to implement compose and curry in rust. pipe
was easy enough.
Variadic partial application though? Not so much. Props to anyone who can come up with a solution for this partial application macro that doesn't use those pesky seemingly ignorable type args (and maybe variadic? w/o reflection I think it's impossible):
//ltr compose for closures
macro_rules! pipe {
($init:tt |> $($fn:ident),+) => {{
let mut r = $init;
$( r = $fn(r); )*
r
}}
}
//partially apply a closure w/ up to 4 args
macro_rules! apply {
($fn:ident<$t1:ty,$t2:ty>($arg1:expr)) => {
|x:$t2| $fn($arg1 as $t1, x)
};
($fn:ident<$t1:ty,$t2:ty,$t3:ty>($arg1:expr)) => {
|x:$t2, y:$t3| $fn($arg1 as $t1,x, y)
};
($fn:ident<$t1:ty,$t2:ty,$t3:ty>($arg1:expr, $arg2:expr)) => {
|x:$t3| $fn($arg1 as $t1,$arg2 as $t2,x)
};
($fn:ident<$t1:ty,$t2:ty,$t3:ty,$t4:ty>($arg1:expr)) => {
|x: $t2, y:$t3, z:$t4| $fn($arg1 as $t1, x, y, z)
};
($fn:ident<$t1:ty,$t2:ty,$t3:ty,$t4:ty>($arg1:expr, $arg2:expr)) => {
|x:$t3, y:$t4| $fn($arg1 as $t1,$arg2 as $t2, x, y)
};
($fn:ident<$t1:ty,$t2:ty,$t3:ty,$t4:ty>($arg1:expr, $arg2:expr, $arg3:expr)) => {
|x:$t4| $fn($arg1 as $t1,$arg2 as $t2,arg3 as $t3,x)
};
}
fn main() {
let add = |x:i32, y:i32| x + y;
let sq = |x:i32| x * x;
let dbl = |x:i32| x * 2;
//blargh i hate those typeargs! i need them for the matcher
//to get the original params number and type since rust has no reflection :(
//weirdly if i ignore them with _ it works. But it will luckily fail
//hard if you supply incorrect args.
let plus1 = apply!(add<_,_>(1));
let r = pipe!(3 |> dbl, plus1, sq);
print!("{}", r);
}
Edit: The reason I seemingly cannot do variadic args on this is because that changes the structure of the closure. Seemed fine at first glance, I'd just wrap it in a match statement based on the total params and difference between the number of exprs vs total params. but if you try to return closures of varying signatures in a match arm it won't compile. The good news is this approach is probably low-overhead. But its really not a great improvement over partial application thru inline closures as necessary:
//simple, works, no type args or even type declarations on params I'm just a crazy person who wants it all!
let add = |x, y| x + y;
let add1 = |x| add(x, 1);
So its really just an academic pursuit at this point, it hardly seems practical