[swift-evolution] [Pitch] Remove destructive consumption from Sequence

Dave Abrahams dabrahams at apple.com
Thu Jun 30 18:24:29 CDT 2016


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

>> On Jun 30, 2016, at 3:37 PM, Dave Abrahams <dabrahams at apple.com> wrote:
>> 
>> 
>>>>> 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? 
>> 
>
> Fair point; we actually do take advantage of the lazy versions of
> filter and map.
>
> Does it make sense to say that single-pass sequences are always lazy?

It might.  Would it be inconvenient?

>
>                Iterable
>           /                    \
> 	 /                       \
>   LazyIterable      EagerIterable
>        |                             |
>  LazyCollection       Collection
>
> LazyCollection is a wrapper around Collection so you can still use
> .lazy the way you would today.

Is it a protocol?  Wrappers are usually generics.

> 
> LazyIterables are single-pass.  EagerIterables are multi-pass.
> Maybe this doesn’t work because we’re talking about somewhat
> orthogonal things? I could imagine trying to model the idea of single
> vs multi pass and lazy vs eager separately:

The goal of generic programming is not to separate every idea into its
own protocol.  Discovering the natural dependency relationships and
clusters of requirements and capabilities is a crucial part of it.  As
far as I can tell, lazy operations are always appropriate.

>
>> protocol Iterable {
>>     associatedtype Iterator: IteratorProtocol
>>     associatedtype Element = Iterator.Element
>>     func makeIterator() -> Iterator
>> }
>> protocol MultiIterable: Iterable { }
>> protocol SingleIterable: Iterable { }
>> protocol LazyIterable: Iterable { }
>> protocol EagerIterable: Iterable { }
>> 
>> extension MultiIterable where Self: EagerIterable {
>>     func map<U>(t: @noescape (Element) -> U) -> [U] { }
>> }
>> 
>> extension MultiIterable where Self: LazyIterable {
>>     func map<U>(t: (Element) -> U) -> LazyMapMultiIterable<Self> { }
>> }
>> 
>> extension SingleIterable where Self: LazyIterable {
>>     func map<U>(t: (Element) -> U) -> LazyMapSingleIterable<Self> { }
>> }
>
> I guess it comes back to what you and others have pointed out - it
> might not be worth the effort to provide this level of flexibility.

-- 
Dave


More information about the swift-evolution mailing list