[swift-evolution] Proposal: CollectionType.cycle property for an infinite sequence

Andrew Bennett cacoyi at gmail.com
Tue Dec 29 04:27:48 CST 2015


+1 looks good, I had a go at implementing it and I think it may require
changes not discussed in the proposal.

You've covered the potential issues fairly well, to be a little more
explicit these are the issues I've found:

 1) LazySequenceType's property array cannot be defined without an infinite
sized array.
 2) what should [1,2,3].cycle.suffix(4) return? [3,1,2,3] probably has the
least surprises, but it's like asking what's the number before infinity.

 3) dropLast should return AnySequence(self), but requires specialisation,
this may have to also be a fatalError (see below).


One issue I don't think you've mentioned, and I don't seem to be able to
resolve is this:

let mySequence = [1,2,3].cycle.dropLast(1)

mySequence.suffix(7)


This could have well defined behaviour (see part 2 above), however the
implementation has some issues.

In this case mySequence is an AnySequence<Int>, mySequence.suffix(7) uses
AnySequence's specialisation and so tries to iterate over the entire
sequence to find the suffix. AnySequence<Int> is type-erased so there's no
way to specialise when the underlying sequence is infinite (to get a valid
implementation of suffix).

Potential solutions:
 * Replace erased Any* types with a more flexible alternative that doesn't
remove type information (higher kinded types perhaps).
 * Replace SequenceType with two protocols FiniteSequenceType and
InfiniteSequenceType, have type erased versions of each, duplicate all the
things.
 * Add a property to SequenceType to indicate if it's definitely finite
(undefined?), AnySequence uses a different backing implementation depending
on this boolean.

Here's the implementation I used in a playground to toy with this:

public struct CycleSequence<Base : CollectionType> : LazySequenceType {

    private let base: Base

    private init(_ collection: Base) {

        self.base = collection

    }

    @warn_unused_result

    public func generate() -> CycleGenerator<Base> {

        return CycleGenerator<Base>(base.generate)

    }

    public var array: [Base.Generator.Element] {

        fatalError("This is undefined!")

    }

}


public struct CycleGenerator<Base : CollectionType> : GeneratorType {

    private let generatorProducer: () -> Base.Generator

    private var generator: Base.Generator

    private init(_ generatorProducer: () -> Base.Generator) {

        self.generatorProducer = generatorProducer

        self.generator = generatorProducer()

    }

    @warn_unused_result

    public mutating func next() -> Base.Generator.Element? {

        if let element = generator.next() {

            return element

        }

        generator = generatorProducer()

        return generator.next()

    }

}


extension CycleSequence {

    @warn_unused_result

    public func dropLast(n: Int) -> AnySequence<Base.Generator.Element> {

        return AnySequence(self)

    }

    @warn_unused_result

    public func suffix(maxLength: Int) -> AnySequence<Base.Generator.Element>
{

        let maxCount = base.count.toIntMax()

        let sequenceLength = maxCount >= Int.max.toIntMax() ? Int.max : Int
(maxCount)

        if maxLength < sequenceLength {

            return AnySequence(base.suffix(maxLength))

        }

        return self.dropFirst(sequenceLength - (maxLength %
sequenceLength)).prefix(maxLength)

    }

}


extension CollectionType {

    public var cycle: CycleSequence<Self> { return CycleSequence(self) }

}



// this produces an infinite loop when evaluating .suffix(7)

let cycle = ["a", "b", "c"].cycle

cycle.dropLast(1).suffix(7).forEach { print("suffix: \($0)") }



On Mon, Dec 28, 2015 at 6:35 PM, Developer via swift-evolution <
swift-evolution at swift.org> wrote:

> +1.  Stream support is long overdue.
>
> ~Robert Widmann
>
> 2015/12/28 2:20、Kevin Ballard via swift-evolution <
> swift-evolution at swift.org> のメッセージ:
>
> > ## Introduction
> >
> > Add a new property `cycle` to CollectionType that returns an infinite
> SequenceType that yields the elements of the collection in a loop.
> >
> > ## Motivation
> >
> > It's sometimes useful to be able to have an infinite sequence. For
> example, `CollectionOfOne(x).cycle` could be used to have an infinite
> sequence of a single element (similar to Repeat but without a count). A
> common use for infinite sequences is zipping with a finite sequence. As far
> as I'm aware, the stdlib does not currently provide any way to create such
> an infinite sequence.
> >
> > ## Proposed solution
> >
> > Extend CollectionType with a new property `cycle` that yields a type
> that conforms to SequenceType. This sequence yields each element of the
> collection in an infinite loop.
> >
> > ## Detailed design
> >
> > 2 new types would be added:
> >
> > struct CycleSequence<Base : CollectionType> : LazySequenceType { ... }
> > struct CycleGenerator<Base : CollectionType> : GeneratorType { ... }
> >
> > CollectionType would be extended with a property:
> >
> > extension CollectionType {
> >    public var cycle: CycleSequence<Self> { get }
> > }
> >
> > This is an extension of CollectionType instead of SequenceType because
> it requires a multi-pass sequence (and SequenceType does not provide that
> guarantee). The returned type conforms to SequenceType instead of
> CollectionType because there is no possible `endIndex` that satisfies the
> requirement of being reachable from `startIndex` by zero or more
> applications of `successor()`.
> >
> > Because the default eager versions of map and filter will execute
> forever on an infinite sequence, CycleSequence conforms to LazySequenceType
> instead of SequenceType in order to provide lazy versions of those
> functions. Additionally, it will provide implementations of the eager
> versions that simply trigger a fatalError(), as the alternative is an
> infinite loop that consumes more and more memory.
> >
> > ## Impact on existing code
> >
> > None
> >
> > -Kevin Ballard
> > _______________________________________________
> > swift-evolution mailing list
> > swift-evolution at swift.org
> > https://lists.swift.org/mailman/listinfo/swift-evolution
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20151229/6ac7c741/attachment.html>


More information about the swift-evolution mailing list