At the moment, we have the following situation:
fn() -> R : Fn<(), Output=R>
fn(A) -> R : Fn<(A,), Output=R>
fn(A,B) -> R : Fn<(A,B), Output=R>
fn(A,B,C) -> R : Fn<(A,B,C), Output=R>
fn(A,B,C,D) -> R : Fn<(A,B,C,D), Output=R>
The main problem with this approach is that there is no way to write, for instance:
struct ReturnFirstArg;
impl<T> FnOnce<T> for ReturnFirstArg where ??? {
type Output = ???;
extern "rust-call" fn call_once(self, args: T) -> Self::Output {
???
}
}
Encoding tuples in the following way can help solve this problem:
() -> ()
(A,) -> (A, ())
(A, B) -> (A, (B, ()))
(A, B, C) -> (A, (B, (C, ())))
(A, B, C, D) -> (A, (B, (C, (D, ()))))
Then we have the following for function types
fn() -> R : Fn<(), Output=R>
fn(A) -> R : Fn<(A, ()), Output=R>
fn(A,B) -> R : Fn<(A, (B, ())), Output=R>
fn(A,B,C) -> R : Fn<(A, (B, (C,()))), Output=R>
fn(A,B,C,D) -> R : Fn<(A, (B, (C, (D, ())))), Output=R>
And existing syntax can write the ReturnFirstArg function above as follows:
struct ReturnFirstArg;
impl<T, Args> FnOnce<(T, Args)> for ReturnFirstArg {
type Output = T;
extern "rust-call" fn call_once(self, (ret, _): (T, Args)) -> Self::Output {
ret
}
}
I believe this technique is quite general; for instance:
struct Sum;
impl<T> FnOnce<(T, ())> for Sum {
type Output = T;
extern "rust-call" fn call_once(self, (t, ()): (T, ())) -> T { t }
}
impl<T, Args> FnOnce<(T, Args)> for Sum
where Sum: FnOnce<Args, Output=T>,
T: Add<Output=T>
{
type Output = T;
extern "rust-call" fn call_once(self, (t, args): (T, Args)) -> Self::Output {
t + <Sum as FnOnce<Args>>::call_once(Sum, args)
}
}
This could be applied to regular tuples as well, though that would require syntax changes - simply changing the signature of the Fn traits would not.