[Pitch] Remove destructive consumption from Sequence

1. Presumably these are all for-in-able. What makes something
for-in-able?

I would think the potentially infinite should require for-in-until
(even if you explicitly set until to false to create an infinite
loop), but collection would allow for-in (with optional until). That
way you have to acknowledge the possibility of an infinite
sequence/iterator.

Are you proposing a new language feature?

That was my impression. It’s an interesting idea. It wouldn’t guarantee termination but would require developers to consider termination and therefore prevent accidents, which are the concerns I am raising.

Yes, exactly.

We could also do this with

  for i in allIntegers.until(isPrime)

Is `until` lazy or eager here? I imagine you’re thinking it would be lazy because making it eager would introduce unnecessary allocation. However, if it’s lazy it is an exception to the explicit laziness Swift has adopted.

Wouldn’t it be better to require explicit laziness `allIntegers.lazy.until` if for..in is going to be require finite sequences and we’re going to use a library solution to support infinite sequences? It’s more verbose but more consistent with how laziness is currently handled. It also doesn’t privilege any specific operator (which isn’t necessary if we do this in the library).

Everything with Iterators / Sequences should be lazy (because that is the only safe thing). Collections would most likely still be eager.

In this case, ‘until' is creating a collection (most likely an array), so it would be eager. For an iterator, it would immediately drain and buffer it. For a sequence, I guess it is whatever is most efficient.

If we went with a language solution I imagine it would look something like this:

for i in allIntegers until i.isPrime && i > 1000 where i.isEven {
   // all even integers < the first prime > 1000
}

IIRC the `until` clause has already been discussed as syntactic sugar for early termination. Its utility wouldn’t be limited to looping over infinite sequences, however it would be *required* when you loop over an infinite sequence.

Yeah, that is my thought as well. I would also be ok with Dave’s suggestion as long as the path (compiler complain -> Suggest Fix) is the same, so you still consider and deal with the infinite case.

I do like the look of the language feature better, of course...

This sugar wouldn’t have to be introduced in Swift 3. We could make for..in require finite sequences in Swift 3 and add it later if there is sufficient demand. In the meantime people could iterate infinite sequences manually and / or we could add library support via lazy operators that select a prefix (if we are willing to live with the fact that in practice termination may depend on arguments to the operator).

Ah, I just saw your point above. allIntegers.until(isPrime) most likely needs to be eager, but that is the opposite of what we want for a for-in loop (it could have an early break), so the language feature might be quite a bit more efficient, and might actually be worthwhile (especially considering that people were already asking for it anyway). I was indifferent to it, but I like it for this case…

Anyway, I am interested to see what they are cooking up for us (It may be much better than this)...

Thanks,
Jon

One more question on this.

How would we handle the opt-out safety for people who are intentionally trying to create an infinite loop?

I don’t think we can make .until() safely lazy for iterators which have reference semantics and are destructive single-pass, so it has to be eager.
If .until() is eager, then .until(false) will fall into an infinite loop before the for-in loop is even run.

This is an argument against a library solution if iterators have reference semantics. Any solution that requires eager behavior is not a good solution.

Maybe there just a way to plug an iterator/sequence directly in, and then silence the warning? or we could do for-in-until…

I don’t think this should be a warning that can be silenced. I think you are uncovering good reasons to go with a language supported solution.

This wouldn’t need to happen in Swift 3. There have been other cases where we have adopted a “break it now in anticipation of making it better in the future” policy. If we conclude that a language solution is warranted and can’t get it into Swift 3 that is ok IMO. It is still possible to manually iterate infinite sequences. This wouldn’t be that big a deal given that it is relatively infrequent.

···

On Jun 28, 2016, at 2:18 PM, Jonathan Hull via swift-evolution <swift-evolution@swift.org> wrote:

Thanks,
Jon

On Jun 28, 2016, at 10:51 AM, Dave Abrahams <dabrahams@apple.com <mailto:dabrahams@apple.com>> wrote:

This is a reasonable structure, but there are important details missing.

1. Presumably these are all for-in-able. What makes something
for-in-able?

I would think the potentially infinite should require for-in-until
(even if you explicitly set until to false to create an infinite
loop), but collection would allow for-in (with optional until). That
way you have to acknowledge the possibility of an infinite
sequence/iterator.

Are you proposing a new language feature? We could also do this with

   for i in allIntegers.until(isPrime)

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

1. Presumably these are all for-in-able. What makes something
for-in-able?

I would think the potentially infinite should require for-in-until
(even if you explicitly set until to false to create an infinite
loop), but collection would allow for-in (with optional until). That
way you have to acknowledge the possibility of an infinite
sequence/iterator.

Are you proposing a new language feature? We could also do this with

   for i in allIntegers.until(isPrime)

I was, but I think I would be ok with this too, as long as the
compiler caught the issue and suggested a fix. The key is that we are
forced to deal with the possibility of an infinite sequence/iterator.
Also:

for i in infSequence.maxLoops(3000) //or a better name for this idea

This is already:

  for i in infSequence.prefix(3000) {

  }

···

on Tue Jun 28 2016, Jonathan Hull <swift-evolution@swift.org> wrote:

On Jun 28, 2016, at 10:51 AM, Dave Abrahams <dabrahams@apple.com> wrote:

Thanks,
Jon
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

--
Dave

Can’t a Sequence be potentially infinite, whereas a collection has a
defined count/endIndex? Other than that, I agree with your

statement.

Here is what I see as the appropriate structure:

Iterator: Single destructive pass, potentially infinite, (should be for-in able)
Sequence: Guaranteed non-destructive multi-pass (vends Iterators),
potentially infinite, (should be subscript-able, gain most of
collection, but lose anything that relies on it ending)
Collection: Multi-pass, guaranteed finite, (no changes from current
form, except extra inits from Iterator/Sequence with end conditions)

Right now we are allowed to have an infinite sequence, but calling
dropLast or non-lazy map will cause an infinite loop. These cases
could be made much safer by considering the potentially infinite and
finite cases separately…

I think this is pointing in the right general direction. It would
make working with `Sequence` much more straightforward and allow us to
depend on the multi-pass property that is true in practice of the most
common models of `Sequence`.

But I agree that we should give much more careful consideration to
finite / infinite generally and for..in specifically.

Now that I have been thinking about the finite / infinite distinction
more closely I have begun to notice a lot of code that is written
generically using `Sequence` where a for..in loop is really what is
required, however the “finite sequence” precondition is not explicitly
stated. Interestingly, this is the case with the standard library’s
eager `map` (but not the case with `dropLast` which explicitly notes
the precondition). I have been somewhat surprised to realize how
common this “bug” is (i.e. not stating a precondition). I think we
have gotten away with it thus far because the sequences most people
use most of the time in practice are finite. But that doesn’t mean we
should accept this as good enough - IMO it is way to easy to forget to
document this precondition (and obviously easier for users to overlook
than preconditions that are actually encoded in the type system,
violations of which are caught at compile time).

The fact that this pattern is so pervasive is what I meant when I said
for..in “naturally” requires a finite sequence.

IMO it’s better to encode preconditions in the type system when that
is practical, and especially when the precondition is shared by a vast
majority of code written using a particular construct (in this case a
for..in loop written using the most generic for..in-able protocol).

I think the safest solution is to take the position that writing an
infinite loop is relatively uncommon and is a more “advanced”
technique, and thus should be done explicitly. Do people really write
infinite loops often enough that the convenience of using for..in when
writing infinite loops outweighs the safety benefit of preventing
accidental infinite loops? I haven’t seen a compelling argument for
this.

Good questions. I'd also add: “do infinite sequences come up often
enough that accidentally looping on them forever is a problem?”

That’s a good question as well. In practice the frequency of infinite
sequences likely depends on the domain.

IMO this falls into the same category as “do single pass sequences
come up often enough that attempting to iterate over them twice is a
problem. To paraphrase your previous post:

Today, people see a beautiful, simple protocol (Sequence) to which many
things conform. They don't recognize that there's a semantic restriction
(you can’t assume it is finite!) on it, so they write libraries
of functions that may iterate a Sequence to termination. They test
their libraries with the most commonly-available Sequences, e.g. Arrays
and Ranges, which happen to be finite. Their tests pass! But their
constraints are wrong, their whole model of how to write generic code
over sequences is wrong, and some of their code is wrong.

IMO this is a problematic programming model.

I definitely don’t mean to put words in your mouth here, but the
logical structure of the argument appears identical to me regardless
of which issue it is applied to. I am only trying to make that point.

Oh, I fully agree. We're *in* this discussion because neither
single-pass nor infinite sequences come up very often. It raises the
question of whether the conceptual framework ought to accomodate them at
all, and if they should be included, how they should be represented.

We’ve considered good practical examples of both. They exist and
people use them. I think we’re better off having a common vocabulary
for them rather than requiring everyone who uses these concepts to
invent their own.

I agree that the question of how best to represent them is crucial.

The quotation you cited above isn't arguing that single-pass sequences
are important enough to represent. It is trying to point out that the
refinement relationship between single- and multipass sequences has an
undesirable effect. I'm certain the same thing could occur for infinite
and finite sequences.

I’m not sure I agree. Neither are clearly modeled in the current
library so I don’t think we know what the impact would be if they
were. I’m willing to bet that people would choose the correct
constraints if they were available (modeled accurately in the standard
library, well documented, etc).

IMO it's just a symptom of concept refinement when the available models
almost all conform to the more-refined concept.

I think I see what you mean. However, if there are algorithms that can be expressed in terms of the weaker concept that are also useful (and performant) with the more refined concept that would be an argument for explicitly modeling the refinement relationship. I don’t know how often this happens in practice though (and I haven’t done any analysis of whether this argument would be applicable to the current thread or not).

What both of these *definitely* have in common is that they are purely
semantic requirements that cannot be stated explicitly as requirements
using syntax of the language. You have reminded us many times that
protocols should not just be about syntax, but should also emphasize
semantics. We shouldn’t shy away from introducing a protocol that
doesn’t add any syntactic requirements if it is necessary to capture
important semantic requirements.

FWIW, Max pointed out to me on Friday that Scala's concept for
possibly-single-pass sequences is called “TraversibleOnce.” I think
that name goes a long way to solving the problem. I'm not sure how we'd
apply the same idea to finite/infinite sequences, though.

I’ve was thinking about how to best represent these while mowing the
lawn this afternoon and had an interesting thought. If we add one
additional protocol and pin down some of the semantics that have been
discussed in this thread we could have a pretty straightforward model
that captures the various semantics using a protocol in each of 4
quadrants:

                       Single Pass Multi Pass

Potentially Sequence
Infinite IteratorProtocol <-------------------------| makeIterator()
                                 > >
                                 > Collection
Finite FiniteIteratorProtocol <------------------- makeIterator()

The semantic refinement on `Sequence` is that it must now produce a
new iterator every time one is requested (and they must all produce
identical sequences of values as long as the sequence was not mutated
in between calls to `makeIterator`).

The semantic refinement on `Collection` is that it must now be finite
and its `Iterator` must conform to `FiniteIteratorProtocol` rather
than `IteratorProtocol`.

`FiniteIteratorProtocol` is the new protocol which introduces the
semantic requirement that calls to `next` must “eventually” produce
`nil`. “Eventually” might be a bit fuzzy but should carry the intent
that your program won’t hang or be killed by the OS (i.e. diverge) if
you iterate until `nil` is reached.

I talked to Dmitri and Max about all this last night and I think we have
a simpler answer, which we'll post shortly. Please let us know if you
think it is missing something.

Looking forward to seeing what you post.

You mentioned in another thread that for..in wouldn’t necessarily have
to use a single protocol (or refinement relationship). Under this
model I would propose that we make for..in operate on top of
`FiniteIteratorProtocol`, but also have the ability to get the
iterator from a `Collection`. (I’m using still advocating for
restricting the for..in sugar to finite iteration but the same idea
would work at the “potentially infinite” level using `IteratorProtocol
and `Sequence`). The only change from current state (aside from the
finite restriction) is to allow you to directly provide an iterator to
the for..in loop rathe than requiring the compiler to get a new one
via a call to `makeIterator`.

If we do adopt something like this model, I think a couple of
guidelines are clear regardless of whether for..in works exclusively
with finite concepts or also admits potentially infinite ones. All
code that requires terminating iteration should be written with
`Collection` or `FiniteIteratorProtocol`, not `Sequence` or
`IteratorProtocol`. Following from this, all finite sequences should
conform to `Collection`, not just `Sequence` so it can continue to
work with all code that requires finite sequences. One advantage of
restricting for..in to the finite concepts is that it encourages
people to follow this guideline so that their types will be usable
with for..in, finite constraints, etc.

In order to make it easy to follow the second guideline, the library
should strive to make it as easy as possible to implement forward only
`Collection` if you can vend an iterator and guarantee it is finite.
Ideally it would be no more difficult than it is to implement
`Sequence` today. Further customization of the implementation would
be done as an optimization, not just to achieve basic capabilities.

One other topic that came up is whether the single pass concepts
should be required to have reference semantics. If we go in that
direction it is clear that in the previous model `IteratorProtocol`
would have a class requirement which would be inherited by
`FiniteIteratorProtocol`.

However, I am not convinced we should require reference semantics
here. This requirement doesn’t exist today and I haven’t heard anyone
say it has directly been a problem. The problem seems to be the due
to the fact that `Sequence` is missing a semantic requirement everyone
expects. I don’t know the rationale that was originally behind that
decision and am curious. Is it because for..in works via `Sequence`
and it was desirable to have for..in work with single pass sequences?
The above suggested tweak to how for..in addresses that.

Further, consider an algorithm which has multiple “choices” available
and would like to be able to take advantage of backtracking. This can
be achieved trivially with value semantic iterators by simply making a
copy at any time during iteration and returning to the copy if a dead
end is reached. You could sort of mimic this with a reference
semantic iterator by conforming the iterator itself to `Sequence` or
`Collection` and calling `makeIterator` to “clone” it. Obviously this
would only be possible with intimate knowledge of the iterator and
would usually not be possible retroactively, whereas it would be
trivial to take advantage of iterators that have value semantics.

Seems to me that in either case you need intimate knowledge of the
iterator. You either need to know that it has value semantics, or that
it conforms to Sequence, right?

My argument here was in regards to an iterator that wasn’t initially designed with this use case in mind and which may exist in a module you don’t control.

If iterators can have value semantics it is likely that simple iterators often *will* have value semantics. This means that it would be possible to use them in this way with no further effort.

It is far less likely that iterators will conform to `Sequence`. That means you would need to add the `Sequence` conformance if you wanted to use them in this use case. Adding `Sequence` conformance requires much more intimate knowledge of implementation details, not just semantic properties. Most of the time it will not be possible to add the `Sequence` conformance retroactively even if the iterator could conform in theory.

This use case seems interesting enough to at least deserve some consideration before we decide to require iterators to have reference semantics. IMO the fact that `next` is a mutating requirement provides a pretty strong indication of the destructive / consuming semantics of using an iterator. Requiring reference semantics gives up flexibility and I’m not completely convinced it adds clarity (but still keeping an open mind about this).

I think a significant part of the trouble with `Sequence` is that `makeIterator` is *not* marked as mutating despite the fact that the current semantics do not allow the sequence to be consumed more than once and the common models of `Sequence` are all value types.

-Matthew

···

On Jun 28, 2016, at 12:44 PM, Dave Abrahams <dabrahams@apple.com> wrote:
on Mon Jun 27 2016, Matthew Johnson <matthew-AT-anandabits.com <http://matthew-at-anandabits.com/&gt;&gt; wrote:

On Jun 27, 2016, at 2:41 PM, Dave Abrahams <dabrahams@apple.com <mailto:dabrahams@apple.com>> wrote:
on Mon Jun 27 2016, Matthew Johnson <matthew-AT-anandabits.com <http://matthew-at-anandabits.com/&gt; <http://matthew-at-anandabits.com/&gt;&gt; wrote:

On Jun 27, 2016, at 11:46 AM, Dave Abrahams <dabrahams@apple.com <mailto:dabrahams@apple.com><mailto:dabrahams@apple.com <mailto:dabrahams@apple.com>>> wrote:
on Mon Jun 27 2016, Matthew Johnson <matthew-AT-anandabits.com <http://matthew-at-anandabits.com/&gt; >>>>> <http://matthew-at-anandabits.com/&gt; >>>>> <http://matthew-at-anandabits.com/ >>>>> <http://matthew-at-anandabits.com/&gt;&gt;&gt; wrote:

On Jun 26, 2016, at 10:56 PM, Jonathan Hull via swift-evolution >>>>>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org> <mailto:swift-evolution@swift.org <mailto:swift-evolution@swift.org>>> wrote:

-Matthew

If we adopt that position then for..in would need to be built on top
of a guaranteed finite construct. This would allow programmers to
continue writing generic code agains the most generic for..in-able
construct while eliminating a precondition that is often (usually?)
unstated and likely unconsidered.

If we do decide to move forward with infinite for..in loops I think we
need to establish strong guidance around how to properly write generic
code with these protocols. Should such code really be constrained to
`Collection` rather than `Sequence` (i.e. should a potentially
infinite `Sequence` have an eager map)? If this is the guidance,
should it be paired with guidance that all finite sequences should
conform to `Collection`? Or is it sufficient to just educate
developers about this issue and expect people to document the “finite
Sequence” precondition when the constraint is `Sequence` rather than
`Collection`?

I hope we will give serious consideration to these questions while
this topic is open for discussion.

-Matthew

Thanks,
Jon

on Wed Jun 22 2016, David Waite <david-AT-alkaline-solutions.com <http://david-at-alkaline-solutions.com/&gt; <http://david-at-alkaline-solutions.com/&gt; >>>>>>>> <http://david-at-alkaline-solutions.com/ >>>>>>>> <http://david-at-alkaline-solutions.com/&gt; >>>>>>>> <http://david-at-alkaline-solutions.com/ >>>>>>>> <http://david-at-alkaline-solutions.com/&gt;&gt;&gt;&gt; wrote:

On Jun 22, 2016, at 2:57 PM, Dave Abrahams via swift-evolution
<swift-evolution at swift.org <http://swift.org/&gt; <http://swift.org/&gt; <http://swift.org/ <http://swift.org/&gt;&gt;
<https://lists.swift.org/mailman/listinfo/swift-evolution
<https://lists.swift.org/mailman/listinfo/swift-evolution&gt;
<https://lists.swift.org/mailman/listinfo/swift-evolution&gt;&gt;&gt;
wrote:

<Ahem> “Iterators,” please.

That makes me happy - for some reason I thought it was still GeneratorProtocol

destructively, but such Generators would not conform to the needs of
Sequence. As such, the most significant impact would be the inability
to use such Generators in a for..in loop,

Trying to evaluate this statement, it's clear we're missing lots of
detail here:

* Would you remove Sequence?
* If so, what Protocol would embody “for...in-able?”

No, I would just remove the allowance in the documentation and API
design for a destructive/consuming iteration. Sequence would be the
interface to getting access to repeatable iteration, without the need
for meeting the other requirements for Collection.

That would be wrong unless there exist substantial examples of a
multipass Sequence that *can't* meet the other requirements of
Collection without loss of efficiency. And since I can write an adaptor
that turns any multipass sequence into a Collection, I think it's
trivial to prove that no such examples exist.

--
-Dave

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

--
-Dave

--
Dave

--
Dave

This use case seems interesting enough to at least deserve some consideration before we decide to require iterators to have reference semantics. IMO the fact that `next` is a mutating requirement provides a pretty strong indication of the destructive / consuming semantics of using an iterator. Requiring reference semantics gives up flexibility and I’m not completely convinced it adds clarity (but still keeping an open mind about this).

My feeling is that, except for the trivial case of IndexingIterator and other iterators over collections, all iterators *should* need reference semantics. If your iterator can offer value semantics, then you should convert it into a Collection to gain the benefits of indices. If it can't offer value semantics, then you should write it as a class so those reference semantics are obvious. This would get rid of the really ugly caveat that's always festered at the heart of Iterator:

Obtain each separate iterator from separate calls to the sequence's makeIterator() method rather than by copying. Copying an iterator is safe, but advancing one copy of an iterator by calling its next() method may invalidate other copies of that iterator. for-in loops are safe in this regard.

The underlying cause of this requirement is that you can't know whether a given iterator is a value type or a reference type. Let's fix that.

···

--
Brent Royal-Gordon
Architechies

This use case seems interesting enough to at least deserve some consideration before we decide to require iterators to have reference semantics. IMO the fact that `next` is a mutating requirement provides a pretty strong indication of the destructive / consuming semantics of using an iterator. Requiring reference semantics gives up flexibility and I’m not completely convinced it adds clarity (but still keeping an open mind about this).

My feeling is that, except for the trivial case of IndexingIterator and other iterators over collections, all iterators *should* need reference semantics. If your iterator can offer value semantics, then you should convert it into a Collection to gain the benefits of indices.

Assuming we are going to pin down the requirement that Collection be finite (in the sense that iterating over the whole collection is practical in a real program running on a real device that is available today) then it is trivial to come up with value semantic iterators that cannot conform to Collection. For example, iterators over infinite mathematical sequences.

If it can't offer value semantics, then you should write it as a class so those reference semantics are obvious.

I agree with this.

This would get rid of the really ugly caveat that's always festered at the heart of Iterator:

Obtain each separate iterator from separate calls to the sequence's makeIterator() method rather than by copying. Copying an iterator is safe, but advancing one copy of an iterator by calling its next() method may invalidate other copies of that iterator. for-in loops are safe in this regard.

The underlying cause of this requirement is that you can't know whether a given iterator is a value type or a reference type. Let's fix that.

I would like to see us eventually have a way to specify a generic constraint indicating value semantics regardless of what happens with iterators. If we gain that capability you *can* know that you have a value semantic iterator when you use that constraint.

I am not firmly opposed to making iterators require reference semantics but I don’t think the pros and cons have been thoroughly discussed on the list yet so I am pointing out what I think are interesting use cases for value semantic iterators.

Swift is a language that embraces value semantics. Many common iterators *can* be implemented with value semantics. Just because we can’t implement *all* iterators with value semantics doesn’t mean we should require them to have reference semantics. It just means you can’t *assume* value semantics when working with iterators in generic code unless / until we have a way to specify a value semantics constraint. That’s not necessarily a bad thing especially when it leaves the door open to interesting future possibilities.

-Matthew

···

On Jun 28, 2016, at 3:33 PM, Brent Royal-Gordon <brent@architechies.com> wrote:

--
Brent Royal-Gordon
Architechies

I'm kind of undecided about this personally. I think one of the problems with Swift is that the only indication that you have a reference type is that you can declare it as a constant, yet still call mutating methods upon it, this isn't a very positive way of identifying it however. This may be more of a GUI/IDE issue though, in that something being a class isn't always that obvious at a glance.

I wonder, could we somehow force iterators stored in variables to be passed via inout? This would make it pretty clear that you're using the same iterator and not a copy in all cases, encouraging you to obtain another if you really do need to perform multiple passes.

···

On 29 Jun 2016, at 00:10, Matthew Johnson via swift-evolution <swift-evolution@swift.org> wrote:

On Jun 28, 2016, at 3:33 PM, Brent Royal-Gordon <brent@architechies.com> wrote:

This use case seems interesting enough to at least deserve some consideration before we decide to require iterators to have reference semantics. IMO the fact that `next` is a mutating requirement provides a pretty strong indication of the destructive / consuming semantics of using an iterator. Requiring reference semantics gives up flexibility and I’m not completely convinced it adds clarity (but still keeping an open mind about this).

My feeling is that, except for the trivial case of IndexingIterator and other iterators over collections, all iterators *should* need reference semantics. If your iterator can offer value semantics, then you should convert it into a Collection to gain the benefits of indices.

Assuming we are going to pin down the requirement that Collection be finite (in the sense that iterating over the whole collection is practical in a real program running on a real device that is available today) then it is trivial to come up with value semantic iterators that cannot conform to Collection. For example, iterators over infinite mathematical sequences.

If it can't offer value semantics, then you should write it as a class so those reference semantics are obvious.

I agree with this.

This would get rid of the really ugly caveat that's always festered at the heart of Iterator:

Obtain each separate iterator from separate calls to the sequence's makeIterator() method rather than by copying. Copying an iterator is safe, but advancing one copy of an iterator by calling its next() method may invalidate other copies of that iterator. for-in loops are safe in this regard.

The underlying cause of this requirement is that you can't know whether a given iterator is a value type or a reference type. Let's fix that.

I would like to see us eventually have a way to specify a generic constraint indicating value semantics regardless of what happens with iterators. If we gain that capability you *can* know that you have a value semantic iterator when you use that constraint.

I am not firmly opposed to making iterators require reference semantics but I don’t think the pros and cons have been thoroughly discussed on the list yet so I am pointing out what I think are interesting use cases for value semantic iterators.

Swift is a language that embraces value semantics. Many common iterators *can* be implemented with value semantics. Just because we can’t implement *all* iterators with value semantics doesn’t mean we should require them to have reference semantics. It just means you can’t *assume* value semantics when working with iterators in generic code unless / until we have a way to specify a value semantics constraint. That’s not necessarily a bad thing especially when it leaves the door open to interesting future possibilities.

-Matthew

Swift is a language that embraces value semantics. Many common
iterators *can* be implemented with value semantics. Just because we
can’t implement *all* iterators with value semantics doesn’t mean we
should require them to have reference semantics. It just means you
can’t *assume* value semantics when working with iterators in generic
code unless / until we have a way to specify a value semantics
constraint. That’s not necessarily a bad thing especially when it
leaves the door open to interesting future possibilities.

-Matthew

I'm kind of undecided about this personally. I think one of the
problems with Swift is that the only indication that you have a
reference type is that you can declare it as a constant, yet still
call mutating methods upon it, this isn't a very positive way of
identifying it however. This may be more of a GUI/IDE issue though, in
that something being a class isn't always that obvious at a glance.

I wonder, could we somehow force iterators stored in variables to be
passed via inout? This would make it pretty clear that you're using
the same iterator and not a copy in all cases, encouraging you to
obtain another if you really do need to perform multiple passes.

I'm going to push single-pass iteration on the stack briefly and talk
about the topic that's been under discussion here: infinite multipass
sequences.

## Fitting “Infinite Multipass” Into the Model

It remains to be decided whether it's worth doing, but if it's to
happen, the standard library team thinks the right design is roughly
this:

  /// A multipass sequence that may be infinite
  protocol Collection {

    // Only eager algorithms that can terminate available here
    func index(where predicate: (Element)->Bool) -> Index
  
    // all lazy algorithms available here
    var lazy: ...

    var startIndex: Index
    var endIndex: Index // possibly not reachable from startIndex

    associatedtype SubSequence : Collection
    // do we need an associated FiniteSubsequence, e.g. for prefixes?
  }

  protocol FiniteCollection : Collection {

    // All eager algorithms available here
    func map(...) ->
    var count: ...
  }

  protocol BidirectionalCollection : Collection { ... }

  protocol RandomAccessCollection : BidirectionalCollection { ... }

Q: Why should there be indices on an infinite multipass sequence?

···

on Wed Jun 29 2016, Haravikk <swift-evolution-AT-haravikk.me> wrote:

On 29 Jun 2016, at 00:10, Matthew Johnson via swift-evolution <swift-evolution@swift.org> wrote:

A: Because the operations on indices apply equally well whether the
   sequence is finite or not. Find the index of a value in the
   sequence, slice the sequence, find again, etc.

Q: Why is there an endIndex on an infinite seque?
A: So you can write algorithms such as index(where:) once.

Q: Why not allow endIndex to have a different type from startIndex?
A: It appears to offer insufficient benefit for the associated
   complexity in typical usage. A classic use case that argues for a
   different endIndex type is the null-terminated C string. But you
   can't index one of those safely without actually counting the length,
   and once you've done that you can make the endIndex an Int.

## Single Pass Iteration

The refinement relationship between Sequence and Collection is
problematic, because it means either:

a) algorithms such as map on single-pass sequences claim to be
   nonmutating even though it's a lie (status quo)

b) those algorithms can't be used on immutable (“let bound”) multipass
   sequences. IMO that would be totally unacceptable.

If we drop the refinement, we can have a saner world. We also don't
need to separate Sequence and Iterator anymore. We can simply drop
Sequence altogether, and the protocol for single-pass iteration becomes
Iterator.

### Mutation and Reference Semantics

Everything in Swift is copiable via `let copy = thing` (let's please not
argue over the definition of copy for classes; this is the one built
into the lowest level of the language—I refer to the other one, that
requires allocation, as “clone”).

Anything you do with a sequence that's truly single-pass mutates the
sequence *and of its copies*. Therefore, such a type *fundamentally*
has reference semantics. One day we may be able to model single-pass
sequences with “move-only” value types, which cannot be copied. You can
find move-only types in languages like Rust and C++, but they are not
supported by Swift today. So it seems reasonable that all Iterators in
Swift today should be modeled as classes.

The fact that Swift doesn't have a mutation model for classes, though,
means that mutating methods on a class constrained protocol can't be
labeled as such. So consuming operations on a class-constrained
Iterator protocol would not be labeled as mutating.

The standard library team is currently trying to evaluate the tradeoffs
in this area. One possibility under consideration is simply dropping
support for single-pass sequences until Swift can support move-only
value types and/or gets a mutation model for class instances. It would
be very interesting to know about any real-world models of single-pass
sequences that people are using in Swift, since we don't supply any in
the standard library.

--
Dave

It would
be very interesting to know about any real-world models of single-pass
sequences that people are using in Swift, since we don't supply any in
the standard library.

--
Dave

I already gave an example of this earlier in the thread:

I use it in a LazyRowSequence<T: SqlModelConvertible> where querying Sqlite in WAL mode allows multiple concurrent readers to get point-in-time snapshots of the database. The query can’t be replayed without buffering all the rows in memory because Sqlite’s step functions are not bi-directional. In some cases we are talking about tens of thousands of rows (or even hundreds of thousands) and the ability to avoid buffering them is a feature, not a bug.

IMHO the typical case for single-pass is IO. In this case it would work just as well as LazyRowIterator<T> assuming the language allows for-in on an iterator.

Russ

···

On Jun 30, 2016, at 10:26 AM, Dave Abrahams via swift-evolution <swift-evolution@swift.org> wrote:

If Iterators become reference types that model single-pass sequences and
becomes for-in-able, as the write-up suggests, couldn't Sequence be
stipulated to be multipass and retain its refinement relationship with
Collection?

···

On Thu, Jun 30, 2016 at 12:26 Dave Abrahams via swift-evolution < swift-evolution@swift.org> wrote:

on Wed Jun 29 2016, Haravikk <swift-evolution-AT-haravikk.me> wrote:

>> On 29 Jun 2016, at 00:10, Matthew Johnson via swift-evolution < > swift-evolution@swift.org> wrote:
>>
>> Swift is a language that embraces value semantics. Many common
>> iterators *can* be implemented with value semantics. Just because we
>> can’t implement *all* iterators with value semantics doesn’t mean we
>> should require them to have reference semantics. It just means you
>> can’t *assume* value semantics when working with iterators in generic
>> code unless / until we have a way to specify a value semantics
>> constraint. That’s not necessarily a bad thing especially when it
>> leaves the door open to interesting future possibilities.
>>
>> -Matthew
>
> I'm kind of undecided about this personally. I think one of the
> problems with Swift is that the only indication that you have a
> reference type is that you can declare it as a constant, yet still
> call mutating methods upon it, this isn't a very positive way of
> identifying it however. This may be more of a GUI/IDE issue though, in
> that something being a class isn't always that obvious at a glance.
>
> I wonder, could we somehow force iterators stored in variables to be
> passed via inout? This would make it pretty clear that you're using
> the same iterator and not a copy in all cases, encouraging you to
> obtain another if you really do need to perform multiple passes.

I'm going to push single-pass iteration on the stack briefly and talk
about the topic that's been under discussion here: infinite multipass
sequences.

## Fitting “Infinite Multipass” Into the Model

It remains to be decided whether it's worth doing, but if it's to
happen, the standard library team thinks the right design is roughly
this:

  /// A multipass sequence that may be infinite
  protocol Collection {

    // Only eager algorithms that can terminate available here
    func index(where predicate: (Element)->Bool) -> Index

    // all lazy algorithms available here
    var lazy: ...

    var startIndex: Index
    var endIndex: Index // possibly not reachable from startIndex

    associatedtype SubSequence : Collection
    // do we need an associated FiniteSubsequence, e.g. for prefixes?
  }

  protocol FiniteCollection : Collection {

    // All eager algorithms available here
    func map(...) ->
    var count: ...
  }

  protocol BidirectionalCollection : Collection { ... }

  protocol RandomAccessCollection : BidirectionalCollection { ... }

Q: Why should there be indices on an infinite multipass sequence?
A: Because the operations on indices apply equally well whether the
   sequence is finite or not. Find the index of a value in the
   sequence, slice the sequence, find again, etc.

Q: Why is there an endIndex on an infinite seque?
A: So you can write algorithms such as index(where:) once.

Q: Why not allow endIndex to have a different type from startIndex?
A: It appears to offer insufficient benefit for the associated
   complexity in typical usage. A classic use case that argues for a
   different endIndex type is the null-terminated C string. But you
   can't index one of those safely without actually counting the length,
   and once you've done that you can make the endIndex an Int.

## Single Pass Iteration

The refinement relationship between Sequence and Collection is
problematic, because it means either:

a) algorithms such as map on single-pass sequences claim to be
   nonmutating even though it's a lie (status quo)

b) those algorithms can't be used on immutable (“let bound”) multipass
   sequences. IMO that would be totally unacceptable.

If we drop the refinement, we can have a saner world. We also don't
need to separate Sequence and Iterator anymore. We can simply drop
Sequence altogether, and the protocol for single-pass iteration becomes
Iterator.

### Mutation and Reference Semantics

Everything in Swift is copiable via `let copy = thing` (let's please not
argue over the definition of copy for classes; this is the one built
into the lowest level of the language—I refer to the other one, that
requires allocation, as “clone”).

Anything you do with a sequence that's truly single-pass mutates the
sequence *and of its copies*. Therefore, such a type *fundamentally*
has reference semantics. One day we may be able to model single-pass
sequences with “move-only” value types, which cannot be copied. You can
find move-only types in languages like Rust and C++, but they are not
supported by Swift today. So it seems reasonable that all Iterators in
Swift today should be modeled as classes.

The fact that Swift doesn't have a mutation model for classes, though,
means that mutating methods on a class constrained protocol can't be
labeled as such. So consuming operations on a class-constrained
Iterator protocol would not be labeled as mutating.

The standard library team is currently trying to evaluate the tradeoffs
in this area. One possibility under consideration is simply dropping
support for single-pass sequences until Swift can support move-only
value types and/or gets a mutation model for class instances. It would
be very interesting to know about any real-world models of single-pass
sequences that people are using in Swift, since we don't supply any in
the standard library.

--
Dave
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Would it not make more sense for sequences that can benefit from subscripts to just conform to Indexable? It seems like having basically all of the Collection-specific methods on Sequence too would be confusing, and dilute any nothing that these are sequences where elements are supposed to be consumed in-order, as it would give the illusion that you can skip around with the convenience of an array.

There's also the issue of how you would even index something that's potentially infinite; you'd need to use a big integer in which case you could end up with your indices growing to infinite size over time? It may not be very obvious that this could be the case.

···

On 30 Jun 2016, at 18:26, Dave Abrahams <dabrahams@apple.com> wrote:

Q: Why should there be indices on an infinite multipass sequence?
A: Because the operations on indices apply equally well whether the
  sequence is finite or not. Find the index of a value in the
  sequence, slice the sequence, find again, etc.

Swift is a language that embraces value semantics. Many common
iterators *can* be implemented with value semantics. Just because we
can’t implement *all* iterators with value semantics doesn’t mean we
should require them to have reference semantics. It just means you
can’t *assume* value semantics when working with iterators in generic
code unless / until we have a way to specify a value semantics
constraint. That’s not necessarily a bad thing especially when it
leaves the door open to interesting future possibilities.

-Matthew

I'm kind of undecided about this personally. I think one of the
problems with Swift is that the only indication that you have a
reference type is that you can declare it as a constant, yet still
call mutating methods upon it, this isn't a very positive way of
identifying it however. This may be more of a GUI/IDE issue though, in
that something being a class isn't always that obvious at a glance.

I wonder, could we somehow force iterators stored in variables to be
passed via inout? This would make it pretty clear that you're using
the same iterator and not a copy in all cases, encouraging you to
obtain another if you really do need to perform multiple passes.

I'm going to push single-pass iteration on the stack briefly and talk
about the topic that's been under discussion here: infinite multipass
sequences.

## Fitting “Infinite Multipass” Into the Model

It remains to be decided whether it's worth doing, but if it's to
happen

I definitely think it’s worth doing. I really appreciate the attention that the library team has given to this.

, the standard library team thinks the right design is roughly
this:

/// A multipass sequence that may be infinite
protocol Collection {

   // Only eager algorithms that can terminate available here
   func index(where predicate: (Element)->Bool) -> Index

   // all lazy algorithms available here
   var lazy: ...

   var startIndex: Index
   var endIndex: Index // possibly not reachable from startIndex

   associatedtype SubSequence : Collection
   // do we need an associated FiniteSubsequence, e.g. for prefixes?
}

protocol FiniteCollection : Collection {

   // All eager algorithms available here
   func map(...) ->
   var count: ...
}

protocol BidirectionalCollection : Collection { ... }

protocol RandomAccessCollection : BidirectionalCollection { … }

Does this design entirely break with the relationship between collections and iterators (dropping `makeIterator` as a protocol requirement)?

If so, would for..in (over collections) be built on top of indices and use `formIndex(after:)`? Would it require a finite collection (unless we add `until` to the language and then allow `for..in..until` to work with infinite collections)?

Would we still retain `IndexingIterator`even if we break the relationship in the protocol requirements?

Would it still be possible to do things like zip a multi-pass sequence with a single-pass sequence (assuming we keep single-pass sequences or add them back eventually)? This seems like a use case worth supporting in some way.

One subtle change I think this implies is that things like `LazyFilterSequence` can implement `makeIterator` with constant complexity, deferring the O(N) complexity to the first call to `next`. `startIndex` for `LazyFilterCollection` currently has O(N) complexity. The complexity of a complete iteration doesn’t change and probably isn’t a big deal, but it’s worth noting.

I’ve been looking at some code that wraps a sequence and considering how it would be impacted. With iterators it looks like this:

guard let element = base.next()
  else { return nil }

With collections and indices it would look something like this:

base.formIndex(after: &index)
guard index != baseEndIndex
   else { return endIndex }

let element = base[index]

That’s not too bad but it is more verbose. If we’re going to push people towards collections and indices we should try to make common patterns like “update the iteration state and return the next element if there is one" simpler. This could be accomplished with an extension method along these lines:

guard let element = base.formIndex(after: &index, .returningOptionalElement)
    else { return endIndex }

With an implementation something like:

enum FormIndexResult {
    .returningOptionalElement
}
extension Collection {
    func formIndex(after i: inout Self.Index, _ result: FormIndexResult) -> Self.Element?
}

This would provide similar functionality to `IndexingIterator` without coupling the storage of `elements` and `position` (which is important if you’re wrapping a collection and need to wrap the collection and its indices independently).

Q: Why should there be indices on an infinite multipass sequence?
A: Because the operations on indices apply equally well whether the
  sequence is finite or not. Find the index of a value in the
  sequence, slice the sequence, find again, etc.

Q: Why is there an endIndex on an infinite seque?
A: So you can write algorithms such as index(where:) once.

Q: Why not allow endIndex to have a different type from startIndex?
A: It appears to offer insufficient benefit for the associated
  complexity in typical usage. A classic use case that argues for a
  different endIndex type is the null-terminated C string. But you
  can't index one of those safely without actually counting the length,
  and once you've done that you can make the endIndex an Int.

It’s also worth nothing that we can use `Optional` with `nil` as the `endIndex` sentinel if necessary.

## Single Pass Iteration

The refinement relationship between Sequence and Collection is
problematic, because it means either:

a) algorithms such as map on single-pass sequences claim to be
  nonmutating even though it's a lie (status quo)

b) those algorithms can't be used on immutable (“let bound”) multipass
  sequences. IMO that would be totally unacceptable.

If we drop the refinement, we can have a saner world. We also don't
need to separate Sequence and Iterator anymore. We can simply drop
Sequence altogether, and the protocol for single-pass iteration becomes
Iterator.

Makes sense to me.

### Mutation and Reference Semantics

Everything in Swift is copiable via `let copy = thing` (let's please not
argue over the definition of copy for classes; this is the one built
into the lowest level of the language—I refer to the other one, that
requires allocation, as “clone”).

Anything you do with a sequence that's truly single-pass mutates the
sequence *and of its copies*. Therefore, such a type *fundamentally*
has reference semantics. One day we may be able to model single-pass
sequences with “move-only” value types, which cannot be copied. You can
find move-only types in languages like Rust and C++, but they are not
supported by Swift today. So it seems reasonable that all Iterators in
Swift today should be modeled as classes.

I think this makes a lot of sense in the model you are proposing. All multipass structures are collections. Any sequence that can only support a single pass is modeled as an iterator which inherently has identity. Making this distinction strong will prevent any confusion.

The fact that Swift doesn't have a mutation model for classes, though,
means that mutating methods on a class constrained protocol can't be
labeled as such. So consuming operations on a class-constrained
Iterator protocol would not be labeled as mutating.

The standard library team is currently trying to evaluate the tradeoffs
in this area. One possibility under consideration is simply dropping
support for single-pass sequences until Swift can support move-only
value types and/or gets a mutation model for class instances. It would
be very interesting to know about any real-world models of single-pass
sequences that people are using in Swift, since we don't supply any in
the standard library.

I’m happy to see you mention a mutation model for class instances! (I don’t mean to sidetrack the discussion, but would love to see that someday)

I don’t have any objection to dropping support for single-pass sequences temporarily. It’s possible that I would feel differently if I was making use of them in my own code but I’m not.

In the meantime, people would be able to implement their own protocol for single pass sequences. What they would lose is for..in as well as the standard library algorithms. I’m not sure how many people this would impact or how big the impact would be for them. We have seen a couple of examples in this discussion, but probably not enough to asses the overall impact.

One thing you don’t mention here is a distinction between finite and infinite single-pass sequences (iterators). I don’t know if the finite / infinite distinction is as important here, but wanted to point it out. Obviously if we remove support single-pass sequences now we could defer that discussion until we’re ready to bring back support for them.

-Matthew

···

On Jun 30, 2016, at 12:26 PM, Dave Abrahams <dabrahams@apple.com> wrote:
on Wed Jun 29 2016, Haravikk <swift-evolution-AT-haravikk.me> wrote:

On 29 Jun 2016, at 00:10, Matthew Johnson via swift-evolution <swift-evolution@swift.org> wrote:

--
Dave

If Iterators become reference types that model single-pass sequences and
becomes for-in-able, as the write-up suggests, couldn't Sequence be
stipulated to be multipass and retain its refinement relationship with
Collection?

AFAIK there is no interesting multipass Sequence that cannot reasonably be
made to support indexing.

There *is* existing code that exposes multipass data structures without
exposing the ability to compare iteration state for equality. In every
case I can think of, index equality could easily have been exposed, but
wasn't.These designs can't be adapted to model Collection.

Those designs are real, but I am unconvinced they are worth supporting
directly with a separate protocol in the standard library; I'm willing
to accept the idea that those data structures will simply be limited to
modeling Iterator.

···

on Thu Jun 30 2016, Xiaodi Wu <xiaodi.wu-AT-gmail.com> wrote:

On Thu, Jun 30, 2016 at 12:26 Dave Abrahams via swift-evolution < > swift-evolution@swift.org> wrote:

on Wed Jun 29 2016, Haravikk <swift-evolution-AT-haravikk.me> wrote:

>> On 29 Jun 2016, at 00:10, Matthew Johnson via swift-evolution < >> swift-evolution@swift.org> wrote:
>>
>> Swift is a language that embraces value semantics. Many common
>> iterators *can* be implemented with value semantics. Just because we
>> can’t implement *all* iterators with value semantics doesn’t mean we
>> should require them to have reference semantics. It just means you
>> can’t *assume* value semantics when working with iterators in generic
>> code unless / until we have a way to specify a value semantics
>> constraint. That’s not necessarily a bad thing especially when it
>> leaves the door open to interesting future possibilities.
>>
>> -Matthew
>
> I'm kind of undecided about this personally. I think one of the
> problems with Swift is that the only indication that you have a
> reference type is that you can declare it as a constant, yet still
> call mutating methods upon it, this isn't a very positive way of
> identifying it however. This may be more of a GUI/IDE issue though, in
> that something being a class isn't always that obvious at a glance.
>
> I wonder, could we somehow force iterators stored in variables to be
> passed via inout? This would make it pretty clear that you're using
> the same iterator and not a copy in all cases, encouraging you to
> obtain another if you really do need to perform multiple passes.

I'm going to push single-pass iteration on the stack briefly and talk
about the topic that's been under discussion here: infinite multipass
sequences.

## Fitting “Infinite Multipass” Into the Model

It remains to be decided whether it's worth doing, but if it's to
happen, the standard library team thinks the right design is roughly
this:

  /// A multipass sequence that may be infinite
  protocol Collection {

    // Only eager algorithms that can terminate available here
    func index(where predicate: (Element)->Bool) -> Index

    // all lazy algorithms available here
    var lazy: ...

    var startIndex: Index
    var endIndex: Index // possibly not reachable from startIndex

    associatedtype SubSequence : Collection
    // do we need an associated FiniteSubsequence, e.g. for prefixes?
  }

  protocol FiniteCollection : Collection {

    // All eager algorithms available here
    func map(...) ->
    var count: ...
  }

  protocol BidirectionalCollection : Collection { ... }

  protocol RandomAccessCollection : BidirectionalCollection { ... }

Q: Why should there be indices on an infinite multipass sequence?
A: Because the operations on indices apply equally well whether the
   sequence is finite or not. Find the index of a value in the
   sequence, slice the sequence, find again, etc.

Q: Why is there an endIndex on an infinite seque?
A: So you can write algorithms such as index(where:) once.

Q: Why not allow endIndex to have a different type from startIndex?
A: It appears to offer insufficient benefit for the associated
   complexity in typical usage. A classic use case that argues for a
   different endIndex type is the null-terminated C string. But you
   can't index one of those safely without actually counting the length,
   and once you've done that you can make the endIndex an Int.

## Single Pass Iteration

The refinement relationship between Sequence and Collection is
problematic, because it means either:

a) algorithms such as map on single-pass sequences claim to be
   nonmutating even though it's a lie (status quo)

b) those algorithms can't be used on immutable (“let bound”) multipass
   sequences. IMO that would be totally unacceptable.

If we drop the refinement, we can have a saner world. We also don't
need to separate Sequence and Iterator anymore. We can simply drop
Sequence altogether, and the protocol for single-pass iteration becomes
Iterator.

### Mutation and Reference Semantics

Everything in Swift is copiable via `let copy = thing` (let's please not
argue over the definition of copy for classes; this is the one built
into the lowest level of the language—I refer to the other one, that
requires allocation, as “clone”).

Anything you do with a sequence that's truly single-pass mutates the
sequence *and of its copies*. Therefore, such a type *fundamentally*
has reference semantics. One day we may be able to model single-pass
sequences with “move-only” value types, which cannot be copied. You can
find move-only types in languages like Rust and C++, but they are not
supported by Swift today. So it seems reasonable that all Iterators in
Swift today should be modeled as classes.

The fact that Swift doesn't have a mutation model for classes, though,
means that mutating methods on a class constrained protocol can't be
labeled as such. So consuming operations on a class-constrained
Iterator protocol would not be labeled as mutating.

The standard library team is currently trying to evaluate the tradeoffs
in this area. One possibility under consideration is simply dropping
support for single-pass sequences until Swift can support move-only
value types and/or gets a mutation model for class instances. It would
be very interesting to know about any real-world models of single-pass
sequences that people are using in Swift, since we don't supply any in
the standard library.

--
Dave
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

--
Dave

It would
be very interesting to know about any real-world models of single-pass
sequences that people are using in Swift, since we don't supply any in
the standard library.

--
Dave

I already gave an example of this earlier in the thread:

Yes, I remember that one. It's not that I believe these things don't
exist at all; I'm trying to get a read on how important it is that they
fit into the standard library's protocol framework. For that, I need to
know if they are very common.

I use it in a LazyRowSequence<T: SqlModelConvertible> where
querying Sqlite in WAL mode allows multiple concurrent readers to
get point-in-time snapshots of the database. The query can’t be
replayed without buffering all the rows in memory because Sqlite’s
step functions are not bi-directional. In some cases we are talking
about tens of thousands of rows (or even hundreds of thousands) and
the ability to avoid buffering them is a feature, not a bug.

IMHO the typical case for single-pass is IO.

Yes. Also, truly-random number generators.

In this case it would work just as well as LazyRowIterator<T> assuming
the language allows for-in on an iterator.

So, you're not interested in using algorithms like map and filter on
these things?

If it was just about for-in, we could say you can for-in over any
instance of

   ()->T?

for example.

···

on Thu Jun 30 2016, Russ Bishop <xenadu-AT-gmail.com> wrote:

On Jun 30, 2016, at 10:26 AM, Dave Abrahams via swift-evolution <swift-evolution@swift.org> wrote:

--
Dave

Q: Why should there be indices on an infinite multipass sequence?
A: Because the operations on indices apply equally well whether the
  sequence is finite or not. Find the index of a value in the
  sequence, slice the sequence, find again, etc.

Would it not make more sense for sequences that can benefit from
subscripts to just conform to Indexable?

All multi-pass sequences can benefit from subscripts.

It seems like having basically all of the Collection-specific methods
on Sequence too would be confusing, and dilute any nothing that these
are sequences where elements are supposed to be consumed in-order, as
it would give the illusion that you can skip around with the
convenience of an array.

If traversal consumes the sequence, it is a single-pass thing.

There's also the issue of how you would even index something that's
potentially infinite; you'd need to use a big integer in which case
you could end up with your indices growing to infinite size over time?

It's trivial; the index contains the iteration state. The only
fundamental difference between the constraints on Iterator and the
constraints on an Index is that Iterator doesn't support comparison for
equality.

···

on Thu Jun 30 2016, Haravikk <swift-evolution-AT-haravikk.me> wrote:

On 30 Jun 2016, at 18:26, Dave Abrahams <dabrahams@apple.com> wrote:

--
Dave

All multi-pass sequences can benefit from subscripts.

Sorry, not really what I meant, but rather; how many sequences are really going to use them?

It's trivial; the index contains the iteration state. The only
fundamental difference between the constraints on Iterator and the
constraints on an Index is that Iterator doesn't support comparison for
equality.

Won't this make implementing sequences more complex? Sequences are currently dead easy to implement, and to implement in an ad-hoc way via AnySequence(body:), how would that be done under this required indexing scheme? I just feel like things are easiest if Sequence is simply tweaked to require that it's iterators must be non-destructive, as implementing them will be just as easy, at which point it's just a matter of separating out which methods take Sequences and which take Iterators.

···

On 30 Jun 2016, at 23:39, Dave Abrahams <dabrahams@apple.com> wrote:

Swift is a language that embraces value semantics. Many common
iterators *can* be implemented with value semantics. Just because we
can’t implement *all* iterators with value semantics doesn’t mean we
should require them to have reference semantics. It just means you
can’t *assume* value semantics when working with iterators in generic
code unless / until we have a way to specify a value semantics
constraint. That’s not necessarily a bad thing especially when it
leaves the door open to interesting future possibilities.

-Matthew

I'm kind of undecided about this personally. I think one of the
problems with Swift is that the only indication that you have a
reference type is that you can declare it as a constant, yet still
call mutating methods upon it, this isn't a very positive way of
identifying it however. This may be more of a GUI/IDE issue though, in
that something being a class isn't always that obvious at a glance.

I wonder, could we somehow force iterators stored in variables to be
passed via inout? This would make it pretty clear that you're using
the same iterator and not a copy in all cases, encouraging you to
obtain another if you really do need to perform multiple passes.

I'm going to push single-pass iteration on the stack briefly and talk
about the topic that's been under discussion here: infinite multipass
sequences.

## Fitting “Infinite Multipass” Into the Model

It remains to be decided whether it's worth doing, but if it's to
happen

I definitely think it’s worth doing.

Opinions are nice, but rationales are better. How will we understand
*why* it's worth doing?

I really appreciate the attention that the library team has given to
this.

, the standard library team thinks the right design is roughly
this:

/// A multipass sequence that may be infinite
protocol Collection {

   // Only eager algorithms that can terminate available here
   func index(where predicate: (Element)->Bool) -> Index

   // all lazy algorithms available here
   var lazy: ...

   var startIndex: Index
   var endIndex: Index // possibly not reachable from startIndex

   associatedtype SubSequence : Collection
   // do we need an associated FiniteSubsequence, e.g. for prefixes?
}

protocol FiniteCollection : Collection {

   // All eager algorithms available here
   func map(...) ->
   var count: ...
}

protocol BidirectionalCollection : Collection { ... }

protocol RandomAccessCollection : BidirectionalCollection { … }

Does this design entirely break with the relationship between
collections and iterators (dropping `makeIterator` as a protocol
requirement)? If so, would for..in (over collections) be built on top
of indices and use `formIndex(after:)`? Would it require a finite
collection (unless we add `until` to the language and then allow
`for..in..until` to work with infinite collections)?

All of these points are up for discussion. John McCall pointed out to
me that an index-based for..in would make it possible to implement

  for inout x in y { mutate(&x) }

Would we still retain `IndexingIterator`even if we break the
relationship in the protocol requirements?

Yes: it should be possible to implement Collection algorithms in terms
of Iterator algorithms, and IndexingIterator provides the means. That
said, I think the makeIterator requirement does little harm, especially
when it can be defaulted for Collections.

Would it still be possible to do things like zip a multi-pass sequence
with a single-pass sequence (assuming we keep single-pass sequences or
add them back eventually)? This seems like a use case worth
supporting in some way.

Yes. If you can create an Iterator from a Collection, and you can zip
Iterators, you can do this.

One subtle change I think this implies is that things like
`LazyFilterSequence` can implement `makeIterator` with constant
complexity, deferring the O(N) complexity to the first call to `next`.

I don't believe that's a difference, though I could be wrong.

`startIndex` for `LazyFilterCollection` currently has O(N) complexity.
The complexity of a complete iteration doesn’t change and probably
isn’t a big deal, but it’s worth noting.

Filtered collection views always require a bit of hand-waving around
performance guarantees; I don't think that changes.

I’ve been looking at some code that wraps a sequence and considering
how it would be impacted. With iterators it looks like this:

guard let element = base.next()
  else { return nil }

With collections and indices it would look something like this:

base.formIndex(after: &index)
guard index != baseEndIndex
   else { return endIndex }

let element = base[index]

That’s not too bad but it is more verbose.

Sequence today is a single-pass thing. If you are wrapping Sequence
today presumably you'd wrap an Iterator tomorrow, and you wouldn't have
to deal with indices.

If we’re going to push people towards collections and indices we
should try to make common patterns like “update the iteration state
and return the next element if there is one" simpler.

That's IndexingIterator.

This could be accomplished with an extension method along these lines:

guard let element = base.formIndex(after: &index,
.returningOptionalElement)
    else { return endIndex }

With an implementation something like:

enum FormIndexResult {
    .returningOptionalElement
}
extension Collection {
    func formIndex(after i: inout Self.Index, _ result:
FormIndexResult) -> Self.Element?
}

This would provide similar functionality to `IndexingIterator` without
coupling the storage of `elements` and `position` (which is important
if you’re wrapping a collection and need to wrap the collection and
its indices independently).

I'm afraid I don't understand. Could you be more explicit about what
you have in mind?

Q: Why should there be indices on an infinite multipass sequence?
A: Because the operations on indices apply equally well whether the
  sequence is finite or not. Find the index of a value in the
  sequence, slice the sequence, find again, etc.

Q: Why is there an endIndex on an infinite seque?
A: So you can write algorithms such as index(where:) once.

Q: Why not allow endIndex to have a different type from startIndex?

A: It appears to offer insufficient benefit for the associated
   complexity in typical usage. A classic use case that argues for a
   different endIndex type is the null-terminated C string. But you
   can't index one of those safely without actually counting the
   length,
   and once you've done that you can make the endIndex an Int.

It’s also worth nothing that we can use `Optional` with `nil` as the
`endIndex` sentinel if necessary.

True, that's a useful technique when there's no underlying storage in
the collection (e.g. a fibonacci sequence)

## Single Pass Iteration

The refinement relationship between Sequence and Collection is
problematic, because it means either:

a) algorithms such as map on single-pass sequences claim to be
  nonmutating even though it's a lie (status quo)

b) those algorithms can't be used on immutable (“let bound”)
  multipass sequences. IMO that would be totally unacceptable.

If we drop the refinement, we can have a saner world. We also don't
need to separate Sequence and Iterator anymore. We can simply drop
Sequence altogether, and the protocol for single-pass iteration
becomes Iterator.

Makes sense to me.

### Mutation and Reference Semantics

Everything in Swift is copiable via `let copy = thing` (let's please
not argue over the definition of copy for classes; this is the one
built into the lowest level of the language—I refer to the other one,
that requires allocation, as “clone”).

Anything you do with a sequence that's truly single-pass mutates the
sequence *and of its copies*. Therefore, such a type *fundamentally*
has reference semantics. One day we may be able to model single-pass
sequences with “move-only” value types, which cannot be copied. You
can find move-only types in languages like Rust and C++, but they are
not supported by Swift today. So it seems reasonable that all
Iterators in Swift today should be modeled as classes.

I think this makes a lot of sense in the model you are proposing. All
multipass structures are collections. Any sequence that can only
support a single pass is modeled as an iterator which inherently has
identity. Making this distinction strong will prevent any confusion.

The fact that Swift doesn't have a mutation model for classes,
though, means that mutating methods on a class constrained protocol
can't be labeled as such. So consuming operations on a
class-constrained Iterator protocol would not be labeled as mutating.

The standard library team is currently trying to evaluate the
tradeoffs in this area. One possibility under consideration is
simply dropping support for single-pass sequences until Swift can
support move-only value types and/or gets a mutation model for class
instances. It would be very interesting to know about any real-world
models of single-pass sequences that people are using in Swift, since
we don't supply any in the standard library.

I’m happy to see you mention a mutation model for class instances! (I
don’t mean to sidetrack the discussion, but would love to see that
someday)

I don’t have any objection to dropping support for single-pass
sequences temporarily. It’s possible that I would feel differently if
I was making use of them in my own code but I’m not.

On second thought, I believe it is important to have a way to support
existing “partially formed” multipass sequences that don't expose
copying or equality for their iteration states. Iterator is the right
way to do that. So I think we need to keep Iterator around.

In the meantime, people would be able to implement their own protocol
for single pass sequences. What they would lose is for..in as well as
the standard library algorithms. I’m not sure how many people this
would impact or how big the impact would be for them. We have seen a
couple of examples in this discussion, but probably not enough to
asses the overall impact.

One thing you don’t mention here is a distinction between finite and
infinite single-pass sequences (iterators). I don’t know if the
finite / infinite distinction is as important here, but wanted to
point it out. Obviously if we remove support single-pass sequences
now we could defer that discussion until we’re ready to bring back
support for them.

There are a few possible answers I can think of:

1. Do the “obvious” thing and create a separate protocol for finite
   single-pass sequences

2. Decide that the combination of infinite and single-pass is rare
   enough (/dev/urandom, temperature sensor) that it's better to just
   ask people handling them to be careful and not, e.g., try to “count”
   them.

3. Decide that everything on a single-pass sequence is lazy. Since you
   can only take a single pass anyway, people won't observe their
   closures being called more often than necessary, which was the main
   motivator for making map, filter, et. al eager on collections without
   an explicit .lazy.

Implications of #3:

* Any “partially-formed” multipass sequences (modeling only Iterator)
  would be free to expose an accurate underestimatedCount, thereby
  optimizing the process of copying into an array. The lazy filter
  Iterator adaptor would have an underestimatedCount of 0.

* All algorithms that require multiple passes, such as sorted(), would
  be unavailable on Iterator. You'd have to construct an Array (or
  other MutableCollection) and sort that in-place. Of course,
  constructing an Array from an Iterator could still go on forever if
  the Iterator turned out to be infinite, which means, at some level #3
  is just a refinement of #2 that makes it less error-prone.

···

on Fri Jul 01 2016, Matthew Johnson <matthew-AT-anandabits.com> wrote:

On Jun 30, 2016, at 12:26 PM, Dave Abrahams <dabrahams@apple.com> > wrote:
on Wed Jun 29 2016, Haravikk <swift-evolution-AT-haravikk.me> wrote:

On 29 Jun 2016, at 00:10, Matthew Johnson via swift-evolution <swift-evolution@swift.org> wrote:

--
Dave

If Iterators become reference types that model single-pass sequences and
becomes for-in-able, as the write-up suggests, couldn't Sequence be
stipulated to be multipass and retain its refinement relationship with
Collection?

AFAIK there is no interesting multipass Sequence that cannot reasonably be
made to support indexing.

There *is* existing code that exposes multipass data structures without
exposing the ability to compare iteration state for equality.

It’s worth noting that indices require comparability, not just equality. I think comparability might cause more difficulty than equality (but haven’t thought too hard about it).

In every
case I can think of, index equality could easily have been exposed, but
wasn't.These designs can't be adapted to model Collection.

Why can’t they be adapted to model Collection if equality could have been exposed? Is it because comparability would be difficult?

Those designs are real, but I am unconvinced they are worth supporting
directly with a separate protocol in the standard library; I'm willing
to accept the idea that those data structures will simply be limited to
modeling Iterator.

Can you elaborate on what designs / data structures you’re talking about here?

···

On Jun 30, 2016, at 5:32 PM, Dave Abrahams via swift-evolution <swift-evolution@swift.org> wrote:
on Thu Jun 30 2016, Xiaodi Wu <xiaodi.wu-AT-gmail.com <http://xiaodi.wu-at-gmail.com/&gt;&gt; wrote:

On Thu, Jun 30, 2016 at 12:26 Dave Abrahams via swift-evolution < >> swift-evolution@swift.org> wrote:

on Wed Jun 29 2016, Haravikk <swift-evolution-AT-haravikk.me> wrote:

On 29 Jun 2016, at 00:10, Matthew Johnson via swift-evolution < >>> swift-evolution@swift.org> wrote:

Swift is a language that embraces value semantics. Many common
iterators *can* be implemented with value semantics. Just because we
can’t implement *all* iterators with value semantics doesn’t mean we
should require them to have reference semantics. It just means you
can’t *assume* value semantics when working with iterators in generic
code unless / until we have a way to specify a value semantics
constraint. That’s not necessarily a bad thing especially when it
leaves the door open to interesting future possibilities.

-Matthew

I'm kind of undecided about this personally. I think one of the
problems with Swift is that the only indication that you have a
reference type is that you can declare it as a constant, yet still
call mutating methods upon it, this isn't a very positive way of
identifying it however. This may be more of a GUI/IDE issue though, in
that something being a class isn't always that obvious at a glance.

I wonder, could we somehow force iterators stored in variables to be
passed via inout? This would make it pretty clear that you're using
the same iterator and not a copy in all cases, encouraging you to
obtain another if you really do need to perform multiple passes.

I'm going to push single-pass iteration on the stack briefly and talk
about the topic that's been under discussion here: infinite multipass
sequences.

## Fitting “Infinite Multipass” Into the Model

It remains to be decided whether it's worth doing, but if it's to
happen, the standard library team thinks the right design is roughly
this:

/// A multipass sequence that may be infinite
protocol Collection {

   // Only eager algorithms that can terminate available here
   func index(where predicate: (Element)->Bool) -> Index

   // all lazy algorithms available here
   var lazy: ...

   var startIndex: Index
   var endIndex: Index // possibly not reachable from startIndex

   associatedtype SubSequence : Collection
   // do we need an associated FiniteSubsequence, e.g. for prefixes?
}

protocol FiniteCollection : Collection {

   // All eager algorithms available here
   func map(...) ->
   var count: ...
}

protocol BidirectionalCollection : Collection { ... }

protocol RandomAccessCollection : BidirectionalCollection { ... }

Q: Why should there be indices on an infinite multipass sequence?
A: Because the operations on indices apply equally well whether the
  sequence is finite or not. Find the index of a value in the
  sequence, slice the sequence, find again, etc.

Q: Why is there an endIndex on an infinite seque?
A: So you can write algorithms such as index(where:) once.

Q: Why not allow endIndex to have a different type from startIndex?
A: It appears to offer insufficient benefit for the associated
  complexity in typical usage. A classic use case that argues for a
  different endIndex type is the null-terminated C string. But you
  can't index one of those safely without actually counting the length,
  and once you've done that you can make the endIndex an Int.

## Single Pass Iteration

The refinement relationship between Sequence and Collection is
problematic, because it means either:

a) algorithms such as map on single-pass sequences claim to be
  nonmutating even though it's a lie (status quo)

b) those algorithms can't be used on immutable (“let bound”) multipass
  sequences. IMO that would be totally unacceptable.

If we drop the refinement, we can have a saner world. We also don't
need to separate Sequence and Iterator anymore. We can simply drop
Sequence altogether, and the protocol for single-pass iteration becomes
Iterator.

### Mutation and Reference Semantics

Everything in Swift is copiable via `let copy = thing` (let's please not
argue over the definition of copy for classes; this is the one built
into the lowest level of the language—I refer to the other one, that
requires allocation, as “clone”).

Anything you do with a sequence that's truly single-pass mutates the
sequence *and of its copies*. Therefore, such a type *fundamentally*
has reference semantics. One day we may be able to model single-pass
sequences with “move-only” value types, which cannot be copied. You can
find move-only types in languages like Rust and C++, but they are not
supported by Swift today. So it seems reasonable that all Iterators in
Swift today should be modeled as classes.

The fact that Swift doesn't have a mutation model for classes, though,
means that mutating methods on a class constrained protocol can't be
labeled as such. So consuming operations on a class-constrained
Iterator protocol would not be labeled as mutating.

The standard library team is currently trying to evaluate the tradeoffs
in this area. One possibility under consideration is simply dropping
support for single-pass sequences until Swift can support move-only
value types and/or gets a mutation model for class instances. It would
be very interesting to know about any real-world models of single-pass
sequences that people are using in Swift, since we don't supply any in
the standard library.

--
Dave
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

--
Dave
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

Q: Why should there be indices on an infinite multipass sequence?
A: Because the operations on indices apply equally well whether the
sequence is finite or not. Find the index of a value in the
sequence, slice the sequence, find again, etc.

Would it not make more sense for sequences that can benefit from
subscripts to just conform to Indexable?

All multi-pass sequences can benefit from subscripts.

It seems like having basically all of the Collection-specific methods
on Sequence too would be confusing, and dilute any nothing that these
are sequences where elements are supposed to be consumed in-order, as
it would give the illusion that you can skip around with the
convenience of an array.

If traversal consumes the sequence, it is a single-pass thing.

It’s also worth noting that Collection does not imply the ability to skip around “with the convenience of an array” if that means skipping around in constant time. You need RandomAccessCollection to get that guarantee.

There's also the issue of how you would even index something that's
potentially infinite; you'd need to use a big integer in which case
you could end up with your indices growing to infinite size over time?

It's trivial; the index contains the iteration state. The only
fundamental difference between the constraints on Iterator and the
constraints on an Index is that Iterator doesn't support comparison for
equality.

Not just equality, but also general `Comparable` (you’re not considering changing that are you?).

···

On Jun 30, 2016, at 5:39 PM, Dave Abrahams <dabrahams@apple.com> wrote:
on Thu Jun 30 2016, Haravikk <swift-evolution-AT-haravikk.me> wrote:

On 30 Jun 2016, at 18:26, Dave Abrahams <dabrahams@apple.com> wrote:

--
Dave

>>
>>
>>
>
>>>>
>>>> Swift is a language that embraces value semantics. Many common
>>>> iterators *can* be implemented with value semantics. Just because we
>>>> can’t implement *all* iterators with value semantics doesn’t mean we
>>>> should require them to have reference semantics. It just means you
>>>> can’t *assume* value semantics when working with iterators in generic
>>>> code unless / until we have a way to specify a value semantics
>>>> constraint. That’s not necessarily a bad thing especially when it
>>>> leaves the door open to interesting future possibilities.
>>>>
>>>> -Matthew
>>>
>>> I'm kind of undecided about this personally. I think one of the
>>> problems with Swift is that the only indication that you have a
>>> reference type is that you can declare it as a constant, yet still
>>> call mutating methods upon it, this isn't a very positive way of
>>> identifying it however. This may be more of a GUI/IDE issue though, in
>>> that something being a class isn't always that obvious at a glance.
>>>
>>> I wonder, could we somehow force iterators stored in variables to be
>>> passed via inout? This would make it pretty clear that you're using
>>> the same iterator and not a copy in all cases, encouraging you to
>>> obtain another if you really do need to perform multiple passes.
>>
>> I'm going to push single-pass iteration on the stack briefly and talk
>> about the topic that's been under discussion here: infinite multipass
>> sequences.
>>
>> ## Fitting “Infinite Multipass” Into the Model
>>
>> It remains to be decided whether it's worth doing, but if it's to
>> happen
>
> I definitely think it’s worth doing.

Opinions are nice, but rationales are better. How will we understand
*why* it's worth doing?

> I really appreciate the attention that the library team has given to
> this.
>
>> , the standard library team thinks the right design is roughly
>> this:
>>
>> /// A multipass sequence that may be infinite
>> protocol Collection {
>>
>> // Only eager algorithms that can terminate available here
>> func index(where predicate: (Element)->Bool) -> Index
>>
>> // all lazy algorithms available here
>> var lazy: ...
>>
>> var startIndex: Index
>> var endIndex: Index // possibly not reachable from startIndex
>>
>> associatedtype SubSequence : Collection
>> // do we need an associated FiniteSubsequence, e.g. for prefixes?
>> }
>>
>> protocol FiniteCollection : Collection {
>>
>> // All eager algorithms available here
>> func map(...) ->
>> var count: ...
>> }
>>
>> protocol BidirectionalCollection : Collection { ... }
>>
>> protocol RandomAccessCollection : BidirectionalCollection { … }
>
> Does this design entirely break with the relationship between
> collections and iterators (dropping `makeIterator` as a protocol
> requirement)? If so, would for..in (over collections) be built on top
> of indices and use `formIndex(after:)`? Would it require a finite
> collection (unless we add `until` to the language and then allow
> `for..in..until` to work with infinite collections)?

All of these points are up for discussion. John McCall pointed out to
me that an index-based for..in would make it possible to implement

  for inout x in y { mutate(&x) }

> Would we still retain `IndexingIterator`even if we break the
> relationship in the protocol requirements?

Yes: it should be possible to implement Collection algorithms in terms
of Iterator algorithms, and IndexingIterator provides the means. That
said, I think the makeIterator requirement does little harm, especially
when it can be defaulted for Collections.

> Would it still be possible to do things like zip a multi-pass sequence
> with a single-pass sequence (assuming we keep single-pass sequences or
> add them back eventually)? This seems like a use case worth
> supporting in some way.

Yes. If you can create an Iterator from a Collection, and you can zip
Iterators, you can do this.

> One subtle change I think this implies is that things like
> `LazyFilterSequence` can implement `makeIterator` with constant
> complexity, deferring the O(N) complexity to the first call to `next`.

I don't believe that's a difference, though I could be wrong.

> `startIndex` for `LazyFilterCollection` currently has O(N) complexity.
> The complexity of a complete iteration doesn’t change and probably
> isn’t a big deal, but it’s worth noting.

Filtered collection views always require a bit of hand-waving around
performance guarantees; I don't think that changes.

> I’ve been looking at some code that wraps a sequence and considering
> how it would be impacted. With iterators it looks like this:
>
> guard let element = base.next()
> else { return nil }
>
> With collections and indices it would look something like this:
>
> base.formIndex(after: &index)
> guard index != baseEndIndex
> else { return endIndex }
>
> let element = base[index]
>
> That’s not too bad but it is more verbose.

Sequence today is a single-pass thing. If you are wrapping Sequence
today presumably you'd wrap an Iterator tomorrow, and you wouldn't have
to deal with indices.

> If we’re going to push people towards collections and indices we
> should try to make common patterns like “update the iteration state
> and return the next element if there is one" simpler.

That's IndexingIterator.

> This could be accomplished with an extension method along these lines:
>
> guard let element = base.formIndex(after: &index,
> .returningOptionalElement)
> else { return endIndex }
>
> With an implementation something like:
>
> enum FormIndexResult {
> .returningOptionalElement
> }
> extension Collection {
> func formIndex(after i: inout Self.Index, _ result:
> FormIndexResult) -> Self.Element?
> }
>
> This would provide similar functionality to `IndexingIterator` without
> coupling the storage of `elements` and `position` (which is important
> if you’re wrapping a collection and need to wrap the collection and
> its indices independently).

I'm afraid I don't understand. Could you be more explicit about what
you have in mind?

>> Q: Why should there be indices on an infinite multipass sequence?
>> A: Because the operations on indices apply equally well whether the
>> sequence is finite or not. Find the index of a value in the
>> sequence, slice the sequence, find again, etc.
>>
>> Q: Why is there an endIndex on an infinite seque?
>> A: So you can write algorithms such as index(where:) once.
>>
>> Q: Why not allow endIndex to have a different type from startIndex?
>>
>> A: It appears to offer insufficient benefit for the associated
>> complexity in typical usage. A classic use case that argues for a
>> different endIndex type is the null-terminated C string. But you
>> can't index one of those safely without actually counting the
>> length,
>> and once you've done that you can make the endIndex an Int.
>
> It’s also worth nothing that we can use `Optional` with `nil` as the
> `endIndex` sentinel if necessary.

True, that's a useful technique when there's no underlying storage in
the collection (e.g. a fibonacci sequence)

>>
>> ## Single Pass Iteration
>>
>> The refinement relationship between Sequence and Collection is
>> problematic, because it means either:
>>
>> a) algorithms such as map on single-pass sequences claim to be
>> nonmutating even though it's a lie (status quo)
>>
>> b) those algorithms can't be used on immutable (“let bound”)
>> multipass sequences. IMO that would be totally unacceptable.
>>
>> If we drop the refinement, we can have a saner world. We also don't
>> need to separate Sequence and Iterator anymore. We can simply drop
>> Sequence altogether, and the protocol for single-pass iteration
>> becomes Iterator.
>
> Makes sense to me.
>
>>
>> ### Mutation and Reference Semantics
>>
>> Everything in Swift is copiable via `let copy = thing` (let's please
>> not argue over the definition of copy for classes; this is the one
>> built into the lowest level of the language—I refer to the other one,
>> that requires allocation, as “clone”).
>>
>> Anything you do with a sequence that's truly single-pass mutates the
>> sequence *and of its copies*. Therefore, such a type *fundamentally*
>> has reference semantics. One day we may be able to model single-pass
>> sequences with “move-only” value types, which cannot be copied. You
>> can find move-only types in languages like Rust and C++, but they are
>> not supported by Swift today. So it seems reasonable that all
>> Iterators in Swift today should be modeled as classes.
>
> I think this makes a lot of sense in the model you are proposing. All
> multipass structures are collections. Any sequence that can only
> support a single pass is modeled as an iterator which inherently has
> identity. Making this distinction strong will prevent any confusion.
>
>> The fact that Swift doesn't have a mutation model for classes,
>> though, means that mutating methods on a class constrained protocol
>> can't be labeled as such. So consuming operations on a
>> class-constrained Iterator protocol would not be labeled as mutating.
>>
>> The standard library team is currently trying to evaluate the
>> tradeoffs in this area. One possibility under consideration is
>> simply dropping support for single-pass sequences until Swift can
>> support move-only value types and/or gets a mutation model for class
>> instances. It would be very interesting to know about any real-world
>> models of single-pass sequences that people are using in Swift, since
>> we don't supply any in the standard library.
>
> I’m happy to see you mention a mutation model for class instances! (I
> don’t mean to sidetrack the discussion, but would love to see that
> someday)
>
> I don’t have any objection to dropping support for single-pass
> sequences temporarily. It’s possible that I would feel differently if
> I was making use of them in my own code but I’m not.

On second thought, I believe it is important to have a way to support
existing “partially formed” multipass sequences that don't expose
copying or equality for their iteration states. Iterator is the right
way to do that. So I think we need to keep Iterator around.

> In the meantime, people would be able to implement their own protocol
> for single pass sequences. What they would lose is for..in as well as
> the standard library algorithms. I’m not sure how many people this
> would impact or how big the impact would be for them. We have seen a
> couple of examples in this discussion, but probably not enough to
> asses the overall impact.
>
> One thing you don’t mention here is a distinction between finite and
> infinite single-pass sequences (iterators). I don’t know if the
> finite / infinite distinction is as important here, but wanted to
> point it out. Obviously if we remove support single-pass sequences
> now we could defer that discussion until we’re ready to bring back
> support for them.

There are a few possible answers I can think of:

1. Do the “obvious” thing and create a separate protocol for finite
   single-pass sequences

2. Decide that the combination of infinite and single-pass is rare
   enough (/dev/urandom, temperature sensor) that it's better to just
   ask people handling them to be careful and not, e.g., try to “count”
   them.

Not really feeling sufficiently in my element (excuse the pun) to comment
on most of this discussion, but here I thought I'd chime in. What's
interesting about your two examples (/dev/urandom, temperature sensor) is
that, though single-pass, they should be insensitive to destructive
consumption, no? By which I mean, if some function returns 5 elements from
the "sequence", in both scenarios it would be undetectable whether it
consumes 5, 10 or 15 elements in the process, IIUC. Are there other
examples of infinite, single-pass sequences where destructive consumption
would make a difference?

···

On Fri, Jul 1, 2016 at 11:51 AM, Dave Abrahams via swift-evolution < swift-evolution@swift.org> wrote:

on Fri Jul 01 2016, Matthew Johnson <matthew-AT-anandabits.com> wrote:
>> On Jun 30, 2016, at 12:26 PM, Dave Abrahams <dabrahams@apple.com> > > wrote:
>> on Wed Jun 29 2016, Haravikk <swift-evolution-AT-haravikk.me> wrote:
>>>> On 29 Jun 2016, at 00:10, Matthew Johnson via swift-evolution < > swift-evolution@swift.org> wrote:

3. Decide that everything on a single-pass sequence is lazy. Since you
   can only take a single pass anyway, people won't observe their
   closures being called more often than necessary, which was the main
   motivator for making map, filter, et. al eager on collections without
   an explicit .lazy.

Implications of #3:

* Any “partially-formed” multipass sequences (modeling only Iterator)
  would be free to expose an accurate underestimatedCount, thereby
  optimizing the process of copying into an array. The lazy filter
  Iterator adaptor would have an underestimatedCount of 0.

* All algorithms that require multiple passes, such as sorted(), would
  be unavailable on Iterator. You'd have to construct an Array (or
  other MutableCollection) and sort that in-place. Of course,
  constructing an Array from an Iterator could still go on forever if
  the Iterator turned out to be infinite, which means, at some level #3
  is just a refinement of #2 that makes it less error-prone.

--
Dave
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution