-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft RFC: variadic generics #376
Comments
There is one issue with my initial approach: a reference to a "subtuple" of a larger tuple could have different padding than the tuple type made out of those fields. One solution I may have is a & |
This is probably a prerequisite for taking the Cartesian product of iterators without a macro, like in rust-itertools/iproduct! and rust-itertools/itertools#10. |
This should be expanded to also support using the ".." operator on fixed size arrays (behaves equivalent to a tuple where the elements are all the same type and the arity matches the length of the array). In combination with #174 this would make it much easier to deal with variadic arguments of the same type. Something which even C++ is still missing. |
This would need to be compatible with range syntax. |
How would one implement the cartesian product of two type tuples with this? |
@gnzlbg what exactly do you have in mind, |
@eddyb yes, but with variadics. |
Having wrestled with c++11 templates and Rust macros, I really like this RFC. I hope it comes to fruition sooner rather than later. |
Me too, I really wished this to come before 1.0 and solved tuple impl etc. |
Unfortunately this syntax doesn't work, because the For the general idea: 👍, I was imagining variadic generics using pretty much the same approach. However, the devil is in the details that aren't yet worked out. C++ resolves expressions involving parameter pack expansion only after monomorphization; but I assume Rust would want to type-check the generic code? Wouldn't that require restrictions on where the unpacking operator is used? Also, consuming variadics in C++ usually involves a recursive function using template specialization. With this proposal, it seems like you'd need to create a trait for every variadic function, so that you can "specialize" using trait impls as in the Also, the template <typename... T> void consumer(T... t) { ... }
template <typename... T> void adapter(T... t)
{
consumer(t.adapt()...);
// expands to:
// consumer(t1.adapt(), t2.adapt(), t3.adapt(), ...);
} What's the easiest way to write the 3-line As for |
Why not the dereference operator? type AppendTuple<T, U> = (*T, U);
f(*(a, b), *(c, d, e), f) => f(a, b, c, d, e, f); |
In C++1y there are fold-expressions that allow you to do a lot of things without recursion: template<typename... Args>
bool all(Args... args) { return (... && args); }
bool b = all(true, true, true, false);
You can easily type check that all parameters in the parameter pack implement a given trait. If one needs more complicated type-checking a variadic function is likely not the best solution. |
@gnzlbg that's C++1z actually. |
The syntax between the value-level and type-level languages should be kept as close as possible. Already the @gnzlbg I don't think that form of duck-typing is compatible with Rust's trait-based generics. Rather than having special fold syntax, it's cleaner to design some system compatible with HKTs for constraining the tuple's types, and then iterate over a tuple for fn fold_variadic<*T> (x: T) -> u32
where *T: BitAnd<u32>
{
x.iter().fold(!0u32, |b, a| b & a)
}
fold_variadic(0xFFF0, 0x0FFF) == 0x0FF0 In the above example, T is a tuple of variadic length, and <*T> = <u32, u32, u32>
*T = u32, u32, u32 The curried/destructured tuple is matched to The interaction of a curried tuple with where *T: BitAnd<u32>
// or
where *T = u32 A sensible semantic is distribution over the curried form. |
@oblitum you are correct. A recurring task in C++ meta-programming is zipping a tuple of values with a tuple of types (tags, or |
Any update on this? |
@Vikaton +1 I would be really interested to know as well. This is the only feature that I miss from C++. |
How will this interact with type level numerals (in particular, polymorphism over arrays of size N)? |
There's been lots more discussion on this over on #1582 but I'll try to summarise here. The main problem with all of these RFCs is the problem of memory layout for tuples. In general, in current Rust, a tuple of the form
By comparison, a tuple
Note the different position of There are two proposed solutions to this:
The second option is to accept RFC #1397 and reverse the layout order of tuples so that
And its tail
This comes at the cost of disallowing the compiler from generating code which overwrites padding at the end of tuples as the "padding" may actually contain the next element of a larger tuple. |
IIUC what you want is to be able to slice
I think it would be great to be able to slice [*]: https://rmf.io/cxx11/optimal-tuple-i/, https://rmf.io/cxx11/optimal-tuple-ii/, https://rmf.io/cxx11/optimal-tuple-iii/ , https://rmf.io/cxx11/optimal-tuple-iv/ |
Meaning if the trade-off is between sugar for a particular use case (slicing |
FWIW the last option mentioned by @canndrew, involving #1397, seems like clearly the cleanest and nicest design to me. The only big question mark hanging over it in my mind is the backwards (in)compatibility impact of #1397 on already-existing |
I want to crosslink you to the fresh RFC clarification issue /pull/1592 because the commitment to having a specific position in the tuple possible to be unsized is relevant to the tuple representation discussion here. |
@tinyplasticgreyknight : even so, every function would have a different tuple type and that would not work either for me right? Each method for each parser rule's pattern has a different signature. I think that was not clear enough. What I implemented in C++ is similar to the I don't really see how I can build the correct tuple, its signature being something like |
Will it implement in 2018? |
@NateLing up for writing a formal RFC proposal about it? ;) |
@Centril yeap |
For reference, since this seems to be the most prominent issue when looking for "variadic generics":
Since there is no dedicated tag for it, is there any place where the general progress of this topic can be followed? |
Why not just use induction on heterogeneous lists? |
I just want to say here that now TypeScript 4.0 implemented the same thing in the same shape (more or less). See: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-0.html#variadic-tuple-types |
This is a draft RFC, so I can pretty confidently say no |
If you want optional arguments soon then implement them on nightly using procmacros
and invoke like It'd even support different return types for different optional argument types. |
There's been several attempts over the years and it doesn't seem like it's going to happen again any time soon, sorry. I have a branch somewhere with some of the implementation details (that can be used internally by |
Is there anyone else? |
I don't think this will be implemented in 2018. It would still be pretty useful. |
I'd expect some builder patters plus #3681 would be preferable for many if not most variadics use cases, ala I suppose
defines
|
Issue by eddyb
Monday Oct 28, 2013 at 17:39 GMT
For earlier discussion, see rust-lang/rust#10124
This issue was labelled with: B-RFC in the Rust repository
The Problem
bind
method,f.bind(a, b)(c) == f(a, b, c)
) and defining such functions may only be done (in a limited fashion) with macrosThe Solution: Part One
C++11 sets a decent precedent, with its variadic templates, which can be used to define type-safe variadic functions, among other things.
I propose a similar syntax, a trailing
..T
in generic formal type parameters:The simple example above only uses
..T
, but notT
itself.The question which arises is this: what is
T
? C++11 has a special case for variadic parameter packs, but we can do better.We have tuples. We can use them to store the actual variadic generic type parameters:
The Solution: Part Two
Now that we know the answer is "tuples", everything else is about extending them.
The prefix
..
operator would expand a tuple type:Then we can do the same thing with values:
There's only one piece missing: we're still not able to define a function which takes a variable number of arguments.
For this, I propose
..x: T
(where T is a tuple type) in a pattern, which can be used to "capture" multiple arguments when used in fn formal arguments:A type bound for
..T
(i.e.impl<..T: Trait>
) could mean that the tupleT
has to satisfy the bound (or each type inT
, but that's generally less useful).Examples:
Todo
The text was updated successfully, but these errors were encountered: