Open gavinking opened 7 years ago
hey, y'know what would be the coolest thing ever?
A renamed
annotation that:
renamed ("Iterable")
shared interface Stream<out Element=Anything,
out Absent=Null>
given Absent satisfies Null { ... }
That would be cool and not even very hard to implement!
but it has grown into something quite different
I'm curious to know what Stream
is meant to indicate, and how it compares to Iterable
.
At a very practical level, 1.3.3
's Iterable
s:
How does the word "stream" fit in with these properties? Are there other properties that should help guide the naming? Is it all the default methods?
It's probably worth noting that the value {1.0, 4.0^0.5, 9.0^0.5}
is a very specific type of thing, whereas {Float+}
is a very general concept, despite the former's type being no more specific than the latter.
That would be cool and not even very hard to implement!
Yeah it would be. It would be a lot nicer than, e.g., ceylon.json
's JsonObject
alias.
hey, y'know what would be the coolest thing ever? A
renamed
annotation [...]
I don't really like that idea; it feels weird to have an annotation imply a whole declaration like that. I'd much rather have the IDEs prefer to use the aliased declaration of a deprecated
alias:
deprecated("Use [[Stream]] instead.")
see(class Stream)
shared interface Iterable<out Element = Anything, out Absent = Null>
given Absent
satisfies Null
=> Stream<Element, Absent>;
This would avoid introducing a new annotation whose behavior feels completely alien to the behaviors of the current annotations.
I don't really have an opinion about changing "Iterable
" to "Stream
", though. I wouldn't mind either way.
I'd much rather have the IDEs prefer to use the aliased declaration of a deprecated alias
That would not accomplish the goals of a) maintaining binary compatibility and b) having the new source code be minimally complicated and cluttered by workarounds for past naming mistakes.
"hey, y'know what would be the coolest thing ever? A renamed annotation [...]"
Don't we already have an alias
annotation doing something barely similar, but only at IDE level? Can't we just add the missing typechecker and backend behaviours?
@someth2say well I agree that aliased
is kinda similar, but it's really just a hint, meant for people migrating from other languages. It's doesn't have the effect of changing the binary code generated.
@gavinking well, the point of having renamed
is helping migration from <1.4 to >=1.4, isn't it? Binary changing effects are to be added.
BTW, getting back to the beginning of the issue,I actually ended liking Iterable
, but I agree Stream
is more "mainstream", and would help Ceylon adoption. So +1 for the change.
@jvasileff
I have oppositions, but I think someone pursuing this idea should open a thread about it, instead of discussing it here.
To be honest, I like the @Zambonifofex's proposal even better. Less magic there.
In the end, the renamed
annotation would perform exactly the same function, but do it in a slightly unsafer way -- the name of the interface in the annotation argument has a slight chance of being mistyped or "forgotten" in the course of some refactoring...
I don't think it's worth changing, because our definition of Stream
is different to other languages anyway, and especially now that reactive streams are becoming mainstrearm, it's extremely confusing to speak to people about Java streams and reactive streams, which have absolutely nothing in common.
So what's the consensus here? To not make the change?
I like the idea of making the change, if only to make parts of the documentation like this page more clear.
I always disliked the name Iterable. Stream would be great.
IMHO, Stream
carries better the meaning of internal/functional/lazy iterator, while Iterable
suggests external/procedural/eager iterator.
I'm changing my mind on this. As pointed out by others, there is a difference between a "stream", and an "iterable", in that a stream is in some sense stateful and cannot be re-iterated, whereas an iterable is in this sense state less and can be re-iterated "from the start", whatever that means, given the semantics of the particular iterable.
And a second problem is: if we rename Iterable
to Stream
, what do we call Iterator
.
So I'm now thinking of leaving the name alone.
Without trying to force this issue in either direction, an alternative could be:
Iterable
→ Source
or Streamable
Iterator
→ Stream
Based on the above two comments, I suppose that between Iterable
and Iterator
, it makes more sense for Iterator
to be renamed to Stream
than for Iterable
to be renamed.
I do hope we have some suitable terms remaining when the async revolution hits Ceylon :)
Based on the above two comments, I suppose that between Iterable and Iterator, it makes more sense for Iterator to be renamed to Stream than for Iterable to be renamed.
I really like this idea. Seems to me a viable relationship (and bridge) between Iterable
s and Stream
s in the language.
Would that mean that the "streaming methods" that are usually associated with the notion of streams (like map
, filter
, reduce
etc) should be moved from Iterable
to Iterator
aka Stream
?
Further, I think this is connected to #6406.
@dlkw that would create bad ergonomics, like in java where you always have to call stream()
before being able to do anything useful.
Also, methods like map()
should preserve the source's re-iterability, which wouldn't be possible on Iterator
s.
Based on the above two comments, I suppose that between Iterable and Iterator, it makes more sense for Iterator to be renamed to Stream than for Iterable to be renamed.
Stream
is a really nice name, and I think it would be a waste to use it on something so useless as an Iterator
, which is primarily just a temporary object used by for
loops. And even if Stream
isn't otherwise used, I'm not sure giving Iterator
s a nice name would do anything other than encourage overuse (misuse).
Sounds like consensus is forming around leaving the names as they are?
Sounds like consensus is forming around leaving the names as they are?
Yes.
@dlkw wrote:
Would that mean that the "streaming methods" that are usually associated with the notion of streams (like map, filter, reduce etc) should be moved from Iterable to Iterator aka Stream?
@jvasileff wrote:
Also, methods like map() should preserve the source's re-iterability, which wouldn't be possible on Iterators.
Nothing stopping from having both interfaces include them :) Maybe even a common superinterface...
OFFTOPIC Sorry, can't resist.. A totally different way of looking at Iterable
vs Iterator
would be to see Iterable simply being a source of Iterators.. an Iterator producing Iterators..
interface Iterable<out Element=Anything, out Absent=Null>
satisfies Iterator<Iterator<Element>> {
next() => iterator();
}
And while at it, why not introduce a OmniPresent
type parameter for indicating infinite Iterables..
"""Use:
* Iterable<Element[, Null, Null]> for possibly-empty streams
* Iterable<Element, Nothing[, Null]> for nonempty streams
* Iterable<Element, Nothing, Nothing> for infinite streams
"""
interface Iterable<out Element=Anything, out Absent=Null, out OmniPresent=Null>
given Absent satisfies Omnipresent
{
shared default Iterable<Element,Absent&OmniPresent,OmniPresent> rest => skip(1);
}
I wouldn't mind if Iterator<T>
also satisfied Iterable<T>
(and {T*}
) and could be used in for…in
loops. That's always bugged me in Java, too.
I wouldn't mind if Iterator
also satisfied Iterable (and {T*}) and could be used in for…in loops.
I think having Iterator
extend Iterable
(or, perhaps vice versa) would create confusion around the re-iterability of Iterable
s, implying that all Iterable
s are re-iterable (which they are not), and that Iterator
s should always be used for non-reiterable streams.
A common argument against having the type system indicate re-iterability is that it would be prone to errors. You would have two types with basically identical APIs, and people may too easily (and incorrectly) use Iterable
s for single-shot streams.
That's always bugged me in Java, too
Yeah. In both Java and Ceylon, I've run into cases where I've wanted to use multiple for
loops to processes a single Iterator
, passing control among them.
Why could we not just let the for (a in b)
accept both Iterable
and Iterator
for b
? This would imo be clearer than having Iterator
s satisfy Iterable
..
@jvasileff wrote:
Yeah. In both Java and Ceylon, I've run into cases where I've wanted to use multiple for loops to processes a single Iterator, passing control among them.
As the Iterator.next() contract requires implementations to indefinitely return finished
after having done so once, my above proposal would also allow breaking out of one loop and resuming iteration in another..
Alright, so here's a thing I've wanted to do forever. It turns out that
Iterable
is a really terrible name for the things we call "streams". The whole perspective of working with streams is to not think of procedurally "iterating" over them. Now, sure, Ceylon'sIterable
historically grew out of something that filled the same role as Java's interface of the same name, but it has grown into something quite different.Now, we could rename
Iterable
toStream
without breaking any existing source code simply by creating an alias for it:We could even make it binary compatible by simply munging the name
Stream
back toIterable
in the backend, but at this point I'm not sure that's even worth the effort, since we're breaking binary compatibility in the next release anyway.Thoughts?