0

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

max taldykin
  • 12,459
  • 5
  • 45
  • 64
Ross
  • 19
  • 4
  • 1
    Hi there! Maybe I'm missing something, but, uhm, what is the question exactly? Is it just that you are asking if/how you can rid of those "type args"? – Lukas Kalbertodt Jun 21 '20 at 08:04
  • 1
    I suppose I should've been a bit more clear. I want to primarily get rid of those type args yes, just so my implementation doesn't need them. But I was also wondering if there was a way to express this variadically. Rarely do i want to curry a function with more than 4 args but maybe I do and then to use my current implementation I'll need to add (args-1) worth of patterns per number of original arguments. – Ross Jun 21 '20 at 15:59

1 Answers1

1

I am not sure whether or not I understand your question, but I think maybe this library may help you: https://docs.rs/partial_application/0.2.0/partial_application/

#[macro_use]
extern crate partial_application;

//ltr compose for closures
macro_rules! pipe {
    ($init:tt |> $($fn:ident),+) => {{
        let mut r = $init;
        $( r = $fn(r); )*
        r
    }}
}

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 = partial!(add => 1,_);
    let r = pipe!(3 |> dbl, plus1, sq);

    print!("{}", r);
}

You may refer to this library to create your own macro.

HarborYuan
  • 11
  • 1