Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFC for allowing eliding more type parameters. #1196

Closed
wants to merge 4 commits into from

Conversation

huonw
Copy link
Member

@huonw huonw commented Jul 9, 2015

since it becomes easier to type-hint things. E.g. the `random`
example can becomes `random(..): u8`.
- Require noting that parameters have been left to inference,
e.g. `random::<u8, ...>`. This defeats much of the syntactic point,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this alternative is my personal preference (though perhaps with two periods rather than three).

I see why @huonw prefers his proposal, but on the flip side: How do I now specify "these are the exact type parameters, and if another gets added, I want to be told at compile time" ? (Think of it like not having a _ => ... match arm...)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(of course one might reasonably point out that we already do not have a way to express "these are the exact type parameters" when dealing with a struct/enum with default type params" ...)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you have any cases in mind where it's important to know that one is specifying all of the parameters? It seems to me that most cases where adding new parameters requires external adjustment will fail to compile, but I could very very easily be wrong.

@nrc
Copy link
Member

nrc commented Jul 9, 2015

I assume you can only elide the last n type parameters this way? Would this interact with default type parameters?

Like Felix, I think I would also prefer the dots - some evidence of the elision seems worthwhile. Generally we want this - we require it in patterns for example, so I think it is consistent. The only places we allow elision without some syntactic indication (I think) is where we elide everything - e.g., lifetime elision (one of one thing) or type inference (all type parameters).

@nrc nrc added the T-lang Relevant to the language team, which will review and decide on the RFC. label Jul 9, 2015
@nrc nrc assigned huonw Jul 9, 2015
@huonw
Copy link
Member Author

huonw commented Jul 9, 2015

I assume you can only elide the last n type parameters this way?

Yeah, only trailing parameters.

Would this interact with default type parameters?

I assumed that omitting a default and writing _ for it are the same thing, so this wouldn't interact in any non-trivial way. I could be wrong, though, which would definitely make this RFC less nice.

@mdinger
Copy link
Contributor

mdinger commented Jul 10, 2015

What would happen if only one <_> was used? Would it be dropped? Like this:

// before: collect::<Vec<_>>()
[1, 2, 3].iter()
         .map(|&x| x)
         .collect::<Vec<_>>();
// after: collect::<Vec>()
[1, 2, 3].iter()
         .map(|&x| x)
         .collect::<Vec>();

@huonw
Copy link
Member Author

huonw commented Jul 10, 2015

The proposed elision only applies to top-level parameters in expressions, not types, so no that doesn't work.

@reem
Copy link

reem commented Jul 10, 2015

+1 to this proposal, I've wanted this many times.

@petrochenkov
Copy link
Contributor

Does it have any undesirable interactions with potential variadic generics?
It seems like it doesn't. With variadics N could be deduced first, then N - M _s could be inserted.

It is interesting, that full omission of generic parameters in brackets is currently allowed:

fn f<T, U>(_: T, _: U) {}
fn g(_: u8, _: u16) {}

fn main() {
    f::<>(1u8, 1u16); // Equvalent to f::<_, _>(1u8, 1u16);
    g::<>(1u8, 1u16); // Bug?
}

It looks like the alternative with <T, ..> would affect exactly one line of code in the whole Rust codebase:
read_seq_3::<char, Ipv6Addr, char, _, _, _>(open_br, ip_addr, clos_br)
=>
read_seq_3::<char, Ipv6Addr, char, ..>(open_br, ip_addr, clos_br)
, so it's not a very useful alternative.

@huonw
Copy link
Member Author

huonw commented Jul 13, 2015

I agree that it shouldn't be problematic with VG.

g::<>(1u8, 1u16); // Bug?

(FWIW, I don't think we should treat this as a bug, I like how Rust is quite flexible with "zero or more" and "allow trailing" rules, which help with macros and code-generation.)

@eddyb
Copy link
Member

eddyb commented Jul 13, 2015

As another alternative, maybe use defaults to opt into this feature, e.g. fn foo<T, U = _>(...) {...}.
I would love this for lifetimes, coupled with #1177.

@pnkfelix
Copy link
Member

@nrc

The only places we allow elision without some syntactic indication (I think) is where we elide everything - e.g., lifetime elision (one of one thing) or type inference (all type parameters).

Wait, in which of the above two categories are you placing default type parameters?

  • Or are you solely talking about stable Rust?
  • Or by "elision", are you only talking about elision that is backed up by region + type inference, and not the injection of type parameter defaults (which presumably only requires looking up the type definition in the environment, and does not require finding a solution to the unification problem implied by the type constraints)?

Attempted illustration of what I'm trying to ask about: http://is.gd/gT0ALq

(At this point I am leaning towards a position that this RFC is consistent with our attitude towards default type parameters, for better or for worse. However, one might make the reasonable argument that up until now, elided things followed pretty simple rules to determine what was being elided -- I also wrote "syntactic rules" but I'm not 100% sure we can call the resolution of the type parameter defaults 100% syntactic...)

@pnkfelix
Copy link
Member

@eddyb Just to clarify: Am I right that the semantics of your proposed alternative is:

fn all_req<X, Y>(x: X, y: Y) { ... }
fn has_default<X, Y = i32>(x: X, y: Y) { ... }
fn can_omit<X, Y = _>(x: X, y: Y) { ... }

all_req::<char, _>(c, c);
all_req::<char>(c, i); // error: type parameter missing from instantiation
has_default::<char>(c, i); // okay
has_default::<char>(c, c); // error: `c` is not `i32`
can_omit::<char>(c, i); // okay
can_omit::<char>(c, c); // okay

@eddyb
Copy link
Member

eddyb commented Jul 14, 2015

@pnkfelix That is exactly what I meant, yes. I originally thought of the lifetime counterpart like this, back when 'tcx was first introduced:

struct SomeContext<'tcx, 'a = '_> {
    tcx: &'a TypeContext<'tcx>,
    ...
}
fn compute_type<'tcx>(cx: SomeContext<'tcx>) -> Ty<'tcx> {...}

The 'a parameter does not need to be specified outside of constructors and a getter methods (e.g. for the tcx field).
There was also a reason I wanted to use 'a = '? instead, possibly to avoid conflict with '_, if the latter was used for a different feature.

Which reminds me, there's a subtle issue with type holes: they only exist at the AST level.

fn infer_vec<X, Y = Vec<_>>(x: X, y: Y) { ... }

The definition above would result in the internal type representation TyStruct(std::vec::Vec, [TyInfer(TyVar(0))]) for the default type.
That type is also written like Vec<_#0t> or Vec<$0>, where _#Xt and $X are used to indicate "Xth type inference variable in this inference context (usually a function)".
If Vec<$0> is used in a function directly, its element type will alias with the first inference variable created in that function, instead of creating a new one.
At this moment we do not have a placeholder type to use instead, though I am wondering if TyInfer(FreshTy(..)) could be used as such, and replaced in defaults when they are instantiated.

Although, even that is suboptimal:

type DoubleMe<T> = (T, T);
fn double_trouble<A = DoubleMe<_>>(...) {...}

The above use of a type alias should not expand to just any (_, _) pair, but rather ($X, $X), where the two types have to be the same.
Actually, that leads to a simple way to instantiate a default: add the ID of the next inference variable to all the inference IDs inside the default type and mark all IDs, up to the largest one used, as allocated.
The relationships between inference variables in the default should be preserved and those inference variables would not interfere with inference variables of the function the default is instantiated in.

@chris-morgan
Copy link
Member

Concerning this example:

fn random<T: Rand, D: IntoRand<T>>(d: D) -> T

If we had the impl Foo anonymous generics syntax, that could be written like this:

fn random<T: Rand>(d: impl IntoRand<T>) -> T

This would achieve the same purpose.

I suspect that a great many of the cases that cause type parameter omission to be desirable would also be satisfied by that proposal. Just something to consider.

@nikomatsakis
Copy link
Contributor

@eddyb

The definition above would result in the internal type representation TyStruct(std::vec::Vec, [TyInfer(TyVar(0))]) for the default type.

I do not believe we currently allow _ to be used in this position? This problem you're describing doesn't fit my mental model, since we don't allow inference variables to be instantiated in function signatures. (That said, I would be in favor of supporting _ in fn signatures as shorthand for "introduce an anonymous type parameter", but that's a separate matter.)

@nikomatsakis
Copy link
Contributor

I agree with @nrc and @pnkfelix that using .. is more consistent with patterns, but I also agree with @huonw that it seriously degrades the value of the proposal. There is not enough usability win to design APIs around it, and it won't serve (in practice) to mitigate breakage from adding a new type parameter to a fn. @eddyb's middle-ground is potentially interesting, but I'm not sure.

@eddyb
Copy link
Member

eddyb commented Jul 16, 2015

@nikomatsakis I was describing an issue that would arise if we just allowed _ in defaults, as they have to be converted from AST form to the type-system before they're instantiated, so the type-system has to express those type holes somehow.

My worries were unfounded, as type inference variables can still be used (with an extra instantiation step), but I wanted to document the case anyway.

@blaenk
Copy link
Contributor

blaenk commented Jul 16, 2015

Yeah I've run into situations where it was necessary for the user to explicitly state one type parameter, but the rest were 'inconsequential' to the user, for example, a closure trait bound, so I did away with the idea entirely instead of requiring the user to do foo::<Sometype, _, _>(blah).

This would be nice.

@eddyb
Copy link
Member

eddyb commented Jul 16, 2015

@blaenk I keep seeing these examples and wondering whether a trait method would've worked better.

@blaenk
Copy link
Contributor

blaenk commented Jul 16, 2015

Yeah unfortunately I don't remember exactly what I was doing that I ran into that. But wouldn't that require me to do fn foo<T, U>(blah) where U: TheTrait? So I would still have to do foo::<i8, _>(thething)? Or what do you mean?

@eddyb
Copy link
Member

eddyb commented Jul 17, 2015

@blaenk No, I mean, splitting the type parameters into the type implementer aka Self and type parameters on the method, so you do T::method(...) or Trait::method::<T>(...).

@huonw
Copy link
Member Author

huonw commented Jul 27, 2015

@chris-morgan

If we had the impl Foo anonymous generics syntax, that could be written like this:

This would achieve the same purpose.

This isn't obvious to me. That is, it's not obvious to me what the impl syntax means in argument position. If it exists at all, it seems like it would make most sense to desugar to a proper generic, i.e. specifiable with ::<...>.

@eddyb

I'm not sure I understand the overall point of #1196 (comment). Am I correct that is basically "just" rustc implementation details? I.e. completely unrelated from semantics.

@eddyb
Copy link
Member

eddyb commented Jul 27, 2015

@huonw Well, _ doesn't have any semantics in those locations atm (it's a compiler error), and the proposed semantics don't correspond to anything pre-existing, but the rest of the comment is indeed just me not seeing the forest for the trees, I'm afraid.

@nikomatsakis
Copy link
Contributor

In the language subteam meeting, we explored an interesting question about the interaction of this idea with defaulted type parameters. In particular, in types at least, if type parameters with defaults are omitted, this is equivalent to having typed the default value explicitly (versus creating a variable with fallback). For example, if I write this:

struct Foo<T,U=T>(T,U);
...
let x: Foo<i32> = Foo(22, 'c');

I will get a compilation error, because Foo<i32> is equivalent to Foo<i32,i32>, which means that 'c' is of the wrong type. If instead we had made Foo<i32> equivalent to Foo<i32,_> (as is proposed by this RFC, for functions) then this example would compile.

This RFC is specific to fn/method references, so in a way there is no conflict here, but the discrepancy is somewhat unnerving. It feels like type parameters lists should behave the same with respect to defaults, regardless of what they are attached to.

It's not entirely clear to me which behavior is better, for that matter, when it comes to types at least. The current behavior means that (e.g.) HashMap<K,V> always refers to the default hasher. If we inserted an inference variable for defaulted type parameters, then when you write let x: HashMap<K,V> you would actually be leaving the hasher unspecified, which means that to understand what type x has it is not enough to read what is written, one must consider the full context of the surrounding fn.

@huonw pointed out that the alternative of requiring an explicit T=_ notation would address this discrepancy. However, that also somewhat limits the usefulness of the proposal, since a handy pattern might be to change functions like:

fn foo<T>(..., x: u64) { ... }

to

fn foo<T,U=u64>(..., x: U) { ... }

The default here helps to ensure that an integer literal like 22 gets type u64 and not i32.

Thoughts?

Hat tip @jroesch, who brought this interaction to my attention

@alexcrichton
Copy link
Member

@nikomatsakis currently in the RFC it states that this sort of "mechanical fill in with _" only applies to expressions, which makes it sound like the example you gave would have Foo<i32> desugar to Foo<i32, i32> (as this RFC wouldn't apply), causing a compile error with the 'c'.

Perhaps though the same confusion can arise just from using expressions?

@strega-nil
Copy link

I just wanted to say 👎

This feels like bringing implicitness to a place which should be explicit. I like Rust because it forces you to be explicit about what you want in most cases. If you want type inference, you must explicitly request type inference.

edit: nevermind.

@bstrie
Copy link
Contributor

bstrie commented Dec 1, 2015

-1 to <T, ..> or any syntax akin to it. Though I could be persuaded if I saw some real code in the wild that would benefit greatly from this.

-1 to this RFC in general, at least for the moment. As mentioned in the proposal, we do expect general type ascription to land someday, which seems more composable than what this RFC describes while also not introducing more special cases between expression position and type position like this proposal does. At best, I'd say to postpone this RFC until after type ascription lands.

@huonw
Copy link
Member Author

huonw commented Dec 2, 2015

why would this be only for function calls in expressions? It makes the semantics of type parameter lists different in some places than others, which could be quite surprising, and confusing.

I think the potential confusion and surprisingness of allowing it in other places is even worse: it feels natural for functions, but not so much for types, e.g. let x: Vec = foo.collect(); and let a: Result = foo(); seem quite weird to me.

@tikue
Copy link
Contributor

tikue commented Dec 2, 2015

I think the potential confusion and surprisingness of allowing it in other places is even worse: it feels natural for functions, but not so much for types, e.g. let x: Vec = foo.collect(); and let a: Result = foo(); seem quite weird to me.

I think that's because you're eliding the brackets in addition to the types themselves. On the other hand, let a: Result<..> = foo(); feels perfectly natural to me.

It could also be extended to allow let manyTypedThing: ManyTypedThing<first, .., last> = foo();

@comex
Copy link

comex commented Dec 2, 2015

What about let a: Result<> = foo();, C++ style?

@pnkfelix
Copy link
Member

pnkfelix commented Dec 2, 2015

I'd say to postpone this RFC until after type ascription lands.

This does make some amount of sense...

@aturon
Copy link
Member

aturon commented Dec 2, 2015

I'd say to postpone this RFC until after type ascription lands.

This does make some amount of sense...

I'm very confused. To me, the core motivation of this RFC is to make it so that you can add new type parameters to a function without breaking existing code. Of the various alternatives that have been floated, none seem to tackle this essential goal. Type ascription seems orthogonal.

@pnkfelix
Copy link
Member

pnkfelix commented Dec 2, 2015

@aturon

Of the various alternatives that have been floated, none seem to tackle this essential goal.

I think eddyb's did; it just adds the onus that when adding such parameters, one must declare them via either T=TypeExpr or T=_.

Update: of course further discussion up above points out places where the resulting semantics may be a bit weird when comparing behavior in type constructions versus expressions (as noted #1196 (comment) )

Type ascription seems orthogonal.

Hmm, okay that may be true; I may have not thought fully through what @bstrie was saying

@huonw
Copy link
Member Author

huonw commented Dec 2, 2015

I think that's because you're eliding the brackets in addition to the types themselves. On the other hand, let a: Result<..> = foo(); feels perfectly natural to me.

What about let a: Result<> = foo();, C++ style?

Maybe, but we don't write foo::<..>() or foo::<>() now, we just write foo() i.e. the delimiters have to be elidable if we truly wanted all type parameter lists to be uniform.

@brendanzab
Copy link
Member

To me, the core motivation of this RFC is to make it so that you can add new type parameters to a function without breaking existing code.

Ohh... this really wasn't obvious from the way the RFC was worded, at least to me. I'm still hesitant though. Isn't semver meant to solve these problems? Or is this a problem for the standard libraries, which have less flexibility to bump a major version for these kinds of changes?

@eddyb
Copy link
Member

eddyb commented Dec 2, 2015

@huonw I am against a plain Type in a type context having any "hidden" type parameters, unless it's opt-in (all its type parameters being declared with a default of _ was my suggestion, as @pnkfelix mentioned).

But that's not enough if the type was used outside of a function, in which case, there is no way to handle the backwards compat issue, except for the cases where is actually a sensible default to use (in which case, defaulting to inference is not what you get inside function bodies).

@huonw
Copy link
Member Author

huonw commented Dec 3, 2015

I've now added a commit that adds API evolution as part of the motivation, since this RFC plays into that well, as @aturon has been pointing out.

@nikomatsakis
Copy link
Contributor

We discussed this a fair amount in the @rust-lang/lang meeting yesterday. The general consensus was that the proper interaction between this and fallback was unclear, but that was partly because the proper behavior of fallback itself is unclear. We didn't reach a firm decision, but as of the end of the meeting we were leaning towards:

  1. Merging this RFC, as it is well-motivated.
  2. Reconsidering whether to ever support fallback or indeed defaults anywhere but types (but see the notes about impl bugs below).

Let me start with the reasons in favor of this RFC, and I'll discuss point 2 afterwards. I'm repeating points that have already been raised in the discussion thread here, but it's helpful to have it summarized. Basically there are two motivations:

  • Usability: Right now, some APIs (like transmute) include some type parameters that the user is expected to specify, along with others that can typically be derived from the types of input parameters. This RFC would allow those APIs to be used more ergonomically, so long as the author orders the type parameters correctly.
  • Evolution: The API evolution story is a bit tricky, since almost any change can cause regressions in the margins. However, there is at least one case where this scheme would be very helpful, which is when adding new defaulted type parameters to an existing type. Consider adding an allocator A to a type like Vec -- if we now have an option Vec<T, A> parameter, where A defaults to DefaultAllocator, we'd also like to modify functions that take vectors as input. So if there is some function fn foo<T>(v: Vec<T>) we probably want to change it to fn foo<T,A>(v: Vec<T,A>). In this case, the A is almost certain to be inferred from the input type, but any code that explicitly specified T would be in trouble.
    • there is another case, which is converting from fn foo(..., param: SomeType) to fn foo<T>(..., param: T), but this is much sketchier, in that this can affect inference and coercions in many ways, and it's hard to estimate the impact of such a change in practice.

Now, on the topic of the interactions with fallback for inference, there is growing concern from many of us that fallback is a bad idea. Experiments with implementing it revealed that there are a number of nontrivial decisions to make, and there are also strong composability concerns (basically, it's very easy to wind up with competing defaults, and there is no good way to resolve them). Defaults will also have trouble permitting forward evolution due to those same composability concerns as as well as the interaction with coercion: changing something from a concrete type to a default is potentially quite a big difference in terms of its interaction with other inference variables.

Unfortunately, it seems that the compiler now accepts defaults on fns and, moreover, they act in a kind of inconsistent way. That is, if you write fn foo<A,B=u64>, and you call it with no parameters (foo(22_i32, 22_i32)), then both A and B are inferred using type variables. But if you supply any values, then the defaults are fixed, just as with a type specification (e.g., foo::<i32>(22_i32, 22_i32) yields an error). This seems to be somewhat surprising to me in a number of ways. I think though that we ought to simply make defaults on anything but types an error (presumably with a deprecation period), since the semantics do not match the accepted RFC and were unintentional.

Some questions that arose in my mind as I typed this comment:

  • Another use case for defaults on type parameters is to make things like Vec::new or HashMap::new work more smoothly with defaulted parameters like allocators or custom hashers. But then, both of those examples basically require another constructor anyway, as you ought to (generally) supply the allocator value -- and perhaps users can just be taught to use Vec::default or HashMap::default when selecting an alternate allocator/hasher which does not, in fact, have state.
  • Do we need defaults on impls in order to make defaults on types work out? I don't see why we would off-hand.

@brendanzab
Copy link
Member

Thanks for taking the time to write that up, it's most appreciated!

@bstrie
Copy link
Contributor

bstrie commented Dec 7, 2015

I as well didn't realize that the impetus was to allow API evolution without breaking changes, so I retract my objection. Still have a tickle in my mind like this is going to constrain us down the road, though.

+1 to deprecating defaults on fns asap.

@briansmith
Copy link

I'm very confused. To me, the core motivation of this RFC is to make it so that you can add new type parameters to a function without breaking existing code. Of the various alternatives that have been floated, none seem to tackle this essential goal. Type ascription seems orthogonal.

How would you add a new type parameter to a function and guarantee that the calling code will break? Being able to do this is one of the key features of using a statically-typed language, especially one that is designed to use the static typing for safety-oriented programming. The proposal would be better if it were changed so that defaulting to the inferred type is only done when specifically requested, e.g.

/// This would not allow U and V to be omitted.
fn foo<T, U, V>() -> T {
    // ...
}

// U and V can be omitted.
fn foo2<T, U=_, V=_>() -> T {
    // ...
}

// Only V can be omitted.
fn foo3<T, U, V=_>() -> T {
    // ...
}

@jnicklas
Copy link

jnicklas commented Dec 8, 2015

Speaking as a Rust beginner: Restricting this to expressions only seems very arbitrary to me. The distinction is quite subtle. I think this would be quite difficult to explain in documentation. I know Rust is not a language designed for beginners but even relatively proficient programmers might not be aware of the distinction between statements and expressions. Unless they've had cause to work on a compiler there isn't really any good reason to know this anyway. As such these rules seem pretty arbitrary.

Is there any good reason to allow this in one place and not in any other? I don't feel like the RFC lays out why this restriction is in place.

Also it's unclear from the RFC if it should be legal to elide all type parameters in a case such as collect::<Vec<>>(). Or if it should be legal to write collect::<Vec>() which doesn't seem unreasonable to me. In fact it would remove Rust's IMO biggest eyesore, so 👍 to that.

If it is restricted to eliding all type parameters except the first, then that's even more arbitrary, and even harder to explain.

It feels to me like this RFC is kind of a half-way solution. Either go for full-on elision or don't do it at all.

@eddyb
Copy link
Member

eddyb commented Dec 8, 2015

@jnicklas The difference is not between statements and expressions, but between paths with explicit type parameters, in both expressions and types.
There is already a difference: you can write Vec::new() as an expression, but inside a type, you must provide the type parameter, e.g. let v: Vec<u8>;.

This is in part affected by/possible due the fact that inside functions there is type inference (and non-const expressions can only be found in functions).
So we could do the same thing for types which are part of expressions or let declarations inside a function and allow let v: Vec;.

But it would be very surprising to me, I would like to see let v: Vec<_>;.
I wouldn't mind having it be opt-in so let v: Vec<_>; may infer a secondary allocator parameter without forcing the default (and I proposed T=_ as a way of having this behavior without an actual default to fall back to if not inferred).

I guess I'm mostly worried about littering code with explicit types that give the wrong impression about their actual number of type parameters.
Also, with HKT, Vec is a legitimate (higher-kinded) type, so then there is more confusion to be had.

@erickt
Copy link

erickt commented Dec 14, 2015

For general consistency with the pattern matching syntax, I do like using .. for this instead of having _ match multiple arguments.

@rkjnsn
Copy link
Contributor

rkjnsn commented Dec 30, 2015

I agree that this should be opt in, either using .. at the usage site or T=_ at the definition. Actually, why not both? Allow .. to explicitly omit type arguments, and T=_ to enable type parameters to be added in a backward-compatible fashion.

@colin-kiegel
Copy link

As a compromise, you could allow ellided type parameters in two ways - say random<T,V> has two type parameters:

  1. implicit ellision like random::<u8> would compile, but it would print a warning about implicit ellision and suggest to supply all types or use the explicit elision (or possibly type ascription in the future)
  2. explicit ellision like random::<u8, ...> would just work without complaints from the compiler

In the future the second version could be deprecated in favour of type ascription?

This way existing code does not break and the ellision does not happen silently, too. Just my two cents. :-)

@aturon
Copy link
Member

aturon commented Feb 12, 2016

The lang team discussed this RFC and related issues a final time yesterday, and determined that we'd like to close the RFC for the time being, and ultimately open a new RFC that covers not only the use cases here, but also the open questions about fallback in general.

I'm planning to spearhead the effort of creating a comprehensive plan here. I'll be working with various stakeholders who would like to see improvements in this space for library ergonomics. @bluss, you're one of the main people I have in mind, but if others have specific use cases, please let me know!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
final-comment-period Will be merged/postponed/closed in ~10 calendar days unless new substational objections are raised. T-lang Relevant to the language team, which will review and decide on the RFC.
Projects
None yet
Development

Successfully merging this pull request may close these issues.