for in? optionalCollection {


(Tino) #1

This one started over at swift-users, with a question on how to deal with looping over containers that may be nil.

Imho the beauty of the feature is that it's simple enough to be explained in the subject line, but here is the "long" story:

let test: [Int]? = nil

// this is possible now
if let test = test {
  for i in test {
    print(i)
  }
}

// how it could be written with a modified keyword
for i in? test {
  print(i)
}

I've been thinking "in?" had been brought up long ago, but as I haven't found such a proposal, I probably confused it with the cancelled plan to write one on my own (or I just was to stupid to search ;-).

Syntactic sugar like this is definitely nothing that has priority now, but discussing it shouldn't be a big distraction — and if it turns into a proposal that as well survives review, it might be even simple enough to act as a trigger for me to finally get my hands on some real work for Swift :wink:

- Tino


Another try at allowing optional iteration
For loops with optional arrays, is there a better, less wordy way?
(André Videla) #2

I don't think this use case warrants a syntax change since it can already be expressed quite elegantly with

let test: [Int]? = nil

test?.forEach { i in
    print(i)
}

Maybe "in?" could be used instead of

let test: [Int?] = [0,1,nil,3]

for case let i? in test {
    print(i)
}
?

···

On 11 Feb 2017, at 12:48, Tino Heth via swift-evolution <swift-evolution@swift.org> wrote:

This one started over at swift-users, with a question on how to deal with looping over containers that may be nil.

Imho the beauty of the feature is that it's simple enough to be explained in the subject line, but here is the "long" story:

let test: [Int]? = nil

// this is possible now
if let test = test {
  for i in test {
    print(i)
  }
}

// how it could be written with a modified keyword
for i in? test {
  print(i)
}

I've been thinking "in?" had been brought up long ago, but as I haven't found such a proposal, I probably confused it with the cancelled plan to write one on my own (or I just was to stupid to search ;-).

Syntactic sugar like this is definitely nothing that has priority now, but discussing it shouldn't be a big distraction — and if it turns into a proposal that as well survives review, it might be even simple enough to act as a trigger for me to finally get my hands on some real work for Swift :wink:

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


(Anton Zhilin) #3

for i in test ?? [] {
    print(i)
}

For a more general solution, we could add Optional.flatten() to support
optional sequences:

for i in test.flatten() {
    print(i)
}


(Zhao Xin) #4

What about just use

test?.forEach { print($0) }

Zhaoxin

···

On Sat, Feb 11, 2017 at 7:48 PM, Tino Heth via swift-evolution < swift-evolution@swift.org> wrote:

This one started over at swift-users, with a question on how to deal with
looping over containers that may be nil.

Imho the beauty of the feature is that it's simple enough to be explained
in the subject line, but here is the "long" story:

let test: [Int]? = nil

// this is possible now
if let test = test {
for i in test {
print(i)
}
}

// how it could be written with a modified keyword
for i in? test {
print(i)
}

I've been thinking "in?" had been brought up long ago, but as I haven't
found such a proposal, I probably confused it with the cancelled plan to
write one on my own (or I just was to stupid to search ;-).

Syntactic sugar like this is definitely nothing that has priority now, but
discussing it shouldn't be a big distraction — and if it turns into a
proposal that as well survives review, it might be even simple enough to
act as a trigger for me to finally get my hands on some real work for Swift
:wink:

- Tino

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


(Guillaume Lessard) #5

This could come for almost free after SE-0143 is implemented: an Optional of a Sequence could itself be made to conform to Sequence. It would cost no new syntax.

extension Optional: Sequence where Wrapped: Sequence {
  func makeIterator() -> AnyIterator<Wrapped.Iterator.Element> {
    switch self {
    case .some(let sequence):
      return AnyIterator(sequence.makeIterator())
    case .none:
      return AnyIterator { nil }
    }
  }
}

This would be more easily done than new syntax, surely.

Cheers,
Guillaume Lessard


Extend "emptiness" test to Optional Collections
(Tino) #6

I don't think this use case warrants a syntax change since it can already be expressed quite elegantly with

let test: [Int]? = nil

test?.forEach { i in
    print(i)
}

What about just use

test?.forEach { print($0) }

This works for the simple example, but it isn't as powerful:

if let test = test {
  for i in test {
    if i == 42 {
      break
    }
  }
}

You could add
func forEach(_ body: (Element) throws -> Bool) rethrows

but even this would be less powerful as a loop, which allows you to break, continue or return as you like.


(Derrick Ho) #7

let test: [Int]? = nil
for b in test ?? [] where b != 42 {
   Print(b)

}

I don't think you need new syntax since what you want can be accomplished
quite succinctly already

···

On Sat, Feb 11, 2017 at 8:18 AM Anton Zhilin via swift-evolution < swift-evolution@swift.org> wrote:

for i in test ?? [] {
    print(i)
}

For a more general solution, we could add Optional.flatten() to support
optional sequences:

for i in test.flatten() {
    print(i)
}


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


(Tino) #8

Most alternatives already where discussed in swift-user:

Imho the "forEach" solution is flawed, because you can't break the loop, and the "?? []" isn't perfect either:
I hope the compiler can optimise so that the assembly is as fast as the "if let" solution, but even if this is the case, it is not obvious for a human reader.

This

extension Optional where Wrapped: Sequence {
  var elements: [Wrapped.Iterator.Element] {
    switch (self) {
    case .none:
    return []
    case .some(let o):
    return Array(o)
    }
  }
}

let test: [Int]? = nil
for i in test.elements {
  print(i)
}

looks nice to me (except the return type — I guess there are better options), but I don't expect that the compiler can do much to optimise it.

But maybe there is something better:

for i in test? {
  print(i)
  if i == 42 {
    break
  }
}

This doesn't work — but it compiles fine when you replace the "?" with "!".

I haven't proposed "in?" earlier because it would be a new keyword (even if its not that new…), and I'm very skeptical towards such changes.
But it is a very common pattern in Swift to have a "!" form that may crash and a save "?" variant, so I think it is actually missing in the syntax of for-loops.


(Tino) #9

This would be more easily done than new syntax, surely.

… and it wouldn't increase the size of the language, so in general, I prefer library-solutions.

I'd expect some pushback against the conformance, but my personal opinion is that the difference between an empty collection and a missing collection doesn't matter most of the time.

Performance doesn't matter most of the time as well ;-), but imho it's a big plus if the most elegant solution is the fastest, too (and I have no idea if or when the compiler infrastructure is clever enough to recognise empty loops without instantiating useless objects).

But afaics, there is some consensus that Optional<Sequence> deserves some sugar applied to it — either in the language, or in the stdlib.


(André Videla) #10

Ah I see you point a bit better.

But I don't agree with your example, since it can be easily expressed with

test?.prefix(while: { $0 != 42}).forEach { i in
    print(i)
}

But arguing about examples is besides the point, I would like to stop here.

I have a question for you. How do you think we could use this pattern in the generalised situation:

if condiition {
    for thing in condition {
    }
}

for example

enum Result<T> {
    case success(T)
    case failure(Error)
}

let result: Result<[Int]> = .success([0,1,2,3])

if case .success(let arr) = result {
    for e in arr {
        print(e)
    }
}

Maybe it would be nice to write something along the lines of

for e in case .success(let arr) = result {
    
}

or even
for e in arr where .success(let arr) = result {
    
}

therefore the "in?" syntax would be sugar for
for i in array where let array? = test {
    
}

···

On 11 Feb 2017, at 13:22, Tino Heth <2th@gmx.de> wrote:

I don't think this use case warrants a syntax change since it can already be expressed quite elegantly with

let test: [Int]? = nil

test?.forEach { i in
    print(i)
}

What about just use

test?.forEach { print($0) }

This works for the simple example, but it isn't as powerful:

if let test = test {
  for i in test {
    if i == 42 {
      break
    }
  }
}

You could add
func forEach(_ body: (Element) throws -> Bool) rethrows

but even this would be less powerful as a loop, which allows you to break, continue or return as you like.


(Tino) #11

I have a question for you. How do you think we could use this pattern in the generalised situation:

In general :wink: I like things that can be used universally much more than a huge number of special cases.
But here, I'm not sure if it's not an increase of complexity:

I often if statements with several stages of unpacking, and when there is something similar for loops, imho it should act in the same way:
for let array? = test, i in array {

This would preserve the right order and keep the where clause reserved as it is now.