Tuple-Based Variadic Generics

Rendered
cc @eddyb @sgrif

How would you actually write variadic functions under this proposal? Am I correct that you'd still have to write something like foo( (arg1, arg2, arg3) ), or would there be some kind of sugar?

It is not a major deal breaker or anything, but it's worth noting that my field reordering stuff does currently affect tuples, and we probably want it to continue doing so where possible, I'd think.
Now if only the world would leave me alone for the like 2 days I need to finish my field reordering stuff, but that's another discussion.

@withoutboats You're correct, the current proposal would require something like foo((arg1, arg2, arg3)). However, as you also noticed, the "unresolved questions" section mentions a possible extension by allowing things like fn foo<Args: Tuple + ...>(args: ...Args), which could be called like foo(arg1, arg2, arg3).

@camlorn Right, and nothing in the RFC assumes field order matters, i.e. there are no "partial tail borrows" or anything like that, it can all be implemented in a library with safe code for a fixed number of arities.

fn foo<Args: Tuple + MyTrait>(args: ...Args)

MyTrait would presumably imply Tuple, especially if it's a helper specifically for foo.
I was thinking at some point that it makes sense to put ... on the pattern but I was wrong!

the signature is fn(...Args) which needs the ... for all external interactions
the pattern is matching against a tuple, e.g. these should be equivalent:

fn foo<H, T: Trait>((head, ...tail): ...(H, ...T)) {...}
fn foo<H, T: Trait>(head: H, tail: ...T) {...}

I like the "... drops the parens" phrasing :slightly_smiling_face:
What bound on a generic type parameter guards being able to use T...?  Tuple, I guess?   () }.  C++, FWIW, requires the ... in the template parameter list for things you're going to ..., like fn foo<T...>().
Or, a possibly more interesting marker trait, where I used Tail1... without having constrained Tail1:
trait<T> SameLengthAs<T> : Tuple {}
impl SameLengthAs<()> for () {}
impl<Head1,Tail1,Head2,Tail2> SameLengthAs<(Head1, Tail1...)> for (Head2, Tail2...) {}

From a documentation perspective, it might be nice to just see the <T...> right up front, even if the method is sufficiently constrained later on to where T:TupleMap<fn(Foo)->Bar>, implying T:Tuple.
Hmm, I suppose that <T> vs <T...> is also the difference between being able to turbofish foo::<i32, u32>() vs needing to foo::<(i32, u32)>(), the same as the unresolved question relating to being able to call just call bar(3) instead of needing to bar((3,)).  (And typing the one-argument version of that was horrible, so I started thinking the variadic argument stuff should be part of this RFC.  But then I started contemplating homogeneous variadic arguments, which I think probably comes after Π-types so we can have something like fn max<T, const n: usize>(v:T, vs...: [T;n]) -> T, so now I'm less sure.  Unless the partial trait-based version would enable fn max<T:Array>(v:T::Item, vs...: T) -> T, with that Array trait working not unlike the Tuple one in this RFC...)
On joining: if I somehow got two packs, can I (tail1..., tail2...)?  (I normally can't in a pattern.  Hmm, maybe I could if they were distinct types.)
On patterns: can I have arbitrary stuff on either side of the variadic, like (a, b, yay..., y, z)?  I assume so, but it's not mentioned.
Bikeshedding: Option taught me that "move the ampersand inside" methods are named as_ref and as_mut.
Expansion musing: In C++, x... is pack expansion using a pattern, not joining.  So mapping over a tuple is essentially just (f(x)...).  (I have no idea what sorcery bounds the pattern syntactically.  Surprisingly for C++ syntax, I've never hit issues with it.)  Similarly, zip(x,y) is just ((x,y)...)—which, being templates, fails at instantiation time if the packs are of different lengths, so rust would need to bound it somehow (this is how I got to SameLengthAs above).  Almost certainly not part of this RFC, since Rust doesn't have the perfect forwarding problems that made it particularly essential in C++.  But perhaps interesting to contemplate, at least as part of picking syntax that doesn't preclude it later.  I can imaging having Some(x)... in a pattern, for example.  Or Tail::Output... when dealing with types.  (But of course there's also the silly possibilities, like (...x...) expanding to (...x.0, ...x.1) :cold_sweat:)

@scottmcm Rust can use similar operations to iterator adapters, but eager and heteregenous with #1650.

It's worth noting that similar syntactic sugar exists for arrays (let [head, tail..] = [1, 2, 3];, play, see rust-lang/rfcs#495 and rust-lang/rust#23121). It might be prudent to keep this symmetrical.

@killercup Sadly that's not valid in expressions (because we kind of screwed up when adding ranges).

Could we explicitly mention "partial tail borrows" under the alternatives? I didn't realize until very recently that this proposal's use AsRefs/AsMuts instead of tail borrows is one reason it doesn't run into the same problems as past variadics RFCs.

@Ixrec Yes, I'll add that.

I read my comment again, and think that I got distracted by the "variadic generics" name.
This RFC is great for "allow impl over generic tuples", and has awesome motivation for that.
I think people like me would be less distracted if the "accept arguments of varying length and type" and Index<usize, usize> stuff were under "future extension possibilities".  All of my weird questions like "can I expand a tuple in the middle of an array literal?" then go away, since this is obviously just for tuples.

@scottmcm Yes, it might be a good idea to move several items from "unresolved questions" into a "possible future extensions" section or similar.

I have a couple of questions that aren't addressed by the RFC. When pattern matching, what happens if I write (ref head, ref ...tail)? Is it a reference to a tuple? A tuple of references? An error? The reliance on ATCs also seems unfortunate.
My biggest concern with this RFC is that it's unclear how one would write an impl for a trait where the signature takes &self instead of self. The intention appears to be disallowing borrowing the tail (which makes sense, as that's been a significant concern in the past). This RFC lists that one of its primary motivations is to allow std to implement traits for arbitrary size tuples rather than up to size 12 as it does today. However, none of the traits that std currently implements for tuples has a method which takes self as an argument. They all take &self, no arguments, or something like &[Self].
For example, I would think one would want to write something like:
impl<Head, Tail, RightHead, RightTail> PartialEq<(RightHead, ...RightTail)> for (Head, ...Tail) where
    Head: PartialEq<RightHead>,
    Tail: PartialEq<RightTail>,
{
    fn eq(&self, other: &(RightHead, ...RightTail)) -> bool {
        let &(ref head, ref ...tail) = self;
        let &(ref right_head, ref ...right_tail) = other;
        head == right_head && tail == right_tail
    }
}

impl PartialEq for () {
    fn eq(&self, other: &()) -> bool {
        true
    }
}

However, that impl seems like it'd be impossible to write with this RFC. One might be able to get around that with a bound like for<'a> <Tail as Tuple>::AsRefs<'a>: PartialEq<<RightTail as Tuple>::AsRefs<'a>>, but that only works for traits where there is a blanket impl for  &'a T which behaves identically to T. Even if we just look at the implementations that std provides for tuples, that would mean an impl of Clone becomes impossible to write. It also would make this RFC incompatible with any trait where someone doesn't wish to provide a blanket impl over &T.

@sgrif The intention, which should be described in the RFC is that you convert to references once and then you iterate the tuple of references, with a helper trait (or, e.g. .map(Clone::clone) in the farther future).

@eddyb So you're thinking something like the following?
impl<Head, Tail> Clone for (Head, ...Tail) where
    Head: Clone,
    Tail: Tuple,
    for<'a> Tail::AsRefs<'a>: TupleClone<Output=Tail>,
{
    fn clone(&self) -> Self {
        let (head, ...tail) = self.elements_as_ref();
        (head.clone(), ...tail.tuple_clone())
    }
}

impl Clone for () {
    fn clone(&self) -> Self {
        *self
    }
}

trait TupleClone {
    type Output;
    fn tuple_clone(self) -> Self::Output;
}

impl<'a, Head, Tail> TupleClone for (&'a Head, ...Tail) where
    Head: Clone,
    Tail: TupleClone,
{
    type Output = (Head, ...Tail::Output);

    fn tuple_clone(self) -> Self::Output {
        (head.clone(), ...tail.tuple_clone())
    }
}

impl TupleClone for () {
    type Output = ();

    fn tuple_clone(self) -> Self::Output {
        ()
    }
}

That seems unfortunately unergonomic. Especially since for many traits, the path of least resistance will still ends up with that &&&&&&&&&T problem, which won't error out until you get to a sufficiently large type. Given that this feature is only visible when implementing traits for arbitrary sized tuples, it seems like we should try to ensure that writing that implementation is as ergonomic as possible.

Is there any reason why we can't allow borrowing the tail, other than that we'd have to guarantee that tuples are laid out in order?

@sgrif Borrowing the tail is the wrong problem to solve, as it limits us to one iteration direction and makes tuples end up with a lot of padding to account for the representation side of things.
Btw, your Clone impls can be just:
impl<T: Tuple> Clone for T where
    for<'a> T::AsRefs<'a>: TupleClone<Output=T>,
{
    fn clone(&self) -> Self {
        self.elements_as_ref().tuple_clone()
    }
}

Now ideally you'd just write self.elements_as_ref().map(Clone::clone) which I hope you agree is more ergonomic than any tail recursion we can come up with - the only problem is describing bounds for that:
impl<T: Tuple> Clone for T where
    for<'a> T::AsRefs<'a>: TupleMap<typeof Clone::clone, Output=T>
{
    fn clone(&self) -> Self {
        self.elements_as_ref().map(Clone::clone)
    }
}

Assuming we get typeof and generic closures, this can be a solved problem, although it's not that nice.
As for tail recursion, it can be always be represented using a fold abstraction, can't it?
The weird part is needing the replicate the same logic in both types and values, i.e. the same problem you'd face if you wanted iterator adapters to model the outputted values and not just their types.
Maybe we can solve that part using a macro? Like I said, ideally you wouldn't need to deal with trait impls.
And no, I wouldn't offer ...ref tail and elements_as_ref at the same time, I think I prefer the latter, in the face of the possibility of accidentally repeatedly referencing and getting types like &&&&&&&&&T.

@eddyb Yes, for Clone specifically we could write that impl. However, for traits which have additional blanket impls, that would not work. Making the Tuple trait #[fundamental] makes T: Tuple disjoint with Vec<T>, but not with T: SomeOtherTrait.

@sgrif Depends, does SomeOtherTrait have impls for tuples? I was told the fundamental logic is recursive - and even if it's not, you could probably get away with impl<T: Tuple> Trait for (...T).

@eddyb It doesn't matter whether SomeOtherTrait has impls for tuples or not. #[fundamental] prevents new impls for existing types. That does not prevent a hypothetical type where T: Tuple + SomeOtherTrait exists. Especially since someone absolutely could write impl SomeOtherTrait for (MyLocalType,) in a downstream crate. The point being, T: Trait1 is never disjoint from T: Trait2, regardless of fundamentalness.

and even if it's not, you could probably get away with impl<T: Tuple> Trait for (...T)

Well at that point you wouldn't need the T: Tuple, you'd need the T: Trait. impl<T: Trait> Trait for (...T) is an obviously recursive definition, which just brings us back to the same "you need to copy the trait and implement it for tuples" problem.

it limits us to one iteration direction

I don't see how that's the case?
impl<Head, Tail: DoInReverse> DoInReverse for (Head, ...Tail) {
    fn do_in_reverse(&self) {
        let (ref head, ref ...tail) = *self;
        tail.do_in_reverse();
        do_stuff_with(head)
    }
}

impl DoInReverse for () {
}

and makes tuples end up with a lot of padding to account for the representation side of things.

Correct me if I'm wrong, but the tuples already have that padding. It prevents a hypothetical future optimization where the fields are re-ordered to reduce padding, but it would not specifically make performance worse. I think it's a tradeoff worth considering.

Now ideally you'd just write self.elements_as_ref().map(Clone::clone) which I hope you agree is more ergonomic than any tail recursion we can come up with

Sure, that's definitely a more ergonomic impl. Not all traits can be represented that way (e.g. Debug), but those traits will need a similar pattern to the impl I presented for Clone anyway. The impls are usually quite painless to write either way though. Repeating the trait is my concern.

Assuming we get typeof and generic closures, this can be a solved problem, although it's not that nice.

That would mean this feature being ergonomic to use is now blocked on 3 future language features, only one of which has an RFC written.
Now I can't confirm that myself, because I had the same intuition as you, but I really did mean T: Tuple coupled with (...T) as a way to have the compiler understand the impl applies solely to tuples.
And if you can have an impl on tuples downstream, isn't that a true overlap anyway?

Correct me if I'm wrong, but the tuples already have that padding.

They do not, (u8, (u8, 16)) is 6 bytes whereas (u8, u8, u16) is only 4.
What field reordering can do is ensure any 3-tuple containing two u8s and one u16 only uses 4 bytes.

Ah yes, I see the issue WRT padding now. Honestly, it just leads me back to my thinking in the previous RFC that (Head, ...Tail) should be a distinct type from some arbitrary length tuple (which would have the extra padding, but wouldn't affect random tuples that aren't used as variadics). Even with everything you've described, an impl like impl<T: Tuple> Clone for T where for<'a> T::AsRefs<'a>: TupleMap<typeof Clone::clone, Output=T> implies a very complex feature with a huge amount of cognitive overhead. Especially given the roadmap item of "Rust should have a lower learning curve", it strikes me as an odd direction.
If the motivating use case is to ultimately allow variadic functions (and presumably structs which contain variadic generics as well), having ...T be a distinct type which can be borrowed would result in a feature that much more closely works with the rest of the language as it already does today. We'd lose the ability to implement things for arbitrary tuples, but I'm not sure that motivation needs to exist if the way variadics are passed is something other than an arbitrary length tuple.

@sgrif WRT impls for &self-- that's entirely my fault for not specifically describing those.

One other question -- What bounds do I need to write on T in order for ...T to be valid? Is it just T: Tuple? Specifically in the above example on TupleClone, do I need to write where Tail::Output: Tuple to write Output = (Head, ...Tail::Output)? Or is ...i32 treated the same as ...(i32,)?

...T iff T: Tuple.

@sgrif You don't need where Tail::Output: Tuple if the other trait has type Output: Tuple;.

@eddyb Yes, I know. The question was more about whether the bounds were required or not.

Just to give what some concrete code in the wild would look like, this is what Diesel's code would look like with this RFC (as I understand it -- harder to write code without being able to see if it compiles). https://github.com/diesel-rs/diesel/commit/51ca264b05da35ba0e84a84ee4ed3fd50717a024

Regardless of the state of this RFC, I'd like to offer a moment of silence for these gems in our docs...

@sgrif lol yeah, My own.

Will the proposal make it possible to use tuple generics in other setups than in recursion? Also, will it support non uniform types?

@est31

Will the proposal make it possible to use tuple generics in other setups than in recursion?

I'm not sure what you mean by this. If you mean "do I need recursion to iterate over a tuple", the answer is no. Generic closures would allow for map, foreach, etc. constructs as shown in the TupleMap example.
This effect can be imitated currently using a struct with multiple implementations of an Fn-like trait.

Also, will it support non uniform types?

This RFC proposes a language addition to allow implementing traits for all tuples. Tuples are a non-uniform datatype in the sense that their type arguments vary by position. However, tuples differ from traditional HList-style non-uniform datatypes in that they aren't defined recursively. This means that they are not limited to (Head, ...Tail)-style matching, and can be matched in a variety of ways: (...Front, Last), (HeadOne, HeadTwo, ...Tail), (Head, ...Middle, Tail), etc.

Potential extensions to this RFC could allow ...Type in non-tuple generics or in function argument types, like fn foo(args: ...Args). This would allow functions and traits to use variadic generics without explicit tuples. This could enable things like the proposed foo[i, j] syntax usingIndex<usize, usize>.

I'd like to see more elaboration about this in the RFC. To me all function signatures fn (args...) -> ReturnType already do look really similar to fn ArgsTuple -> ReturnType. So which problems would there be if we were to just allow this? (I'm pretty sure there are some problems, but would really like to know what they are, and what can / cannot be done about them).

OK, so, I'm sort of slowly getting caught up here. On the topic of coherence: @sgrif is definitely correct that T: Tuple and T: Foo would not be considered disjoint simply because Tuple is fundamental. Sorry @eddyb if I gave you a distinct impression. What is true is that, because Tuple is fundamental -- or, alternatively, because it is a lang-item -- we would know that it is only implemented for tuples.
There is also a notion of "fundamental types" -- which does not presently include tuples -- that might be relevant here. I believe the only current fundamental types are &T and Box<T>. Basically this means that if you are going to implement your trait for all &T, you have to do it initially, since somebody downstream is allowed to implement it for &TheirType or Box<TheirType> (which could conflict).
I'd like to understand better why this is an issue, though.

@gnzlbg I picture it as "..." next to a name means "collect zero or more arguments into this thing".
I can even imagine this with arrays, for homogeneous variadics:
pub fn min<T: Ord, const n: usize>(v: T, ...vs: [T; n]) -> T {
    let f = |v1,v2| if v1 <= v2 { v1 } else { v2 };
    vs.into_iter().fold(v, f)
}

So you can min(1, 2, 3) and it'll just work.

@gnzlbg @scottmcm It might be helpful to the discussion to look at the recently added "Future Extensions" section if you haven't done so already.

@cramertj I like it.  Good to see the Index example written out :+1:
(Unimportant side note: I can't convince myself whether it should be (&self, ...indices: ...Idxs), (&self, ...indices: Idxs), (&self, indices: ...Idxs), or (&self, ...(indices: Idxs)).)

@scottmcm I see the ...types, ...values as type/value list that expand into t0, t1, ..., tn. If we want to be able to expand types independently of values, my intuition says that:

(&self, ...indices: ...Idxs) expands to (&self, index0, index1, ..., indexN: Idxs0, Idxs1, ..., IdxsN).
(&self, ...indices: Idxs) expands to(&self, index0, index1, ..., indexN: Idxs)
(&self, indices: ...Idxs) expands to (&self, indices: Idxs0, Idxs1, ..., IdxsN)
(&self, ...(indices: Idxs)) expands to (&self, (indices0: Idxs0), (indices1: Idxs1), ..., (indicesN: IdxsN))

I wouldn't expect any of them to do the right thing, except maybe the last one =/

The whole discussion starts to look like we are reinventing C++'s parameter pack rules 😄 (C++ uses @gnzlbg third option indices: Idxs...)

@gnzlbg That intuition is not entirely accurate, as the patterns are "internal" to the function while the types are "external", making indices: ...Idxs a variable number of arguments bound to the same pattern.
The last example would make sense if we considered the syntax and semantics to be "type ascription on patterns" but that ship has sailed and I no longer think it can provide a reasonable implementation strategy.

How would the extension to allow tuple unpacking for function argument work on the ABI level?
For example:
fn foo<T...>(args: T...) {}

Would this be a function with one tuple argument, or a function with many arguments?
Afaik that's a relevant consideration if we want to use this for making the Fn traits stable, unless we define the ABI of a tuple argument to be always equivalent to that of one with many arguments - and then we might have issues if we wanted this for extern "C" ABI's or similar.
Also can this proposal deal with unsized types in the tuples?

I think that for extern "C" functions it makes sense to make tuple unpacking have the same ABI as an n-ary C function.
For Rust itself, I don't see any problems in leaving this as "implementation defined". AFAIK to a user of this features it doesn't really matter. Maybe we could reconsider setting an ABI in stone at the end of the stabilization phase when we have more experience with the feature.

Well, it matters in the sense of knowing wether his proposal will actually be able to apply to the Fntraits or not :)

@Kimundi The intention, AFAIK, is for fn((A, B)...) to be literally fn(A, B), i.e. identical at the ABI level.
And if any VG scheme doesn't work for the Fn traits, then I think it's a waste of time, personally, since that was the strongest motivation I ever had for any such proposal (including the very first one).

@eddyb: That was my understanding as well, but I'm not directly seeing it in this RFC. (Might have to reread it though)
I guess I'm wondering what semantic the example fn foo<T...>(args: T...) {} from above would have - Right now I'm reading it as "For a variadic number of types, define foo as a function that takes a tuple with that variadic number of types and bind it to the args variable".
Or should this be read as "For a variadic number of types, define foo as a function that takes a variadic number of arguments and move them into the args tuple afterwards"?

@Kimundi That's why the syntax is there, taking a tuple would be x: T or x: (T...).

Ah, I had missed that distinction. So the syntax would indeed have the special meaning of defining a variable number of arguments, and making them available as a tuple to the function body without being one in the ABI?
Also, RFCs like https://github.com/rust-lang/rfcs/pull/1909 make me wonder if this proposal can handle ?Sized bounds on tuple elements (or indeed how to handle bounds in general, though I guess its all going to be a variation of the Clone example)

I'm also wondering how for example the provided Index example would work in practice in regard to backwards compatibility with existing impls.
Todays Index:
pub trait Index<Idx> where Idx: ?Sized {
    type Output: ?Sized;
    fn index(&self, index: Idx) -> &Self::Output;
}

Proposed Index (with @eddyb's pattern syntax):
pub trait Index<...Idxs> where Idxs: Tuple + ?Sized {
  type Output: ?Sized;
  fn index(&self, indices: ...Idxs) -> &Self::Output;
}

Now, if we take this existing impl:
impl<T> Index<usize> for Vec<T> {
    type Output = T;

    fn index(&self, index: usize) -> &T {
        &(**self)[index]
    }
}

Then I would expect that the compiler interprets it with the new Index definition like this:
impl<T> Index<...(usize, )> for Vec<T> {
    type Output = T;

    fn index(&self, index: ...(usize, )) -> &T {
        &(**self)[index]
    }
}

Which would result in index now being a (usize,) instead the previous usize.

@Kimundi It's not ambiguous, the impl doesn't "infer" anything like that from its trait.
You had a method with two arguments (self and index), that's obviously not variadic.
In other words, fn foo(tup: (A, B)...) { body } expands to fn foo(a: A, b: B) { let tup = (a, b); body }.
That means that if the arity of a trait method is known, you can implement it with variadic syntax, capturing those arguments as a tuple (even if the trait doesn't use variadics at all!) or as separate arguments.

Ah sorry, I think I confused myself into thinking that the ...T in args: ...T and for example tuple: (...T) would have the same meaning, which is of course not what is being proposed.
Using the "unpacking" interpretation, args: ...T would read as args: T1, T2, ... which does not make sense in the general case where T != (T1,) , which means there is no ambiguity with confusing it with a fixed arity like args: T1.
Or in other words, the ... in a fn(args: ...T) is to be read as a syntactic modificator of the function signature, and not as part of the type itself.

So I've been meaning to write a comment here for a while. I have some concerns about how this approach will interact with inference and so forth. I feel like there is complexity that hasn't been fully worked out and isn't specified in the RFC. It seems to me that types like (T, ..U) do not readily desugar into a so-called Tuple trait -- in particular, their role is something "synthesizing" a new tuple type, and sometimes "matching and deconstructing" an existing one, and the way you would desugar such a type will depend on the role.
As an example, if you have:
``rust
impl<T, U> Trait for (T, ..U) { ... }

This could be roughly expressed in terms of a `Tuple1` trait (tuples of arity at least one) like:

```rust
impl<A, T, U> Trait for A where A: Tuple1<Head=T, Rest=U> { ... }

However, if in that impl I have the following:
let x: (T, ..U) = ...

This type is more of a "synthesis", and I imagine it would "desugar" to something like <T as TupleBuild1<U>>::Tuple, where TupleBuild1 is a trait with impls like:
impl<A,B> TupleBuild<(B,)> for A {
    type Tuple = (A, B);
}

I haven't really thought through this much farther than that -- that is, to see that we could trivially desugar into a Tuple traits with Head and Tail. It all seems like something that probably can be worked out, but I feel like I didn't see any text about this in the RFC. Am I missing something? Are there comments or other sources I ought to be reading?

@nikomatsakis You may have missed https://github.com/rust-lang/rfcs/pull/1921#issuecomment-282539243.

I hope this feature would also allow one to be generic over fn-types and Fn-types with arbitrary arguments, in addition to being generic over tuples of arbitrary length.

@jethrogb This RFC is intended as a minimal first step, and would not change how generic arguments or Fn traits work. However, the goal is to eventually support things like that through something like the proposed future extensions.

@eddyb

You may have missed https://github.com/rust-lang/rfcs/pull/1921#issuecomment-282539243.

It seems like that comment is just saying what I was worried about. =) I'd feel better if this could be "desugared" into an associated trait. The fact that is has more power makes me feel like there is missing text from the RFC describing how this interacts with the type system.
Let me put it another way: I'd like to work out how we model this in chalk. Unfortunately, right now, I think that we could not model this purely in chalk's "lowering" phase, but we'd have to integrate this tuple-manipulation directly into the more core unification / proving phases. This isn't necessarily bad but it definitely makes me more reluctant.
I'll noodle a bit more on the matter. I'm sure I'm only repeating things you've already worked out for yourself or left in comments elsewhere. Forgive me. =) You may want to skip the TL;DR at the end.
An important question then is this: given that (T, ..H) is sometimes used to destructure a tuple type and sometimes used to construct one, how clear is it when it will play each of those roles? As you suggested in your comment, It seems to me that it's not actually very clear, at least if we want it to interact fully with inference (which is not, to me, a given). That is, it's not like we can say "when you are doing trait matching, (T, ..H) always destructures an incoming tuple type". You might have a trait with exactly one impl, like this:
impl<H, T> Foo<H, T> for (H, ..T) { ... }

And a trait match like Foo<u32, (i32,)> for ?X, where ?X is a variable. Given that we sometimes infer some impl arguments from others, that the H and T are known, one might expect then that here would we infer ?X = (u32, i32). In this case, (H, ..T) is acting in the "composing" role, than the destructuring role.
This "dual role" is not uncommon in unification, but with a constraint like ?X = (?H, ..?T) it's a bit different. This is because, in order to figure out the arity of ?X (how many things are in the tuple), the arity of ?T must be known. That is, a constraint like ?X = (?H, ..?T), while it resembles an ordinary unification constraint between two types, it is not -- the right-hand side is not a type!
I suspect that the best way to "desugar" this into existing concepts is to take advantage of the fact that Rust will infer on the basis of existing impls, and imagine a trait like this:
trait Tuple1<H, T> { }
impl<A> Tuple1<A, ()> for (A,) { }
impl<A,B> Tuple1<A, (B,)> for (A,B) { }
impl<A,B,C> Tuple1<A, (B,C)> for (A,B,C) { }
... // ad infinitum

Now we can desugar:
impl<H, T> Foo<H, T> for (H, ..T)
// becomes:
impl<X, H, T> Foo<H, T> for X
    where X: Tuple1<H, T>

The key point here is that if X is known, then H and T become known. Similarly, if T is known, then X becomes known (at least in terms of H, which may not yet be known).
Now, all of this is only for (H, ..T). Given that I believe the RFC wants to support (T.., H) and perhaps even (A, B, ..H, C), it seems like we have to give up thoughts of modeling this as special traits, because we'd need not only an infinite family of impls, but an infinite family of traits as well (one for each possible placement of ..T).
In chalk terms, this suggests that what we would want to do is to add a new kind of type. Right now, there are variables (?H), application types (like Vec<T>), and projection types (<T as Iterable>::Iter). This RFC would add a fourth, which I will (whimsically) call dotdot types for now. They would work, I think, somewhat like projection types, which means that when asked to unify ?X = (?A, ..?B, ?C) (or any unification where the RHS is a dotdot type), we would have special code that would figure out whether either ?X or ?B has a known arity and thus we can make progress, and otherwise hold off (much like, for a projection, we have to figure out if we know enough to normalize it).
Continuing in this vein, we have to ask ourselves, what do we do when we are type-checking a generic impl defined in terms of a dotdot type? (In chalk terms, what do we do when we have .. applied to a skolemized variable.) In other words, in the body of this impl, how do we think of Self and T?
impl<H, T> Foo for (H, ..T) {
    fn bar() {
        // how do I think of `Self` in here? What about `T`?
    }
}

The same question arises with normalization. Sometimes we have something like T::Iter where we don't know enough to normalize it something else (chalk internally recognizes this by creating an "application" version, representing an associated type projection where we can't normalize it). Presumably we'd do the same thing with a "dotdot" type, so that (H, ..T) is considered "fully normalized".

TL;DR. OK, that was helpful to me, maybe not to anyone else. I think my judgement is that, indeed, this represents a pretty deep addition to the language -- it is not just a "surface" change. This is reflected in the fact that to model it in chalk, one would have to modify the core unification procedures. However, given that we have to modify the core unification procedures, it seems to fit more-or-less in the mold of how we have to handle normalization.
This seems like a great example of where we could use chalk to prototype the idea and work out any possible implications on the rest of the system -- which seem somewhat unlikely, but worth teasing out -- before we go messing around in rustc itself.

@nikomatsakis What I had in mind is handling it in unification. Not sure how else you'd do fn(H, ...T).

One other thing I want to mention. It wasn't obvious from the way I described it, but this does introduce the possibility of unification failing to make progress. The example that @arielb1 raised was (..A, B) = (C, ..D) -- basically, unless you know either A or D, you can't make progress.
(In terms of how chalk handles unification, at least if we handled this like normalization, is that we would rewrite (..A, B) = (C, ..D) into (..A, B) = ?X and (C, ..D) = ?X, and any rule that concerns a "dotdot" type would only make progress if the arity of either ?X or the .. variable is known. In this case, that means that either A or D would have to be known, or else inference would stall out.
Similar problems can of course arise with projections: you might have <?A as Iterator::Item> = ?B, and we can't make progress until ?A is known.

Repeating what I tried to mention before: How does this interact with the fact that tuples can not contain all existing types today? Specifically, they can not contain unsized types except for the last field, so this gets rejected by the compiler today:
trait MyTuple {}
impl MyTuple for (str, str) {}

14 | impl MyTuple for (str, str) {}
   |      ^ the trait `std::marker::Sized` is not implemented for `str`

(see also https://github.com/rust-lang/rfcs/pull/1592)
Presumably, if we get variadic generics we want them to be usable for all types, since they are just a scheme to work with multiple independent type parameters at once without any concern for their actual representation.
For example I could imagine an T... API that expects one to specify for example T... = <str, [u8]>, aka T = (str, [u8]), in order to produce a (&str, &[u8]).
Also, there is the fact that @nikomatsakis talked about making passing unsized values to functions just work, which would mean that fn(args: T...) would also need to handle tuples with unsized type in them.

The only way I can see to satisfyingly make this work is to allow unrepresentable tuples and having an auto trait for it that can be opted into:
(str, str): !Sized + !Representable
(u32, str): !Sized + Representable
(u32, u32): Sized + Representable

Variadic API's could then opt-into it as needed just like API can opt-into Sized today:
fn foo<T>() where T: Tuple + ?Representable { ... }

foo<(str, [u8])>();

An alternative would maybe be to go more into C++'s direction and interact with variadics purely through the T... syntax:
// Instead of this:
<T> where T: Tuple
  => T...

// Only allow this:
<T...>
  => T...
  => (T...): SomeAlternativeTupleTrait

An alternative would maybe be to go more into C++'s direction and interact with variadics purely through the T... syntax

I rather like that.  That way the choice between writing T or (T...) isn't just style, since the first is the pack and the later is a tuple.  Once we have this for values, that also keeps [args...] and (args...) as equals.  And because the pack is a thing, we could define things like T : Trait† as "All the types in the pack implement the trait", which is helpful and very different from (T...) : Trait—especially for implementing the latter.  I also like that it would probably avoid the world where a bunch of code gets written explicitly passing tuple types into things that don't actually want tuples, but that's the only option.
† or T... : Trait or (T:Trait)..., who knows
(Allowing extra-fat pointers for  multi-trait objects or &(str, str) is of course reasonable, but feels like a different things from variadics.)

@Kimundi

How does this interact with the fact that tuples can not contain all existing types today? Specifically, they can not contain unsized types except for the last field...

@Kimundi and I had a discussion about this on IRC. My personal opinion is that, if functions are eventually allowed to take unsized arguments by value, then tuple types (and other ADTs) should be allowed to contain multiple unsized values. Allowing this would result in a change to the maximum size of fat pointers, but it's a backwards compatible change, and one that has other uses, such as custom DSTs. I'll do some more investigation on this and report back.
@scottmcm

because the pack is a thing, we could define things like T : Trait† as "All the types in the pack implement the trait", which is helpful and very different from (T...) : Trait—especially for implementing the latter.

That's an interesting idea. I have to say, though, I personally find T: Trait where T is some "packed" list of types to be really confusing. Since T isn't really a type, but a list of types, it doesn't really make sense to bound it. One could imagine sugar like T...: Trait, but that works just as well for the tuple-based representation, and could be added as a followup to this proposal.

Presumably, if we get variadic generics we want them to be usable for all types, since they are just a scheme to work with multiple independent type parameters at once without any concern for their actual representation.

Worth mentioning that T is implicitly Sized, I would assume T... would all be also unless they were bound T...: ?Sized; its possible (if necessary) to implement this incrementally and not allow a ?Sized bound until a later date.

@withoutboats:
Right, you'd still have to opt-out of the default traits either way, the issue is wether the tuple type is even valid for the typesystem to begin with.
And yeah, this can of course be implemented incrementally, but we should have a clear notion of what to increment to, or else we run into the danger of this never becoming possible at all. :)

I've read through this RFC more closely. Regretfully I feel confused by it. Here are my sort of jumbled thoughts:
First, when it comes to variadics (in general) there seems to be a sort of table of components:

Tuples
Parameters

Types
Type of a tuple of any arity
Variadic type parameter

Values
Tuple value of unknown arity
Variadic function arguments

That is, we're dealing with the cross product of both type & value level variadics, and of tuples & type parameters.
This RFC proposes to start with just representing the types and values of tuples of unknown arity. Its not obvious to me why that is the best approach.
Second, the reason that jumps to mind to start with tuples (and this RFC suggests) is that the most compelling motivation for variadics is to implement traits for all tuples. But I notice that this RFC never demonstrates how you would actually do that.
I decided to start with the simplest trait we implement for tuples, Default, and try to figure out from the RFC how to implement it.
The first problem I ran into: I'm not sure what this RFC would propose is the receiver of this impl:

Is it impl<T: Tuple> Default for T? Surely not, we haven't bounded the members of the tuple at all (I'm not really clear what Tuple is useful for given that).
Is it impl<A: Default, T: Default> Default for (A, ...T)? I suspect this is the case, but the RFC's text is really vague on the key detail of what ...T means (it suggests "(A, B, C) == (A ....(B, C), but that doesn't seem right to me. ...T is a sequence of distinct type parameters, it seems, what does ...(B, C) even mean? Surely it isn't equivalent to (B, C).

Now I believe that this is how we would implement it:
impl<A: Default, T: Default> Default for (A, ...T) {
    fn default() -> (A, ...T) {
        (A::default(), ...T::default())
    }
}

Is that right? Does ...T::default() construct a tuple of Ts? What would actually be going on here?

Overall, I think this RFC is not a complete resolution of the problem. My intuition, which could be wrong, is that this is the correct approach:

We start with a definition of ...T: Trait and how to instantiate the type of a PhantomData<...T>.
Then we'd define a syntax for constructing a variadic tuple type from a ...T parameter, which any tuple would be an insantiation of.
Then we'd define the syntax for constructing and destructuring tuple values of abstract arity (we might need to start with non-tuple variadic expressions conceptually, but probably we'd want to enable users to write tuple expressions before letting them have e.g. variadic functions).

I think its the first part, which grounds the rest, which is missing from this RFC. The section on the ...T type is not a sufficient explanation. The purpose of the Tuple trait as a foundational element is not clear to me. We need to firm up this foundation and then build from there.

Here's my impression of what the impls of Default and Clone for all tuples "want" to be:
impl Default for () {
    fn default() { }
}

impl<A: Default, ...T: Default> Default for (A, ...T) {
    fn default() -> (A, ...T) {
        // Unclear what the type of the second here would be? <(...T) as Default>::default() ?
        (A::default(), ...Default::default())
    }
}

impl Clone for () {
    fn clone(&self)  { }
}

impl<A: Clone, ...T: Clone> Clone for (A, ...T) {
    fn clone(&self) -> (A, ...T) {
        // head: &A, rest: &(...T). Is ...ref rest correct or is it ref ...rest?
        let &(ref head, ...ref rest) = self;
        (head.clone(), ...rest.clone());
    }
}

@withoutboats:
As far as I understood it, this is the correct intuition how to implement Traits with this RFC:
impl<A: Default, T: Default> Default for (A, ...T) {
    fn default() -> (A, ...T) {
        (A::default(), ...T::default())
    }
}

// And of course the terminal case:
impl Default for () {
    fn default() -> () {
        ()
    }
}

It is all based on delegating to the next smaller tuple.
...T in type position would mean "unpacking" the types on its inside. So ...(B, C) == B, C, thus (A, ...(B, C)) == (A, B, C). This would obviously only work in a limited number of location - specifically only for tuple type constructors here.
...<expression>, as in ...T::default() would mean "take <expression>, which needs to be of a tuple type, and expand it to a sequence of values that are each the copied/moved contants of he elements of the tuple". So given t = (true, 1), ...t == true, 1, and thus ("hello", ...t) == ("hello", true, 1). Again, it would only work inside places where a variadic expansion of a tuple makes sense, like a tuple constructor or a function call.
A exact translation of the expression case would probably look like this:
let t = (true, 1);
let _ = ("hello", ...t);
// ====>
let t = (true, 1);
let _ = {
    let a = "hello";
    let (b, c) = t;
    (a, b, c)
};

That is, there would definitely be some moving/copying going on if optimizations don't kick in.

Re:
// head: &A, rest: &(...T). Is ...ref rest correct or is it ref ...rest?
let &(ref head, ...ref rest) = self;

Afaik this RFC does intentionally not propose something like a ...ref rest since it would not actually be possible to implement - a (A, B, C) does not contain a (B, C) so that creating a &(B, C) would work.
Instead, thats what the intrinsic methods on the Tuple trait are for - to let the compiler handle turning a &(A, B, C) into a (&A, &B, &C). From there you can use the by-value based recursive pattern to implement it.

@withoutboats WRT Clone-- did you see that I implemented Clone in the RFC as an example?

We start with a definition of ...T: Trait and how to instantiate the type of a PhantomData<...T>.

...T: Trait isn't proposed anywhere in this RFC-- @scottmcm brought it up as a way to constrain "type packs", seeing as it's impossible to bound T in impl<...T> if T is a "pack" of types, rather than a type itself. This RFC avoids that problem by making T a tuple, which can be bounded directly.
As @Kimundi said, the idea is to implement traits inductively (/recursively), first implementing for () and then for (Head, ...Tail) where Tail: Tuple. If you want to implement a trait Foo for tuples whose members implement Foo, you would write impl Foo for () and impl<Head, Tail> Foo for (Head, ...Tail) where Tail: Foo + Tuple.
As for the necessity of the Tuple trait, it "unlocks" the ability to "untuple" (...T) the type, and it allows for converting from &(A, B, C) to (&A, &B, &C) (and the same for &mut).

I see now that you implemented Clone. I don't know how you would implement Default by a similar method, because your method seems to require iterating over the values in a tuple, rather than allowing for a type-level iteration. And the implementation for Clone seems extremely indirect & complicated.
This involves changing the type system by adding a magic Tuple type, so this isn't a purely library level approach. What do you see as the advantage of doing this over just adding variadic type parameters like C++ has?

@withoutboats because variadic type params are extremely hard to do correctly without overload-based generics (like C++).

@withoutboats Yes, @Kimundi's implementation of Default matches what I would have written myself. ...T is not sugar for any existing concept, and yes-- this relies on a magic Tuple trait that is implemented exclusively for tuples and allows tuples to be packed and unpacked.

@withoutboats and I had a discussion about this on IRC. I'll try to summarize here so that others can comment. @withoutboats if I'm not representing your thoughts correctly, let me know and I'll edit this.
The goal of this RFC is to allow working with variadic-length type lists. I'd prefer that those type lists be represented as tuples that can "have the parens dropped", whereas @withoutboats would prefer that variadic type parameters be a first class concept. The two strategies are similar, but have a few notable differences.
In the short term, this RFC would allow tuple implementations like the following:
impl Default for () {
    fn default() -> { }
}

impl<Head, Tail> Default for (Head, ...Tail)
    where Tail: Default + Tuple
{
    fn default() -> Self {
        (Head::default, ...Tail::default())
    }
}

while @withoutboats strategy would look more like this:
impl Default for () {
    fn default() -> { }
}

impl<Head, ...Tail> Default for (Head, ...Tail)
    where ...Tail: Default
{
    fn default() -> Self {
        (Head::default(), ...(...Tail)::default())
    }
}

The syntax ...:Tail: Default here means that every type in the type-pack Tail implements Default. If it's necessary to bound Tail as a list directly, then the bound could be written (...Tail): Default. This would be necessary when,  for example, bounding if any type in the list implements a trait, rather than if all types in the list implement a trait.
In the long(er) term, both designs would be used in the Fn traits, so I'll try to show what that would look like here.
This RFC:
pub trait FnOnce<...Args> where Args: Tuple {
    type Output;
    fn call_once(self, ...args: ...Args) -> Self::Output;
}

This would make f.call_once(1, 2, 3) basically just sugar for f.call_once((1, 2, 3)), and would make FnOnce<A, B, C> sugar for FnOnce<(A, B, C)>.
@withoutboats's design:
pub trait FnOnce<...Args> {
    type Output;
    fn call_once(self, ...args: ...Args) -> Self::Output;
}

This is identical except for the omission of the Tuple bound on Args. However, there's no tuple-based sugar here: FnOnce<A, B, C>` is just that-- variadic type lists are a first class concept.
TLDR:
@withoutboats would prefer type lists always be instantiated like impl<...T> or similar, bounded like ...T: Trait, or, when necessary, (...T): Trait.
This RFC proposes type lists be introduced like impl<T> where T: Tuple and bounded like T: Trait, with some sugar impl<...T> that allows you to drop the parens.

@withoutboats

But that seems like we're saying we'll never have first class variadics, just a syntax for manipulating types which implement Tuple. I still don't understand why we'd want to do this instead of having variadic type parameters?

Well the basic idea is to have first class variadic parameters through tuples - its not meant to be in any way more limiting, and would be easier to manipulate because a tuple is a type, and thus can be manipulated by the type system already. Compare that to C++ where its a entirely separate concept, with its own syntax in compiler error messages and all that.
However, it may well be that once all kinks are worked out and we have the variadic tupling and untupling at all needed places, the difference might cease to be relevant.

@cramertj:

The Fn traits will most likely just get rid of the tuple in the ABI completely if we have variadics:
pub trait FnOnce<...Args> where Args: Tuple {
    type Output;
    fn call_once(self, args: ...Args) -> Self::Output;
    // The ABI of this is N arguments that get wrapped
    // in a tuple inside the function to handle the variadic-ness
}

pub trait FnOnce<A, B> {
    type Output;
    fn call_once(self, arg1: A, arg2: B) -> Self::Output;
    // The ABI of this is two arguments arg1 and arg2
    // that where never part of a tuple to begin with nor will be put into one directly.
}

So no sugaring would be going on, and f.call_once(1, 2, 3) would have distinct meaning from f.call_once((1, 2, 3)). Same with FnOnce<A, B, C> which would be distinct from FnOnce<(A, B, C)>
This would be a breaking change, but the Fn traits are intentionally unstable to allow this kind of change. Its also why we have the special Fn(A, B, C) sugar stable - it can remain unchanged even while those internal details change.

@withoutboats:
Being able to do ...T: Trait with the meaning "each parameter in the pack needs to implement Trait" is more limiting than T: Trait where T == (...U) == (A, B, C, ...). With the latter, you have the option of encoding more complicated logic into the trait impl, like "only accept type lists with an even amount of elements".
You could make the argument that you would be able to do that in either case, and ...T: Trait would just be a convenient basic construct for the common case, in which case I would see it as redundant extra semantic.

@withoutboats, @cramertj:
However, while writing this I'm starting to wonder if we want ...T: Trait for bounds anyway, regardless of wether the variadics work through tuples or not.
The reason is making impls for tuples less recursivly-verbose. For example, both Clone and Default could be defined like this:
impl<T: Tuple> Clone for (...T) {
    fn clone(&self) -> (...T) {
        let t = self.as_element_refs(); // T == (&A, &B, &C, ...)
        self.map(Clone::clone)
    }
}
impl<T: Tuple> Default for (...T) {
    fn default() -> (...T) {
        T::generate(Default::default)
    }
}

Where map and generate would be tuple helper functions provided by the Tuple trait, together with the inherently needed as_element_refs.
Given those, we need to put a place to put the element bounds - we can't put it on the next smaller tuple, because there is none. But if we could write where ...T: Trait with the meaning where A: Trait, B: Trait, C: Trait, ... then it would presumably work.

IIUC one could use @withoutboats proposal to implement a user-defined tuple-like type with different semantics. Would it be possible to do the same with the Tuple proposal?

After comparing tuple-based variadics and pure-variadic type parameters just now, I'm wondering if maybe the most ergonomic route is simply a hybrid solutions between both:

Type parameters are defined in terms of a unbound <...T>, and can be bound by doing ...T: Trait which means element-wise bounds.

...T: ?Sized would also potentially allow sidestepping the unsized tuple representation question.

There would be a Tuple trait that all (...T) implement and that provides things like as_element_refs()
This implies that impl<T: Tuple> would be equally valid ways to work with variadics.
Converting to/from a tuple type or value with ... would mostly just work like in this RFC.

This is, of course, mostly just what the RFC describes as future extensions.
However, I'm still not sure if this would mean that the T in ...T should just be a tuple with build-in declaration syntax, or if it should be a distinct list of type arguments that can just be converted to a tuple easily. I think I'd need to sit down for a few hours and compare all aspects between both for that.
Anyway, the point is if we do the hybrid solution then we have the option of starting a implementation with whatever is easiest: Eg we could implement the RFC as is then later "widen" the meaning of variadic generics to be untupled paramter packs instead of just tuples by making impl<T...> valid.

@gnzlbg:
struct Foo<T> where T: Tuple {
    // use ...T here to get a list of types
}
let _: Foo<(A, B, C)>

// With the `...T` in generic declarations extension:
struct Foo<...T> where T: Tuple {
    // use ...T here to get a list of types
}
let _: Foo<A, B, C>

Why does one need the where T: Tuple bound in the second case?

@Kimundi Could you show the type signature of the map function you're talking about? As far as I can tell its not typeable without major extensions to the type signature.
What I don't love in all of these proposals is that to operate on tuples by reference you need a specific unique helper trait. This is fundamentally because we don't support transforming (A, B, C) into (&A, &(B, C). Only something like @Kimundi's map could solve this problem.
Under my proposal though we don't need any built in tuple trait, because anything it could include could just be defined through methods in impl<...T> (...T).

The syntax ...:Tail: Default here means that every type in the type-pack Tail implements Default. If it's necessary to bound Tail as a list directly, then the bound could be written (...Tail): Default. This would be necessary when, for example, bounding if any type in the list implements a trait, rather than if all types in the list implement a trait.

Its also possible that ...T: Trait does not imply (...T): Trait. To pick an arbitrary example: ...T: Read. You just can't express a variadic bound under the T: Tuple proposal except indirectly through such a Tuple implementation.

@gnzlbg: Because that is how the RFC defines that extension at the moment.

@withoutboats: Both the RFC and comments further up this thread include an example implementation of map:
trait TupleMap<F>: Tuple {
    type Out: Tuple;
    fn map(self, f: F) -> Self::Out;
}

impl<F> TupleMap<F> for () {
    type Out = ();
    fn map(self, _: F) {}
}

impl<Head, Tail, F, R> TupleMap<F> for (Head, ...Tail)
    where
    F: Fn(Head) -> R,
    Tail: TupleMap<F>,
{
    type Out = (R, ...<Tail as TupleMap<F>>::Out);

    fn map(self, f: F) -> Self::Out {
        let (head, ...tail) = self;
        (f(head), ...tail.map(f))
    }
}

Its basically works by accepting a function object that needs to implement the function traits for all tuple element types - eg mapping (A, B, C) -> (X, Y, Z) requires F to implement Fn(A) -> X, Fn(B) -> Y and Fn(C)  -> Z.
However, writing this makes me remember that you'd still need generic lambdas to make passing Clone::clone directly like this work. Which can be worked around, but apparently only at a greater effort than the recursive impl.

@withoutboats:
Its actually a fact that you can not implement the &(A, B, C) -> (&A, &B, &C) conversion without a extra language extension for it - be it either the special helper trait that would be defined by the compiler, or a extension of pattern matching. The assumption of this RFC is that, given that you need either-or, you might as well just support the special tuple trait so that you don't need to deal with recursion as much. (Though I'm personally become increasingly unsure if this actually makes the whole thing simpler).

Its also possible that ...T: Trait does not imply (...T): Trait. To pick an arbitrary example: ...T: Read. You just can't express a variadic bound under the T: Tuple proposal except indirectly through such a Tuple implementation, so there's no way to get something like "any number of types which implement Read".

This is a good point though - using tuples for carrying recursive trait impl informations is one thing if its about the tuple itself, but can cause conflicts if its just about carrying around element types without wanting to cause "implements trait" effects on the real tuple type.

Catching up on this now, i have a question/concern: if i have a tuple (A, B, C, ...) and want a tuple (F<A>, F<B>, F<C>, ...) for some F, how would i do so? It is clear if we use a sequence of types like C++ with expansion, but not as proposed here.
I ask, as i have a potential use case where F = Option, and the alternatives to variadic generics are either inefficient or very cumbersome.

@strake That's the purpose of TupleMap, assuming you have some F: Fn(T) -> Option<T> for all T in your tuple.

Its actually a fact that you can not implement the &(A, B, C) -> (&A, &B, &C) conversion without a extra language extension for it - be it either the special helper trait that would be defined by the compiler, or a extension of pattern matching (and I don't mean creating a trailing &(B, C)).

This means map() and similar won't help either.

I know that &(..) -> (&..) needs built in support. I want that support to be syntactic rather than through a psuedotrait.
Tuple as proposed in this RFC is extremely different from every other trait we have. In particular, its a closed set, which no other trait we have is. It also has methods which are actually just magical in every implementation - while some implementations call to intrinsics (like i32::add), these methods are actually just a method-like syntax for a built-in feature that can't be overloaded by users.
It seems much more appropriate for these conversions to be some sort of pattern extension just like ... than for them to be methods. I would suggest ref....
What I said was that without map you can't. I need to think more about the definition of map you posted.

@withoutboats: I discussed this a bit with @eddyb on IRC today and got some further clarification on the ref... vs magic trait background.
Basically, while its possible to support the nice pattern sugar as a front facing interface, for technical reasons it should be implemented as a magic trait in the background. The reason is that baking the logic in any more specific than that would be too complicated, as it would essentially just duplicate a lot of the existing code around associated types in weird more limited ways.
Said trait impl should also be magically defined so that the compiler can reason about the &(A, B, C) -> (&A, &B, &C) conversion in one atomic step, which is important if you want it to handle recursive impls that are based around both (A, ...B) and (A..., B) style recursion.

Setting aside the issue of whether you can define a function that can be passed to map (I agree with you that right now you can't), the definition of TupleMap you posted is not valid. In particular, this impl contains an unconstrained type parameter (R):
impl<Head, Tail, F, R> TupleMap<F> for (Head, ...Tail)
    where
    F: Fn(Head) -> R,
    Tail: TupleMap<F>,
{
   ...
}

FWIW I found this by attempting to validate the trait manually using this definition of tuples:
struct Unit;
struct Cons<A, T: Tuple>(A, T):

impl Tuple for Unit { }
impl<A, T: Tuple> Tuple for Cons<A, T> { }

It is also surprising in several ways:

F is a trait level param, when most map definitions have it as a method level param. This is because the recursive constraint (where Tail: TupleMap<F>) doesn't make sense for the Unit case.
The unit case is also totally unconstrained. You can do ().map("Hello, world") for example.
It relies on the construction of the tuple to define the domain of the function, rather than constraining the input parameter in some way. This works but is very odd (and may have pretty bad compiler performance because you have to dig through the tuple every time).

With the unboxed closure feature it can be implemented because it allows for the return type to be used as a projection, but you still can't call it in a tuple with more than 1 type in it.
#![feature(unboxed_closures)]

trait Tuple { }

struct Unit;
struct Cons<A, T: Tuple>(A, T);

impl Tuple for Unit { }

impl<A, T: Tuple> Tuple for Cons<A, T> { }

trait TupleMap<F>: Tuple {
    type Out: Tuple;
    fn map(self, f: F) -> Self::Out;
}

impl<F> TupleMap<F> for Unit {
    type Out = Unit;
    fn map(self, _: F) -> Unit { Unit }
}

impl<A, T, F> TupleMap<F> for Cons<A, T>
where
    T: TupleMap<F>,
    F: Fn<(A,)>,
{
    type Out = Cons<F::Output, T::Out>;
    fn map(self, f: F) -> Self::Out {
        Cons(f(self.0), self.1.map(f))
    }
}

@withoutboats: Sorry, I just copied it from the RFC text itself, which in turns referenced the working example of eddyb here: https://play.rust-lang.org/?gist=8fd29c83271f3e8744a3f618786ca1de&version=nightly&backtrace=0, so I had assumed it had been correctly transcribed without checking it further.

It turns out that I'm wrong. Presumably because its actually a projection (but quite surprising to me), we do accept an unconstrained impl parameter if its the return type of an Fn trait in the where clauses. !!

@withoutboats: About the surpising aspects of TupleMap: They don't seem surprising or problematic to me?
F is basically intended to be a associated-type-constructor using HKT-like type - eg for all T, it needs to hold that Fn::call::<T> is valid. You can constrain that further so that you only need the Ts that correspond to the tuple elements themself, which would mean that for no elements like in (), no constraint needs to be added.
Your "correct" version also seems strange to me, as for a (A, B, C) tuple it no longer only demands F: Fn(A) -> X, F: Fn(B) -> Y, F: Fn(C) -> Z, but also F: Fn(B, C) -> (Y, Z).

...T vs T: Tuple
One thing I've stressed I dislike about this proposal is the type-level splat operation & the tuple trait. What I dislike about this is the way I feel this is a leaky abstraction, revealing to the user that they're dealing with tuples in a way which, to me, creates a more confuddled mental model. I want to illustrate by comparing how Python and Ruby handle kwargs.
(If you already are familiar with both languages, skip this review.)
# Python

# **kwargs collects all the keyword arguments into a hash table.
def foo(**kwargs):
    print(kwargs['bar'])

# foo will receive an { 'bar' : 0 }, and print 0
foo(bar = 0)

# This will be a TypeError, because foo takes no positional arguments
foo({'bar' : 0 })

# (Note Python doesn't have a syntax for keyword arguments other than collecting to this hash
# which I think is sort of a mistake).

# Ruby

# This function takes a keyword argument called `bar`
def foo(bar:)
    print bar

# Will print 0
foo(bar: 0)

# However, this is just 'sugar' for passing hashes around. This also works:
foo( {bar: 0} )

# Also this works (just normal a positional argument)
def foo2(kwargs)
   print kwargs[:bar]

# Wrapped into a hash table passed as a last positional argument
foo2(bar: 0)

I strongly prefer Python's approach because of the way it wraps & splats for you. In Ruby the mechanics are exposed, so that you can manually perform half of the operation. But these operations are inherently magical, I want them to be as syntactic & unexposed as possible.
That's why I prefer a system in which the surface presentation is of a type which is (for all appearances) actually variadic, rather than of performing operations on tuple types. Allowing users to splice and dice tuples is more expressive, but I believe it is less declarative - you have to think about your operations in terms of cons cells instead of just "this is a tuple of any number of types."
I also strongly prefer the directness of ...T: Clone ("any number of types which implement Clone" over the indirectness of T: Tuple + Clone ("T is a tuple which implements clone; I can infer* that all of its types must implement clone") for the same reason; it seems much more declarative to me.
*Note we can only infer this because the orphan rules prevent you from implementing traits for tuples of your types (otherwise you could say impl Clone for (MyNonCloneType,). It works but its that sort of tenuous negative reasoning that has been so troubling to us in other cases.
The problem of (&A, &(B, C))
The conversation about tuple mappings and CloneRefTuple traits are not really related to the above issue - in both systems, we end up with the need for one of these things. Fundamentally this is because we've said we can't allow you to take a reference to a subtuple.
Here I have to admit my ignorance: is the representation issue here strictly about optimizations, or is it not possible, period to allow you to reference subtuples? Like if we disallowed reordering of tuples & did some pessimistic padding, could this work? If so, how disastrous would that be for performance?
Because the referencing of (A, B, C) to (&A, (&B, &C)) is the cause of a lot of additional complexity in how you implement traits for all tuples. If you could just decompose a tuple into two references, you could impl these traits in terms of a very straightforward recursive solution.
As it stands it seems like we're stuck in a really bad position. We have this trade off: performance, expressiveness, simplicity, choose two. And possibly performance is non-negotiable. I'm not convinced "simplicity" is the one to drop here. :-\

@Kimundi everything else aside, you really don't see a problem with being able to map any type over unit? Its not constrained to even be a function.

Assuming we do adopt this proposal, you don't have to use callback((a, b, c)). Rust is already able to do JavaScript-style invoking with tuples. It's just unstable.

@withoutboats:
Basically, creating subtuple references is a restriction on the memory layout of tuples, which opens a whole lot of worms of discussion about wether that would be ok or not, and has so already in past iterations of this RFC.
So, considering that its also not needed for the operations you actual want to do (getting reference access to each element of a tuple), it basically has more speaking against it than for it.
And yeah, map being entirely unconstrained for a empty tuple seems fine to - its how you always work with traits in languages like Rust: You don't say "I want a generic type only implements the traits I need", you say "I want a type that implements all the traits I need", with it implementing more being totally fine.
In the case of a empty tuple, the formulation of needing a Fn impl for each element type simply works out to no bounds being needed.

@notriddle: Thats just a hack though to make working with closures possible on stable rust. There explicitly is a unstable ABI for functions that means "I'm taking a tuple, but please translate me as if it where separate arguments". The purpose of a RFC like this would be to make that hack unneeded as you could define a variadic function directly.

Hmm, I'm trying to catch up. I find it hard to keep the full details of the various proposals in my head. But I will say that this part of @cramertj's summary raised a red flag for me (it is describing the approah that @withoutboats was arguing for):

This is identical except for the omission of the Tuple bound on Args. However, there's no tuple-based sugar here: FnOnce<A, B, C>` is just that-- variadic type lists are a first class concept.

My concern is that I am not sure how to think about "variadic type lists" in terms of standard type system concepts. In other words, that would mean that the "kind" (in Haskell terms) of FnOnce cannot be written down: whereas today it is (type -> trait), where the first type is always a tuple, in this version it would be "variadic". This seems like it may lead to complications in various places, and I'm not sure how it interacts with separate type-checking and the like.
That said, I do find ...T: Default to be a fairly natural thing to want to write. =)
I also feel like it's really important to explain how variadic functions will work; iirc this RFC tries to avoid it, deferring it for future work, but it seems like it's more-or-less the whole point, no?

We can write FnOnce: [type] → trait where []: ∀ a . a → a is a type-level list/array.

FWIW, we can't use that eencoding inside the compiler itself, not without reintroducing overhead I removed in https://github.com/rust-lang/rust/pull/36002 - besides, an impl can easily need more than one ...T parameter.
So the better approach at this point is to have Foo<T, U, ...V> be represented as Foo<T, U, (...V)> inside the compiler, with the user observing a variadic list of type parameters (when in fact, it's a tuple).
We could use a tuple for type parameters in general, but we'd still want the interleaving of lifetimes and types, so that tuple would have to be heterogenous kind-wise and always guaranteed to a tuple.

@Kimundi Are you sure we wouldn't run into quadratic behavior somewhere in the compiler?  This seems to be doing an O(n) operation O(n) times.

@DemiMarie: Sorry, which of my comments are you referring to?

I've been sort of waiting for the dust to settle a bit on this discussion before evaluating -- I wonder if someone would be willing to put together a summary comment explaining the current state of play?

@cramertj I'm not sure I have a clear understanding about how the TupleMap example would work exactly. More precisely I'm a bit confused about what would be a valid type for function f. It seems that for example in (5i32, 'h', true).map(f) function f should at the same time implement some traits Fn(i32) -> R1, Fn(char) -> R2 and Fn(bool) -> R3. As far as I know this is not the case for any functions/closures in Rust and I don't think a generic function (like fn to_some<T>(e: T) -> Option<T> { Some(e) }) would work either as there is no way to designate it as a generic object but only for particular specializations. What am I missing?

@contactomorph You can write those impls in unstable Rust already. Also see #1650.

@aturon Sorry for the delay. I'll try to summarize the key concerns people have listed:

There's concern that having Tuple be a trait, even a fundamental one, might not allow blanket impls to be as generous as we might like. Specifically, @sgrif was concerned that it might be impossible to write a blanket impl for both tuples and T: SomeOtherTrait. I'm fairly certain this isn't an issue once #[fundamental] is considered. See this playground example, where I implement a trait for both T: Fn() (a fundamental trait) and T: OtherTrait. This also works for T: Debug, showing that this also works for upstream traits. @eddyb and @nikomatsakis can probably shed some more light here.

This RFC's future plan to update the Fn traits to Fn<...Args> where Args: Tuple has some interesting interaction with the unsized rvalues RFC. The proposal to allow multiple !Sized function arguments mean that we'd need to make (str, str) a valid type, which it isn't currently.

@withoutboats would prefer to make type-lists a first-class concept independent of tuples. There's also concern that Tuple isn't properly represented by a trait, since ... isn't a mechanism exposed through the normal trait mechanics, and Tuple can't be implemented by a custom type.

Implementing a trait that borrows (rather than moves) a tuple is awkward. Partial tail borrows could potential make this nicer, but they are limiting from a usage and optimization perspective. I discuss this somewhat in the alternatives section of the RFC.

The proposed implementation of TupleMap allows mapping over () with anything (i.e. ().map("string mapper")) because it places no bounds on the argument. I personally think this is fine-- it matches my intuition: a function can map over a tuple if it knows how to map over all the members, so if the tuple has no members, it can be mapped over with anything.

If anyone has any concerns they feel I've omitted or misrepresented, speak up or ping me on IRC and I'll edit the comment.

@cramertj Your playground example implements something for Fn() directly, not T: Fn(). Those two things are very different.

@sgrif Good catch-- wow, still making that mistake a year later. @aturon how's that whole bare-trait thing coming? :laughing: And yeah, that does cause conflicts, so that issue is still on the table.

Regarding tuples and sized-ness, it's an interesting point. It seems plausible that we could say that a tuple is well-formed regardless of its component types, but that tuples which appear in certain contexts (e.g., as the target type of a reference) are limited to being "representable", so to speak. Have to think about that a bit though.

@nikomatsakis

tuples which appear in certain contexts (e.g., as the target type of a reference) are limited to being "representable"

@Kimundi proposed a similar "Representable" idea.
Personally, I'd prefer to make &(str, str) "just work". &(str, str) could be represented by a pointer along with two integers for the lengths of each str. In general, I think it would be useful to be able to place multiple unsized types into a single datastructure, especially if something like the unsized-Rvalues RFC lands.
The downside of this approach is that fat pointers would no longer have a guaranteed maximum size, which might break some libraries that assume that sizeof(&T) == sizeof((usize, usize)) where T: !Sized.

@cramertj There was never such a guarantee, other than by accident - breaking it eventually was always the plan - the problem is the "eventually".

@eddyb Yeah, I realize that it's technically undefined behavior, but people still do it. I definitely think it falls under "acceptable breakage."

@cramertj Do you think this has reached the point where it'd be useful to discuss in a lang team meeting?

@aturon A few of the points I listed above would benefit from some lang team discussion:

Discussing the possibility of an RFC for structures with multiple DST elements, e.g. (str, str).
Possibly extending the negative reasoning around the Tuple trait. #[fundamental] isn't enough to allow a blanket impl for Tuples and a blanket impl for another trait. Perhaps a #[sealed] annotation would be appropriate to guarantee to the compiler that no Tuple implementations will be added in the future. However, even with #[sealed], the problem isn't solved. For example, consider implementing a trait for T: Tuple and T: Display. This can't ever be coherent, no matter if Tuple is represented by a trait, a concrete type, or a "type list"-- there's nothing to tell the compiler that an impl of Display for some T: Tuple won't be added at a later date, causing conflicts. I suspect that implementing a trait for all tuples and for all T: SomeTrait will require some degree of specialization / lattice impls no matter which design we pursue in this RFC.

I'll also check in @withoutboats again to see if we can come to some consensus WRT their preference for first-class "type lists."

@cramertj I don't recall where the T: Tuple and T: Trait overlap comes from.
Are there any examples of usecases that involve it? I thought you'd have traits implemented for tuples for certain kinds of recursion, and traits implemented for tuples and other types (so no blanket impl would be acceptable anyway - I could be wrong though).

@eddyb @sgrif expressed interest in it-- I'm imagining they have some use case in mind.
I'd imagine the desire is related to implementing a function that takes either a single argument or a tuple of arguments, however if the future extensions to allow fn my_func(...args: ...Args) are accepted, then this could be represented as a function that takes one arguments or many arguments, and in the meantime single-argument calls to the function could use my_func((arg)).

It makes more sense IMO to go straight to variadic functions & methods than have multiple arguments manually packaged in tuples.

@eddyb sigh Yes, I've been gradually drifting towards preferring an "all at once" approach myself. My initial goal was to keep this RFC as minimal as possible in the hopes that it would be merged sooner. However, in order to know what the right "first steps" are, you have to consider how they move the language towards a comprehensive solution.

We're clearly not going to get to this before the impl period, so I propose we formally postpone it and start a fresh thread when we're ready to consider it.
@rfcbot fcp postpone

Alas, what symmetry that my first ever ticked checkbox as a lang team member should be to postpone my first ever RFC. :smile:
I'm excited to work more on this next year after we've had a chance to implement the host of wonderful RFCs currently in the queue.

I'm now closing this RFC as postponed. Thanks, @cramertj and everyone else for pushing further on this issue -- something we'll definitely want to come back to in the future!

Has there been any interest in reviving this lately?

@alexreg There's still general interest in the feature, but it's not the kind of change that's targeted for the roadmap this year, so there's probably nothing going on with it right now.  (Doing it and const generics could be an interesting thing to consider for next year's roadmap, once we're past the stabilize year.)

@scottmcm Fair enough. w.r.t. Const generics, I thought they were targeted for this year?

@alexreg const generics has an RFC merged but implementing it is no official priority of the compiler team. All of the statements were of the form "maybe we can have const generics in nightly by end of 2018"...


Type Definition issue : Cancelling navigation

Hi All,
I have implemented a PipelineStep in order to intercept navigation and cancel the navigation in certain situations.  It is working well, except I had to edit the typescript definition for aurelia-router.d.ts.
In the current version (1.0.0-beta.1.2.1) the definition for Next expects a value to be passed into the cancel method.  i.e.
export interface Next {
/**
  * Indicates the successful completion of the entire pipeline.
  */
complete(result: any): Promise<any>;

/**
  * Indicates that the pipeline should cancel processing.
  */
cancel(result: any): Promise<any>;

/**
  * Indicates that pipeline processing has failed and should be stopped.
  */
reject(result: any): Promise<any>;

/**
  * Indicates the successful completion of the pipeline step.
  */
(): Promise<any>;

}
In order for typescript to be happy I changed the Cancel method to
cancel(result?: any): Promise;
I'm not exactly sure how you are generating your typescript definitions, otherwise I would make a PR to fix this.
Cheers.

You can make a PR directly to the source code to add the typing info there.

Ok, I got around to trying to make a pull request.  But I couldn't for the life of me workout where in the api.json to indicate that a parameter was optional.  Any pointers?

No need to edit the api.json. That is generated from the code itself. You just need to indicate that the param is optional in the code using a ?, for example method(param?: string)

@EisenbergEffect does this look right?

Closing since the fix was merged :)


Feat: Custom global state for jumanji envs

What?
The jumanji MA wrapper has been given a get_global_state method to allow jumanji wrappers to specify their own global state. The GlobalStateWrapper has been removed and the generic global state based on concatenating agent observations has been moved to the generic jumanji MA wrapper. The connector wrapper, which implements its own global state, has also been modified accordingly.
Why?
To allow jumanji wrappers to implement a custom global state.

I've implemented all the suggested changes.


Absolutize paths after remapping

The index store library requires paths being written are absolute. If we
don't absolutize them then it does. Unfortunately its logic for that
computes the PWD for every single path, which can amount to ~5% of the
total time spent in index-import.
Signed-off-by: Keith Smiley<EMAIL_ADDRESS>

Turns out, we had tons of relative paths for header/source files (.h and .m) and was able to benefit performance from this change, yet again.
Before : ~50 seconds
After : ~30 seconds
Thanks @keith !


"Failed to execute 'transaction' on 'IDBDatabase': Database is Closing" exception on runtime cache

Hi there,
I´m having some issues with workbox on chrome for android.
Workbox Version: : 3.4.1
loaded from:   importScripts('https://storage.googleapis.com/workbox-cdn/releases/3.4.1/workbox-sw.js');
Browser and platform: Google Chrome 69.0.3497.100 running  on android 8.0
(the stacktrace below was copied from developer tools running USB debug).
Extra info on the behaviour:

The behaviour does not occour in a anonymous windows under the same conditions ( same browser / OS)

The behaviour does not occour on my desktop chrome ( same version as the mobile one).  69.0.3497.100 - 64 bits

when I run the website on a regular chrome tab, i do not see any databases under indexedDB.

-when i do the same on the anonymous window, i can see the databases.

Edit 1:

recently upgraded from v2 to v3 . it might be revelant . saw something similar in #1109

It seems likely that a previous version of the IndexedDB database exists with the same name (i.e. one created by Workbox v2 code), so when the v3 Workbox code is running, its onupgradeneeded callback is never invoked, and thus the new object stores aren't created.

Stack trace ( visible while in USB Debug)
workbox-core.prod.js:1 Uncaught (in promise) DOMException: Failed to execute 'transaction' on 'IDBDatabase': The database connection is closing.
    at https://storage.googleapis.com/workbox-cdn/releases/3.4.1/workbox-core.prod.js:1:4385
    at new Promise (<anonymous>)
    at https://storage.googleapis.com/workbox-cdn/releases/3.4.1/workbox-core.prod.js:1:4347
    at Generator.next (<anonymous>)
    at n (https://storage.googleapis.com/workbox-cdn/releases/3.4.1/workbox-core.prod.js:1:167)
    at https://storage.googleapis.com/workbox-cdn/releases/3.4.1/workbox-core.prod.js:1:261

Here is my sw file:
importScripts('https://storage.googleapis.com/workbox-cdn/releases/3.4.1/workbox-sw.js');

workbox.setConfig({
  debug: false
});

var maxAgeDay = 1 * 24 * 60 * 60; //01 days
var maxAgeMonth = maxAgeDay * 30; //30 Days
var maxAgeYear = maxAgeDay * 365;

//sharepoint useless static files
workbox.routing.registerRoute(
  /\/_layouts\/.*?\/.*?(\.js|gif|png|jpg|woff|css)/,
  workbox.strategies.cacheFirst({
    cacheName: 'sp-cache',
    plugins: [
      new workbox.expiration.Plugin({
        maxEntries: 60,
        maxAgeSeconds: maxAgeDay,
        purgeOnQuotaError: true,
      }),
    ],
  }),
);

workbox.routing.registerRoute(
  /\/(WebResource.axd|ScriptResource.axd)\?d=*/,
  workbox.strategies.cacheFirst({
    cacheName: 'sp-cache-dyn',
    plugins: [
      new workbox.expiration.Plugin({
        maxEntries: 60,
        maxAgeSeconds: maxAgeMonth,
        purgeOnQuotaError: true,
      }),
    ],
  }),
);

//banner files
workbox.routing.registerRoute(
  /^https:\/\/.*?.domain.com.br\/(.*?\/)*(Banners|PublishingImages)\/(.*?)/gi,
  workbox.strategies.staleWhileRevalidate({
  	cacheName:'img-banners-destaques',
  	plugins: [
      new workbox.cacheableResponse.Plugin({
        statuses: [0, 200]
      }),
      new workbox.expiration.Plugin({
        maxEntries: 90,
        maxAgeSeconds: maxAgeDay,
        purgeOnQuotaError: true,
      })
    ]
  })
);

workbox.routing.registerRoute(
  /\/Style(%20| )Library\/.*?\/.*?(\.css|js)/,
  workbox.strategies.staleWhileRevalidate({
  	cacheName:'js-css-style-lib',
  	plugins: [
      new workbox.cacheableResponse.Plugin({
        statuses: [0, 200]
      }),
      new workbox.expiration.Plugin({
        maxEntries: 90,
        maxAgeSeconds: maxAgeDay,
        purgeOnQuotaError: true,
      })
    ]
  })
);

//fonts
workbox.routing.registerRoute(
  /\/Style(%20| )Library\/.*?\/.*?(\.woff|woff2|ttf|eot|svg)/,
  workbox.strategies.cacheFirst({
    cacheName: 'web-fonts',
    plugins: [
      new workbox.expiration.Plugin({
        maxEntries: 60,
        maxAgeSeconds: maxAgeYear,
        purgeOnQuotaError: true,
      }),
    ],
  }),
);

//static-files-on subdomain
workbox.routing.registerRoute(
	/^https:\/\/static.domain.com.br\/web\//,
	workbox.strategies.staleWhileRevalidate({
		cacheName:'custom-static',
		plugins: [
			new workbox.cacheableResponse.Plugin({statuses: [0, 200]}),
          new workbox.expiration.Plugin({
            maxEntries: 90,
            maxAgeSeconds: maxAgeDay,
            purgeOnQuotaError: true,
          }) // 1 Days
		]
	})
);

self.addEventListener('message', (event) => {
  if (!event.data){
    return;
  }

  switch (event.data) {
    case 'skipWaiting':
      self.skipWaiting();
      break;
    default:
      // NOOP
      break;
  }
});

thank you in advance

Are you able to inspect the IndexedDB databases while debugging on your Android phone? If so, it could be helpful to see what's already there to better understand if an existing database or object store is causing the issue.
I'm skeptical that it is, though, because looking at the code, we now have upgrade logic for all instances of our IDB models, so they should be able to gracefully handle workbox v2 > v3 upgrade scenarios.
If you could narrow down what bit of code is causing the issue, that would also be helpful. Looking at your SW code, the only thing I see that uses IndexedDB is workbox.expiration.Plugin, if you remove that do you still see the error?
Also, it looks like workbox.expiration.Plugin creates an IndexedDB database with the same name as the cache name for the strategy including it. Is it possible that you have other code in your application that's created an IndexedDB database with the same name?
@jeffposnick maybe it's worth add a namespace to all database names we use in the future to minimize potential conflicts like this.

@philipwalton ,

Are you able to inspect the IndexedDB databases while debugging on your Android phone?

Yes. theres nothing there. no databases whatsoever ( see first print).

Removing the expiration plugin seems to work in this case.
upgrading to 3.6.2 fixed the issue on the device.

I suspect the issue is related to the bug described in #1640 .
Downgraded my QA server to 3.4.1 to collect diagnostics data . The same behaviour is present except now i get an exception with the message "workbox.core.registerQuotaErrorCallback is not a function".
Thank you for your help!


Specify TargetVersion with KafkaBalanced input type

In the same way that we can already specify TargetVersion with the Kafka input type, I'd like to be able to select a TargetVersion with KafkaBalanced. This is already supported by the sarama-cluster config (because it is based on the sarama Config struct) so we would just need to add (and then pass) this configuration option.

Fixed in fde9ff6.


[Bug] which-key module is not correctly loaded in keybindings.lua

If I add this line at the start of keybindings.lua:
local whk_status, whk = pcall(require, "which-key")
if not whk_status then
  print("Error loading which-key: " .. whk_status)
  return
end

then the following error is reported, after a LvimCacheReset command:
11:33:40 [WARN ] lvim: "Invalid configuration: ...nfigurations\\Neovim\\config\\lvim/lua/user/keybindings.lua:5: attempt to concatenate local 'whk_status' (a boolean value)" file="init.lua", line=112

If I restart Neovim again, everything gets loaded with no warnings (probably cache affected that).
How could the which-key module be correctly loaded (through the pcall) in keybindings.lua after a cache reset?

that error means you can't concatenate string and boolean which means yeah the which-key is not loaded yet
which makes sense because you are removing the entire cache.
to make sure that which-key is loaded use this function instead
lvim.builtin.which_key.on_config_done = function(whk)
  -- add your whk keybindings here
  -- whk.register(stuff)
end

that makes sure which_key is loaded

Ok, so that function should be inserted just once in the keybindings.lua file, and in no other files? For example, config.lua, or plugin-specific files. I'm asking because maybe multiple declarations of it could overwrite the other ones?
Also, in keybindings.lua, is there a place where you would suggest adding it?

let me make it a bit less confusing,
it doesn't matter where you put those, or the fact that it doesn't properly load after :LvimCacheReset
because we are basically reading the config, and creating a cache of it, so it might act weird when there is no cache available but it will work perfectly fine after the cache has been created, so don't worry about it 😅


Updates README

Description
This updates the Useful Commands section according to the latest changes.
Type

[X] Bug fix (non-breaking change which fixes an issue)

Codecov Report
Patch and project coverage have no change.

Comparison is base (4436167) 90.56% compared to head (48b6540) 90.56%.

:exclamation: Current head 48b6540 differs from pull request most recent head 7b6653e. Consider uploading reports for the commit 7b6653e to get more accurate results

:mega: This organization is not using Codecov’s GitHub App Integration. We recommend you install it so Codecov can continue to function properly for your repositories. Learn more
Additional details and impacted files
@@           Coverage Diff           @@
##             main     #127   +/-   ##
=======================================
  Coverage   90.56%   90.56%
=======================================
  Files          26       26
  Lines        3404     3404
  Branches      281      281
=======================================
  Hits         3083     3083
  Misses        321      321

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Do you have feedback about the report comment? Let us know in this issue.


Invalid timeElement schema

Hi all,
The timeElement.json which is the oneOf  of

age.json
ageRange.json
gestationalAge.json
commonDefinitions.json#/definitions/Timestamp
timeInterval.json
ontologyTerm.json

Will always fail to validate because of very permissive "age" and "ageRange" that have no "required" properties defined.
Cheers,
Dmitry

Thanks for #113 and looking forward to the merge; this exact bug is biting me today
jsonschema.exceptions.ValidationError: {'age': {'iso8601duration': 'P30Y'}} is valid under each of {'$ref': './ageRange.json'}, {'$ref': './age.json'}

...

Resolved. Merged.


James

Dear @killix
Here is my solution.
Please take a look
Waiting for your reply
Thanks

thanks, we will review it soon!


Missing section header and link

https://www.apache.org/foundation/marks/templates/process.html#trademark-issue-tracking-tool-data-fields
is not present in staged version

Likewise for
https://www.apache.org/legal/src-headers.html#is-a-short-form-of-the-source-header-available

https://www.staged.apache.org/foundation/marks/templates/process.html#trademark-issue-tracking-tool-data-fields
and
https://www.staged.apache.org/legal/src-headers.html#is-a-short-form-of-the-source-header-available
now exist. thanks


backend: Rework newBackend() error handling

The newBackend() function may enumerate multiple backends to find the best supported backend for the system. Because of this, the function may return multiple errors.
There are two problems with how this function returns multiple errors:

if only one error occurs, the error is appended to a nil error
they are wrapped with %v instead of %w

Correct these issues.

Handling in https://github.com/go-debos/fakemachine/pull/160

Handling in #160

Not sure what you mean with that; since #160 is this PR?


Format error pages as JSON

In particular, 404 returns HTML which makes the frontend choke.

Actually, the 404 came from the frontend. We should already be returning JSON for all HTTP errors right?


sap.ui.table.Column headerSpan sets all following column headers to display:none

OpenUI5 version: 1.32
Browser/version (+device/version): FF, Chrome, IE11
What is the expected result?
My first column header of the table should span over 4 columns.
What happens instead?
As soon as headerSpan="4" (surprisingly also 1) is defined all of the following headers are set to display:none via the element style. Therefore only the correctly spanning header is visible.
Steps to reproduce the problem:
<c:FragmentDefinition
	xmlns="sap.ui.table"
	xmlns:mvc="sap.ui.core.mvc"
	xmlns:u="sap.ui.unified"
	xmlns:c="sap.ui.core"
	xmlns:m="sap.m">
	<Table
		rows="{/entries}"
		enableSelectAll="false"
		selectionMode="None"
		visibleRowCountMode="Auto">
		<columns>
			<Column width="1.2rem" headerSpan="4">
				<m:Label text="{i18n>entrieslist.column.name}" />
				<template>
					<m:Button class="centered-table-btns" icon="sap-icon://crm-service-manager" tooltip="View" press="showDetailsDialog" />
				</template>
			</Column>
			<Column  width="1.2rem">
				<template>
					<m:Button class="centered-table-btns" icon="sap-icon://delete" tooltip="Delete" press="deleteEntry" />
				</template>
			</Column>
			<Column width="5rem" >
				<template>
					<m:Link text="{lastname}" press="showDetailsPage" wrapping="false"/>
				</template>
			</Column>
			<Column width="5rem">
				<template>
					<m:text text="{firstname}" wrapping="false"/>
				</template>
			</Column>
			<Column width="11rem" sorted="true" sortProperty="{telephone}" >
				<m:Label text="{i18n>entrieslist.column.number}" />
				<template>
					<m:text text="{telephone}" wrapping="false"/>
				</template>
			</Column>

Hello mkueng1,
thank you for reporting this issue. I converted your code fragment to a working jsbin example.
http://jsbin.com/wodasedapo/edit?html,css,output
I could reproduce the issue but i need a table expert to look into this.
I created an internal incident to inform the colleagues:<PHONE_NUMBER>
They will get back to you here as soon as they have analyzed the issue into more detail.
Thank you & Kind Regards,
Michael

Hello mkueng1,
this issue has been solved and the fix is available in master (1.37-SNAPSHOT), 1.36.2 and 1.34.6.
Thanks and regards,
Wenqian


Error with yum and amazon mirror when building pshtt function

When running docker-compose up build_pshtt, I got this error:
 One of the configured repositories failed (Unknown),
 and yum doesn't have enough cached data to continue. At this point the only
 safe thing yum can do is fail. There are a few ways to work "fix" this:

     1. Contact the upstream for the repository and get them to fix the problem.

     2. Reconfigure the baseurl/etc. for the repository, to point to a working
        upstream. This is most often useful if you are using a newer
        distribution release than is supported by the repository (and the
        packages for the previous distribution release still work).

     3. Disable the repository, so yum won't use it by default. Yum will then
        just ignore the repository until you permanently enable it again or use
        --enablerepo for temporary usage:

            yum-config-manager --disable <repoid>

     4. Configure the failing repository to be skipped, if it is unavailable.
        Note that yum will try to contact the repo. when it runs most commands,
        so will have to try and fail each time (and thus. yum will be be much
        slower). If it is a very temporary problem though, this is often a nice
        compromise:

            yum-config-manager --save --setopt=<repoid>.skip_if_unavailable=true

Cannot find a valid baseurl for repo: amzn-main/2017.03
Could not retrieve mirrorlist http://repo.us-west-2.amazonaws.com/2017.03/main/mirror.list error was
12: Timeout on http://repo.us-west-2.amazonaws.com/2017.03/main/mirror.list: (28, 'Resolving timed out after 5540 milliseconds')
ERROR: Service 'build_pshtt' failed to build: The command '/bin/sh -c yum -q -y install wget' returned a non-zero code: 1

FWIW, I can get to the mirror.list file just fine:
$ curl http://repo.us-west-2.amazonaws.com/2017.03/main/mirror.list
http://packages.us-west-2.amazonaws.com/2017.03/main/201703c0ffee/$basearch
http://packages.us-west-1.amazonaws.com/2017.03/main/201703c0ffee/$basearch
http://packages.us-east-1.amazonaws.com/2017.03/main/201703c0ffee/$basearch
http://packages.ap-southeast-1.amazonaws.com/2017.03/main/201703c0ffee/$basearch
http://packages.ap-northeast-1.amazonaws.com/2017.03/main/201703c0ffee/$basearch
http://packages.ap-northeast-2.amazonaws.com/2017.03/main/201703c0ffee/$basearch
http://packages.eu-west-1.amazonaws.com/2017.03/main/201703c0ffee/$basearch
http://packages.eu-central-1.amazonaws.com/2017.03/main/201703c0ffee/$basearch
http://packages.sa-east-1.amazonaws.com/2017.03/main/201703c0ffee/$basearch
http://packages.ap-southeast-2.amazonaws.com/2017.03/main/201703c0ffee/$basearch

@konklone, I'm not seeing the error you are.  I just pulled down the latest lambci/lambda:build-python3.6 too in case that was the problem.  I'm seeing:
$ docker-compose build --no-cache build_pshtt
Building build_pshtt
Step 1/5 : FROM lambci/lambda:build-python3.6
 ---> 87a2e015c246
Step 2/5 : MAINTAINER Shane Frasier<EMAIL_ADDRESS> ---> Running in 720ab22f6958
Removing intermediate container 720ab22f6958
 ---> 6c4e5d04ce08
Step 3/5 : RUN yum -q -y install wget
 ---> Running in ae388cc255b4
http://packages.us-west-1.amazonaws.com/2017.03/main/201703c0ffee/x86_64/repodata/primary.sqlite.bz2?instance_id=fail&region=timeout: [Errno 12] Timeout on http://packages.us-west-1.amazonaws.com/2017.03/main/201703c0ffee/x86_64/repodata/primary.sqlite.bz2?instance_id=fail&region=timeout: (28, 'Operation too slow. Less than 1000 bytes/sec transferred the last 5 seconds')
Trying other mirror.
Removing intermediate container ae388cc255b4
 ---> 392c7384bac6
Step 4/5 : COPY build_pshtt.sh .
 ---> 10a06df0b68d
Step 5/5 : ENTRYPOINT ["./build_pshtt.sh"]
 ---> Running in 42114a8c8b7a
Removing intermediate container 42114a8c8b7a
 ---> 531f54f38de2
Successfully built 531f54f38de2
Successfully tagged dhsncats/build_pshtt:latest

It almost looks like your Docker container can't get a response from DNS.

Hmm, I was able to run it successfully in a different environment. I'll chalk it up to something weird about the first environment and I'll re-open if I can find out any distinguishing details.

Glad it's working now!