iterator_item
iterator_item copied to clipboard
[question] Is the marker `fn*` justified?
I don't understand why a specific marker (like the star next to fn
) would be needed? I don't think that it was correctly justified.
Bjarne Stroustroup, creator of C++, said that for every new features, people wants extra marker, but once they are used to it, they want it removed. It's why there is template<typename T> void foo(t T)
in C++ and not void foo<T>(t T)>
in C++.
I also don't understand why a marker need to be in the public part of the API. Does this means that if at one point I want to replace a manually written iterator by a generator (or vice-versa) this is a breaking change even if it should only be an implementation detail?
Is it necessary? No. Because of async
, there's an expectation that people scan the fn
head to identify what the function does. If we only rely on a contextual keyword that appears after the parameters, where the return type might be, people could miss it. It might not be a real problem, of course.
Having said that, the proposed syntax is just one example and is not my preferred version (although it did grow on me while using it). I desire to have a dozen or more alternatives to try out.
What I don't think would be a good idea is having no affordance in the signature, because then changes to the body could cause breaking changes to the callers.
Having redundancy in the syntax can also help: if you want a generator that yields ()
, with fn*
you wouldn't have to write it, like with return types. Redundancy also helps with parsing, but I am not opposed to having a single element in the signature that signals it's a generator and not just a function.
fn*
is borrowed from JavaScript. It doesn't mean we need to take it wholesale, but there is precedent for that syntax.
Finally, the "nice thing" about fn*
is that it doesn't add a new keyword. Whether that's a consideration even worrying about, particularly given we have k#keywords
now, is a separate discussion that I do want to have.
Does this summary of my thinking address your concerns?
Does this summary of my thinking address your concerns?
Not exactly, but it’s because I didn’t expressed my thought with enough details.
Why would this not work?
fn foo() -> impl Iterator<Item=i32> {
gen {
yield 0;
for i in 5..10 {
yield i;
}
}
}
As far as I can tell, using a generator to create an iterator is an implementation detail. I don’t see why making it explicit in the function signature (fn*
instead of fn
) would be beneficial. Is there any difference for the caller if foo
is implemented with a generator, or with a manual implementation of Iterator
?
For the second marker (-> yield i32 { … }
), I thought that it was just a convenient way of writing -> impl Iterator<Item=i32> { gen { … } }
. It there any more fundamental differences?
And finally, if the generator is really an implementation detail, is the top-level gen
really needed? If not, what prevents us to write the simpler.
fn foo() -> impl Iterator<Item=i32> {
yield 0;
for i in 5..10 {
yield i;
}
}