How does `AnyIterator` work as a generator?

Hi, I apologize in advance for a somewhat vague and open-ended question, but I'm struggling to understand AnyIterator. Suppose I have the following snippet, inspired by code I read in a project:

let result = AnyIterator { } .prefix(2).map { Int.random(in: 0...10) }
print(result)

This make an array with two random Int s, but I don't understand quite how - what is going on here? How would I rewrite this (would one ever) with, say IndexingIterator?

Thinking about generating a flexible list of Ints, in Python I would probably use a list comprehension, although I guess the closer approach would be a lambda with a yield statement or something. In Swift, which I'm newer to, I guess I would have used a loop and appended values to an empty array, haha. The snippet is very pretty - it reminds me a bit of how I would do this in something like Haskell (take the first couple of elements of a lazy series), but I just don't quite grok the syntax.

Thanks in advance for any comments...

1 Like

oof, that is a very strange way to do that.

AnyIterator { }

This returns an AnyIterator<Void>. Iterators stop when they return nil, but this makes use of implicit returns in single-expression closures so it never does - it'll keep returning Voids forever.

AnyIterator also conforms to Sequence (which I personally don't agree with - it only works when the iterator has value semantics). That's where you get the .prefix(Int) method from. It calls next() twice, obtaining 2 Voids, and returning them as an Array<Void>. Then you're calling Array.map to transform those in to random integers.

A simpler, more idiomatic way to write this would be:

(0..<2).map { _ in Int.random(in: 0...10) }
4 Likes

I have to emphasize the first part of @Karl's reply here.

This is a very strange way of accomplishing a very ordinary task; I have never seen anything like it. It's no wonder that you don't grok what's going on: few users would, and I don't think anyone could do so without significant effort.

I would strongly recommend not emulating this practice.

5 Likes

I'm happy to learn that AnyIterator { } creates an infinite sequence. It may not be frequent, as others say, but I'm not sure there's an obvious replacement for the same effect. After the first surprise in front of something new, we can praise the imagination of the Swift community, always eager to invent new idioms. Thanks for sharing, @gnperdue :-)

2 Likes

Whenever you’re using AnyIterator to produce a sequence, stop what you’re doing and switch to using sequence instead. In this case the equivalent would be sequence(first: ()) { }.

5 Likes

Yes. I was trying to write something smart about sequence(first:_:) and sequence(state:_:), but could not. Thanks Ben!