-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
Bikeshedding the 'gen' keyword & syntax #123731
Comments
This comment was marked as resolved.
This comment was marked as resolved.
Relevant Zulip thread regarding reserving the |
As a user, I'm not too concerned about what the keyword is, as long as it's fairly succinct and suggests either generators or iterators1. I'd be highly in favour of providing some sort of Footnotes
|
I can't wait for generator functions, I love generator functions. Your friend, |
One thing that I think is important is that we pick the same keyword for coroutines and generators, as they are very similar. Coroutines take arguments on every resume, even the first one, so they need a closure-style syntax (e.g. I personally am not convinced that the extra language surface of |
Eh. Slight off topic: |
I don't think this is something we should commit to at this point. Both generators and async functions are specializations of the more general underlying control flow mechanism, exposing different subsets of that behavior- neither expose the "resume argument" to the user and both fix the yield and return types in particular ways. Coroutines should remain free to change arbitrarily, so long as |
Yea, that makes sense. Especially since I was reminded that gen closures could be a different thing, the equivalent to async closures |
Throwing some options out there:
|
Right now I'm -1 on any syntax that uses
If we reserve
Seems no worse than |
An insight on the
Considering we have two weeks left until the first 2024 edition deadline, I would say we go with |
Apologies to @oli-obk - it was just yesterday that I said I wouldn't post in this thread and would prefer to write a blog instead to explore the implications of the Gen keywordWith generator blocks and closures there is a distinction between the logical return type and the logical yield type. While // block, inferred
let iter = gen { yield 12; }
// block, yield type annotated
let iter: impl Iterator<Item = u32> gen { yield 12; }
// closure, inferred
let iter = gen || { yield 12; }
// closure, yield type annotated
let iter: impl Iterator<Item = u32> = gen || { yield 12; }
// closure, return type annotated
let iter = gen || -> () { yield 12; } We're not yet making a decision on generator functions, though they are available on unstable. But it's worth pointing out that much of the same rationale I've listed here applies to those too, but unlike generator blocks and closures, generator functions must always spell out their yield type - they cannot just rely on inference the way blocks and closures do. If we were to use Yields keywordOn the RFC PR I floated the idea of using // block, inferred
let iter = yields { yield 12; }
// block, yield type annotated
let iter = yields u32 { yield 12; }
// closure, inferred
let iter = yields || { yield 12; }
// closure, yield type annotated
// NOTE: must be unambiguous with `|| yields {}`
let iter = yields u32 || { yield 12; }
// closure, return type annotated
// NOTE: must be unambiguous with `|| yields {}`
let iter = yields || -> () { yield 12; }
// closure, yields + return type annotated
// NOTE: must be unambiguous with `|| yields {}`
let iter = yields u32 || -> () { yield 12; } As you can see here, the fact that we're supporting both generator blocks and generator functions ends up leading to syntactic ambiguity. If we were to adopt To resolve this ambiguity we'd need to put the Gen + YieldsOne way to still reap the benefits of a // block, inferred
let iter = gen { yield 12; }
// block, yield type annotated
let iter = gen yields u32 { yield 12; }
// closure, inferred
let iter = gen || { yield 12; }
// closure, yield type annotated
let iter = gen || yields u32 { yield 12; }
// closure, return type annotated
let iter = gen || -> () { yield 12; }
// closure, yields + return type annotated
let iter = gen || yields u32 -> () { yield 12; } // either…
let iter = gen || -> () yields u32 { yield 12; } // …or While a little more verbose than the bare // Having a `gen fn` notation here would be
// unlikely to carry its weight here since it
// does not resolve any parser ambiguity.
gen fn iter() yields u32 -> () {} // either…
gen fn iter() -> () yields u32 {} // …or
// The `yields` keyword on its own here is enough
// to indicate this is a generator function.
fn iter() yields u32 -> () {} // either…
fn iter() -> () yields u32 {} // …or Having gone through this exercise, I believe that of the three options this option carries the least worst tradeoffs. ConclusionI think Basically that's a long way to say that I find myself agreeing with what @tmandry expressed earlier. |
I could be wrong, but I don't think the syntax can be gen || yields fn() -> i32 {}
gen || yields impl Fn() -> i32 {} On the other hand, the syntax gen || -> fn() yields i32 {}
gen || -> impl Fn() yields i32 {} I don't really like this second one though, cause in my head it reads "returns the type I think the syntax gen || -> yield fn(), return i32 {}
gen || -> yield impl Fn(), return i32 {} gen || -> yield fn(); i32 {}
gen || -> yield impl Fn(); i32 {} The first feels a bit more readable, refactorable, extensible. The second flows better as a "mini function body" kind of syntax. Similar to |
This is the only part of your comment I disagree with @yoshuawuyts. I think There's also the question of consistency with async closures, blocks, and functions. For completeness though, if we wanted the "minimal syntactic noise" option I guess we could use // Generator block
let iter = gen { yield 12; }
// Generator closure
let iter = || yields u32 { yield 12; }
// Closure which returns a generator block
let iter = || gen { yield 12; }
let iter = || gen yields i32 { yield 12; } // (with explicit yield type)
// Generator function
fn iter() yields u32 { yield 12; }
@Yokinman We can resolve such ambiguities, which I would expect to be rare, by requiring parentheses to disambiguate. gen || yields (fn()) -> i32 {}
gen || yields (impl Fn() -> i32) {} In fact, we may want to use gen || yields (impl GenFn() yields i32) -> i32 {} Admittedly, all of this discussion is getting outside the scope of the original issue. I don't think we have to reserve |
until we know what traits it's supposed to implement (e.g. hypothetical |
@tmandry Oh I absolutely agree about the question about consistency. I see that as the main thing we'd be trading off. I also agree that we need some identifier in the function signature to signal: "Hey this function here, this is a generator function". We could certainly allow people to write the following: gen fn meow() {} // This signature…
gen fn meow() yields {} // …could be a shorthand for this signature…
gen fn meow() yields () {} // …could be a shorthand for this signature. I was thinking that if we disallowed that first case ( |
as per #123731 (comment) I'm gonna close this as resolved now. We're going with |
#117078 lists "unresolved" questions:
From the RFC:
So we need a keyword for "gen blocks";
gen fn
might get added in the future.Conflicts
This conflicts with
rand::Rng::gen
(see rust-random/rand#1435). There are quite a few hits within therand
crate. Therand_distr
crate has 3 uses within documentation, 2 within tests and 14 in algorithms.This isn't a lot of data, but hints at quite a few real uses of
Rng::gen
outsiderand
, not counting other possible uses ofgen
.From the RFC
I haven't read all comments on the RFC, but several indicate that
gen fn
may not be a good thing.From @Kray:
From @withoutboats:
From @emilyyyylime:
There is also discussion on usage of
generator
instead and discussion ongen
as a contextual keyword.Since I'm not fully read-up on the status of this RFC and its implementation, I'll just leave this here with the question of whether
gen
will become a keyword.The text was updated successfully, but these errors were encountered: