Remove forEach?

Could such construct also allow for an if-else expression like behavior?

let result = if bool { 1 } else { 2 }

Or would this be better modeled as a non block-like expression?

let result = 1 if bool else 2

I'm just curious, this maybe be out of topic/ unrelated.

···

On Wednesday, December 9, 2015, Joe Groff via swift-evolution < swift-evolution@swift.org> wrote:

On Dec 9, 2015, at 8:47 AM, David Owens II <david@owensd.io > <javascript:_e(%7B%7D,'cvml','david@owensd.io');>> wrote:

Another language construct seems a bit much for this, right? Maybe I’m
missing something, but can’t we get the same behavior with an overload?

A language construct that helps eliminate multiple other language
constructs would potentially be a net win, though. If there were closures
that supported nonlocal exits, then `do`, `for...in`, and possibly other
constructs could become library functions, and other "block-like" library
features like `autoreleasepool`, `withUnsafePointer` would work more
naturally too.

-Joe

extension Array {
    func forEach<U>(body: (element: Element) throws -> U?) rethrows -> U? {
        for e in self {
            if let result = try body(element: e) { return result }
        }

        return nil
    }
}

func g(e: Int) -> Int? {
    if e == 2 { return e }
    return nil
}

let arr = [1, 2, 3]
arr.forEach { print($0) }
let result = arr.forEach(g)
result // has the value of 2

Now, Swift has some issues determining the types properly if you attempt
to inline the g function at the forEach() callsite, but that can be fixed.

-David

On Dec 9, 2015, at 4:40 AM, Stephen Celis via swift-evolution < > swift-evolution@swift.org > <javascript:_e(%7B%7D,'cvml','swift-evolution@swift.org');>> wrote:

On Dec 8, 2015, at 5:13 PM, Joe Groff via swift-evolution < > swift-evolution@swift.org > <javascript:_e(%7B%7D,'cvml','swift-evolution@swift.org');>> wrote:

Another direction you might take this is to make it a type annotation on
the function type, like throws, so forEach has a type like this:

func forEach(body: (Element) breaks -> ())

and a closure that `breaks` has nonlocal behavior for
break/continue/return (and is implied to be noescape and void-returning, I
guess).

This is really interesting. Ruby provides similar functionality with its
lambda vs. proc, but a type annotation is much more understandable. It
could also imply @noescape automatically:

   func forEach(@canbreak body: Element -> Void)

Stephen
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
<javascript:_e(%7B%7D,'cvml','swift-evolution@swift.org');>
https://lists.swift.org/mailman/listinfo/swift-evolution

Would it make sense to explore this approach a bit further? Or will it be out of scope for Swift 3 anyway? This could also enable a lot of other cool stuff. To me, it feels like a big task, so not sure how to proceed on this...

Chris

···

On 09 Dec 2015, at 12:01, Joe Groff <jgroff@apple.com> wrote:

On Dec 9, 2015, at 8:47 AM, David Owens II <david@owensd.io <mailto:david@owensd.io>> wrote:

Another language construct seems a bit much for this, right? Maybe I’m missing something, but can’t we get the same behavior with an overload?

A language construct that helps eliminate multiple other language constructs would potentially be a net win, though. If there were closures that supported nonlocal exits, then `do`, `for...in`, and possibly other constructs could become library functions, and other "block-like" library features like `autoreleasepool`, `withUnsafePointer` would work more naturally too.

-Joe

extension Array {
    func forEach<U>(body: (element: Element) throws -> U?) rethrows -> U? {
        for e in self {
            if let result = try body(element: e) { return result }
        }
        
        return nil
    }
}

func g(e: Int) -> Int? {
    if e == 2 { return e }
    return nil
}

let arr = [1, 2, 3]
arr.forEach { print($0) }
let result = arr.forEach(g)
result // has the value of 2

Now, Swift has some issues determining the types properly if you attempt to inline the g function at the forEach() callsite, but that can be fixed.

-David

On Dec 9, 2015, at 4:40 AM, Stephen Celis via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Dec 8, 2015, at 5:13 PM, Joe Groff via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Another direction you might take this is to make it a type annotation on the function type, like throws, so forEach has a type like this:

func forEach(body: (Element) breaks -> ())

and a closure that `breaks` has nonlocal behavior for break/continue/return (and is implied to be noescape and void-returning, I guess).

This is really interesting. Ruby provides similar functionality with its lambda vs. proc, but a type annotation is much more understandable. It could also imply @noescape automatically:

   func forEach(@canbreak body: Element -> Void)

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

Seems to me that's still supportable; it's not much different from catching specific error types. It's one of many things someone proposing this would need to account for.

-Joe

···

On Dec 9, 2015, at 10:23 AM, Guillaume Lessard via swift-evolution <swift-evolution@swift.org> wrote:

On 9 déc. 2015, at 10:01, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

A language construct that helps eliminate multiple other language constructs would potentially be a net win, though. If there were closures that supported nonlocal exits, then `do`, `for...in`, and possibly other constructs could become library functions, and other "block-like" library features like `autoreleasepool`, `withUnsafePointer` would work more naturally too.

If a `for` loop became a library function with special closures, what would happen to labeled break statements?

e.g.
outer: for i in 0..<5 {
for j in 0..<5 {
   break outer
}
}

Another direction you might take this is to make it a type annotation on the function type, like throws, so forEach has a type like this:

func forEach(body: (Element) breaks -> ())

and a closure that `breaks` has nonlocal behavior for break/continue/return (and is implied to be noescape and void-returning, I guess).

I missed this post the first time around. Is a really great idea and is something I hoped we might get eventually. It allows "control flow" functions to behave as expected.

I assume a caller could pass a non-breaking closure if desired just as we can pass a non-throwing closure if desired, right?

···

,
How would break and continue interact with the caller of the closure? It would need to implement the correct behavior of skipping to the next loop cycle or moving on to any post-loop logic.

How would this behave for a closure that is stored and called later, possibly asynchronously? Or would it only be allowed on closures declared

Matthew

I think it's definitely an interesting direction. The questions Guillaume and Matthew raised would have to be answered, at minimum.

-Joe

···

On Dec 9, 2015, at 1:17 PM, Chris Eidhof <chris@eidhof.nl> wrote:

Would it make sense to explore this approach a bit further? Or will it be out of scope for Swift 3 anyway? This could also enable a lot of other cool stuff. To me, it feels like a big task, so not sure how to proceed on this...

Another question to consider is if you or another developer can be lined up to contribute an implementation to the compiler. I suspect we’re going to collect a number of “good and approved ideas, but which do not have someone signed up to code it up in the compiler”. This may end up being the difference between something getting into Swift 3 or not.

-Chris

···

On Dec 9, 2015, at 1:44 PM, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

On Dec 9, 2015, at 1:17 PM, Chris Eidhof <chris@eidhof.nl> wrote:

Would it make sense to explore this approach a bit further? Or will it be out of scope for Swift 3 anyway? This could also enable a lot of other cool stuff. To me, it feels like a big task, so not sure how to proceed on this...

I think it's definitely an interesting direction. The questions Guillaume and Matthew raised would have to be answered, at minimum.

Another question to consider is if you or another developer can be lined up to contribute an implementation to the compiler. I suspect we’re going to collect a number of “good and approved ideas, but which do not have someone signed up to code it up in the compiler”. This may end up being the difference between something getting into Swift 3 or not.

Will there be a distinct status for such proposals? It might be nice to do this so prospective contributors to the compiler have a place to find “ready to go” projects if they’re interested in working on new features.

Matthew

I’ll have a look at it. I’ll try to do some experiments with this syntax to see what would happen. If the experiment turns out interesting, I could imagine trying to write some C++ over Christmas to cook up an implementation...

Chris

···

On 09 Dec 2015, at 17:44, Chris Lattner <clattner@apple.com> wrote:

On Dec 9, 2015, at 1:44 PM, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

On Dec 9, 2015, at 1:17 PM, Chris Eidhof <chris@eidhof.nl> wrote:

Would it make sense to explore this approach a bit further? Or will it be out of scope for Swift 3 anyway? This could also enable a lot of other cool stuff. To me, it feels like a big task, so not sure how to proceed on this...

I think it's definitely an interesting direction. The questions Guillaume and Matthew raised would have to be answered, at minimum.

Another question to consider is if you or another developer can be lined up to contribute an implementation to the compiler. I suspect we’re going to collect a number of “good and approved ideas, but which do not have someone signed up to code it up in the compiler”. This may end up being the difference between something getting into Swift 3 or not.

-Chris

The idea is interesting, but I share your concerns, and Guillaume's concern about labelled breaks as well.

I'm inclined to say that making things like for-in as a library feature instead of a language feature may be more easily accomplished with macros, as that bypasses all these questions (since it will get rid of the closure). Note that a for-in loop could be easily transformed by a macro into the following:

Source (for let):

[label:] for [var] pattern in sequence [where condition] {
    body
}

Result (for let):

[label:] do { // to scope the generator
    var gensymmedName = sequence.generate()
    while letOrVar pattern = gensymmedName.next() [where condition] {
        body
    }
}

Source (for case):

[label:] for case pattern in sequence [where condition] {
    body
}

Result (for case):

[label:] do { // to scope the generator
    var gensymmedName = sequence.generate()
    while let gensymmedName2 = gensymmedName.next() {
        if case pattern = gensymmedName2 [where condition] {
            body
        }
    }
}

-Kevin Ballard

···

On Wed, Dec 9, 2015, at 09:43 AM, Matthew Johnson via swift-evolution wrote:

>>>>> Another direction you might take this is to make it a type annotation on the function type, like throws, so forEach has a type like this:
>>>>>
>>>>> func forEach(body: (Element) breaks -> ())
>>>>>
>>>>> and a closure that `breaks` has nonlocal behavior for break/continue/return (and is implied to be noescape and void-returning, I guess).

I missed this post the first time around. Is a really great idea and is something I hoped we might get eventually. It allows "control flow" functions to behave as expected.

I assume a caller could pass a non-breaking closure if desired just as we can pass a non-throwing closure if desired, right?
,
How would break and continue interact with the caller of the closure? It would need to implement the correct behavior of skipping to the next loop cycle or moving on to any post-loop logic.

How would this behave for a closure that is stored and called later, possibly asynchronously? Or would it only be allowed on closures declared

Using logic similar to GeneratorType.next(), a `breaks` closure could effectively return an Optional<Void>.
Reaching the end or a `continue`-equivalent statement would return .Some(Void).
Exiting via the `break`-equivalent statement would return .None and signal the end of looping

Making the construct look right, however, sounds like compiler magic.

Guillaume Lessard

···

On 9 déc. 2015, at 10:43, Matthew Johnson via swift-evolution <swift-evolution@swift.org> wrote:

How would break and continue interact with the caller of the closure? It would need to implement the correct behavior of skipping to the next loop cycle or moving on to any post-loop logic.

Can’t most of this functionality be achieved with current Swift features, though? I mean, you could have the break within the type that the closure returns.

In the case of forEach, you could have it return a Bool, rather than Void, with true signifying break:

extension SequenceType {
  func forEachBreak(body: Generator.Element -> Bool) {
    for element in self {
      if body(element) {
        return
      }
    }
  }
}

[1, 2, 3, 4, 5, 6, 7, 8].forEachBreak { n in
  print(n)
  if n > 4 { return true }
  return false
}

For methods like map, you could have a version which takes a closure that returns an Optional: nil signifying a break. Or, You could add something like a takeWhile method to SequenceType. (which would probably be the easiest to understand)

I think that annotations like @noescape and throws have huge overhead in terms of added confusion, especially for beginners. If the objective is to improve the understandability of certain functions, I think that something like this:

sequence.takeWhile { $0 < 10 }.forEach { print($0) }

is a better option than swelling the signature of forEach to:

func forEach(@noescape body: (Self.Generator.Element) throws, breaks -> ()) rethrows

Oisin.

···

On 9 Dec 2015, at 17:43, Matthew Johnson via swift-evolution <swift-evolution@swift.org> wrote:

Another direction you might take this is to make it a type annotation on the function type, like throws, so forEach has a type like this:

func forEach(body: (Element) breaks -> ())

and a closure that `breaks` has nonlocal behavior for break/continue/return (and is implied to be noescape and void-returning, I guess).

I missed this post the first time around. Is a really great idea and is something I hoped we might get eventually. It allows "control flow" functions to behave as expected.

I assume a caller could pass a non-breaking closure if desired just as we can pass a non-throwing closure if desired, right?
,
How would break and continue interact with the caller of the closure? It would need to implement the correct behavior of skipping to the next loop cycle or moving on to any post-loop logic.

How would this behave for a closure that is stored and called later, possibly asynchronously? Or would it only be allowed on closures declared

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