Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign upDraft RFC: variadic generics #376
Comments
aturon
changed the title
[CLOSED] RFC: variadic generics
Draft RFC: variadic generics
Oct 8, 2014
This comment has been minimized.
This comment has been minimized.
|
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 comment has been minimized.
This comment has been minimized.
alexchandel
commented
Jan 23, 2015
|
This is probably a prerequisite for taking the Cartesian product of iterators without a macro, like in rust-itertools/iproduct! and bluss/rust-itertools#10. |
This comment has been minimized.
This comment has been minimized.
|
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 comment has been minimized.
This comment has been minimized.
alexchandel
commented
Feb 4, 2015
|
This would need to be compatible with range syntax. |
This comment has been minimized.
This comment has been minimized.
|
How would one implement the cartesian product of two type tuples with this? |
This comment has been minimized.
This comment has been minimized.
|
@gnzlbg what exactly do you have in mind, |
This comment has been minimized.
This comment has been minimized.
|
@eddyb yes, but with variadics. |
This comment has been minimized.
This comment has been minimized.
goertzenator
commented
Feb 27, 2015
|
Having wrestled with c++11 templates and Rust macros, I really like this RFC. I hope it comes to fruition sooner rather than later. |
This comment has been minimized.
This comment has been minimized.
oblitum
commented
Feb 27, 2015
|
Me too, I really wished this to come before 1.0 and solved tuple impl etc. |
This comment has been minimized.
This comment has been minimized.
|
Unfortunately this syntax doesn't work, because the For the general idea: 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 |
This comment has been minimized.
This comment has been minimized.
alexchandel
commented
Mar 3, 2015
|
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); |
This comment has been minimized.
This comment has been minimized.
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. |
This comment has been minimized.
This comment has been minimized.
oblitum
commented
Mar 4, 2015
|
@gnzlbg that's C++1z actually. |
This comment has been minimized.
This comment has been minimized.
alexchandel
commented
Mar 5, 2015
|
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) == 0x0FF0In the above example, T is a tuple of variadic length, and <*T> = <u32, u32, u32>
*T = u32, u32, u32The curried/destructured tuple is matched to The interaction of a curried tuple with where *T: BitAnd<u32>
// or
where *T = u32A sensible semantic is distribution over the curried form. |
This comment has been minimized.
This comment has been minimized.
|
@oblitum you are correct. A recurring task in C++ meta-programming is zipping a tuple of values with a tuple of types (tags, or |
This comment has been minimized.
This comment has been minimized.
Vikaton
commented
Dec 22, 2015
|
Any update on this? |
This comment has been minimized.
This comment has been minimized.
semirix
commented
Dec 24, 2015
|
@Vikaton +1 I would be really interested to know as well. This is the only feature that I miss from C++. |
This comment has been minimized.
This comment has been minimized.
|
How will this interact with type level numerals (in particular, polymorphism over arrays of size N)? |
This comment has been minimized.
This comment has been minimized.
|
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. |
This comment has been minimized.
This comment has been minimized.
|
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/ |
This comment has been minimized.
This comment has been minimized.
|
Meaning if the trade-off is between sugar for a particular use case (slicing |
This comment has been minimized.
This comment has been minimized.
|
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 |
This comment has been minimized.
This comment has been minimized.
bluss
commented
Apr 27, 2016
|
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. |
This comment has been minimized.
This comment has been minimized.
|
@canndrew The name The expansion syntax would presumably work for both tuple types and tuple values, although the layout argument is only relevent for tuple values. Also lifetimes are not an issue since lifetime arguments are always separated from type arguments, there's no need for a construct which supports both. It's possible to start with just our existing tuples, and then potentially later add a notion of "tuple lifetimes" or similar, which might look like |
This comment has been minimized.
This comment has been minimized.
|
@canndrew Actually, in my demo I've used a function which turns |
This comment has been minimized.
This comment has been minimized.
No, but we'd need it to express something equivalent to @eddyb |
This comment has been minimized.
This comment has been minimized.
|
@canndrew Ah, I agree then, we can use an intrinsic relying on a trait to produce the right return type in generic contexts, while the actual monomorphized implementation of the intrinsic would only care about concrete tuple types. |
This comment has been minimized.
This comment has been minimized.
|
Currently While I now see the advantage of Is there a way to get both optimizations? |
This comment has been minimized.
This comment has been minimized.
Well, the "conflation" was certainly intentional in my first comment, I do want to use tuple types for expressing type lists. |
This comment has been minimized.
This comment has been minimized.
vi
commented
May 5, 2016
•
|
Shouldn't |
This comment has been minimized.
This comment has been minimized.
|
@vi |
This comment has been minimized.
This comment has been minimized.
|
This would also make our syntax line up with C++'s, which is... I don't know, a nice symmetry at least. |
This comment has been minimized.
This comment has been minimized.
Having |
This comment has been minimized.
This comment has been minimized.
|
I decided to reopen #1582 largely because of this comment. I'll close it again if @eddyb or @nikomatsakis disagree with the decision. |
This comment has been minimized.
This comment has been minimized.
|
@canndrew It seems non-trivial to see at a glance what is being spread, and it's non-symmetric. fn foo<T: Tuple>(; args: Tuple) {...} |
This comment has been minimized.
This comment has been minimized.
leonardo-m
commented
Jun 22, 2017
•
|
Regarding this instead of looking at C++ design I suggest to look instead at the D language and its type lists (once named type tuples, |
This comment has been minimized.
This comment has been minimized.
|
What D does strongly requires either duck typing or value refinement / dependent types. |
This comment has been minimized.
This comment has been minimized.
leonardo-m
commented
Jun 22, 2017
|
Right, D type lists are dynamically typed at compile-time, just like D templates are half-dynamically typed... (half because in D you often use template constraints to perform half type checking) Unlike Rust. Thank you eddyb. |
This comment has been minimized.
This comment has been minimized.
Type level integers would be enough. This is a minimal form of dependent types[*], but you could then use One could probably think of multiple combinations of more or less complex rules to make [*] EDIT: maybe we could already do something like this without dependent types at all by making all integer literals be of their corresponding type in the EDIT 2: The main reason I don't like this "style" of dependent types is that it might generate an explosion in code size and compile times. Each literal integer value has a different type, each function taking one of those is a different function, etc. But I think something like this should be explored as an ergonomic improvement to type-level integers, and in a way that doesn't impact code-size or compile-times, after the minimal RFC goes through because I hate having to manually construct |
This comment has been minimized.
This comment has been minimized.
|
What you are describing is what I call "value refinement" e.g. |
This comment has been minimized.
This comment has been minimized.
Nothing prevents adding a new syntax for this as well (especially together with type/value lists), e.g. |
This comment has been minimized.
This comment has been minimized.
|
|
GabrielCastro
referenced this issue
Oct 13, 2017
Closed
Add some utility functions/macros for deserializing multiple objects. #2
This comment has been minimized.
This comment has been minimized.
qraynaud
commented
Dec 7, 2017
|
I was planning to transcribe some C++ code I have in Rust. It is some playground project of mine that is somehow like yacc. It currently can generate correct parsing tables and parsers for any LALR(1) grammar for C++ and Javascript. Somehow, I wanted to move the grammar analysis codebase to Rust and also the reference parser implementation. My concern is I'd like to be able to define my parsers like I did in C++ / JS: for each pattern in each rule in the grammar, you have to provide a method to construct the value it produces consuming the subrules / tokens values. I had an interface like this: template </* … */>
class parser: base_parser</* … */> {
void register_rules() {
this->register_code("rule_name", 0, *this, /* pattern index in the rule */, &parser::rule_name_0);
}
float rule_name_0(int i, float f) {
return i + f;
}
}The form with 4 args is providing a method, the 3rd being the object on which the method should be invoked on while with 3 args, you could pass on any function instead. What is important to me is that I have in the internal parser code a class that can invoke any of those methods using a provided array of values of the variant type I talked of before. Since everything relies on proper templating, the type checking is ensured at compilation for the most part. The variant still has to check it has the correct type value in it at execution time but it will fail properly if not. I find this important because it makes the rules codebase easy to understand. You put an argument for every token / subrule your pattern contains (eg Since the basic parser code is not generated, I have the feeling that it is impossible to achieve anything even remotely like this in Rust as of today. It would be great if this spec could take into account those kind of usages to make them possible. |
This comment has been minimized.
This comment has been minimized.
tinyplasticgreyknight
commented
Dec 7, 2017
•
|
@qraynaud I'm not sure if I understand what you're looking for exactly, but would it be a useful workaround if your function always accepts only a single argument, which might be a tuple containing the "real" arguments? Maybe I should read your original repository and get a feel for how this is all used :-) |
This comment has been minimized.
This comment has been minimized.
qraynaud
commented
Dec 7, 2017
•
|
@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 |
Centril
added
the
T-lang
label
Dec 10, 2017
This comment has been minimized.
This comment has been minimized.
NateLing
commented
Feb 11, 2018
|
Will it implement in 2018? |
This comment has been minimized.
This comment has been minimized.
|
@NateLing up for writing a formal RFC proposal about it? ;) |
rust-highfive commentedOct 8, 2014
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
bindmethod,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
..Tin generic formal type parameters:The simple example above only uses
..T, but notTitself.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 tupleThas to satisfy the bound (or each type inT, but that's generally less useful).Examples:
Todo