Remove forEach?


(Howard Lovatt) #1

You could replace `forEach` with a supped up `map` that also allowed `break` and `continue`. The following library function gives `continue` and `break` and also combines `repeat`, `times`, `forEach`, `filter`, `flatMap`, and `map` into one:

public final class MapController<E, R> {
    var results = [R]()
    
    var isContinuing = true
    
    init<C: CollectionType where C.Generator.Element == E>(_ collection: C, sizeEstimate: Int = 0, @noescape mapper: (controller: MapController<E, R>, element: E) throws -> R?) rethrows {
        results.reserveCapacity(sizeEstimate)
        for var generator = collection.generate(), element = generator.next(); element != nil && isContinuing; element = generator.next() {
            let result = try mapper(controller: self, element: element!)
            if let actualResult = result {
                results.append(actualResult)
            }
        }
    }
}

extension CollectionType {
    /// Controllable `map`, additional controls beyond simple `map` are:
    ///
    /// 1. Continue without returning a result (`return nil`)
    /// 2. Return multiple results (`control.results += [...]` then `return nil`)
    /// 3. Break (`control.isContinuing = false` then `return nil`)
    ///
    /// These additional controls allow this `map` to function like `repeat`, `times`, `forEach`, `filter`, `flatMap`, and `map` combined into one as well as providing an early termination (break).
    @warn_unused_result func map<R>(sizeEstimate sizeEstimate: Int = 0, @noescape mapper: (controller: MapController<Self.Generator.Element, R>, element: Self.Generator.Element) throws -> R?) rethrows -> [R] {
        return try MapController(self, sizeEstimate: sizeEstimate, mapper: mapper).results
    }
}

// Demonstration of full functionality including continue, break, and multiple returns
var result = (0 ..< 5).map { (control, index) -> Int? in
    switch index {
    case 1:
        return nil // Continue - skip 1 (`filter`)
    case 2:
        control.results.append(2) // Yield two results - this one and the 'return’ yield (`flatMap`)
    case 3:
        control.isContinuing = false // Break after next yield - which could be `return nil` if there are no more results
    default:
        break
    }
    return index // Yield next result - except for case 1 all the above yield `index`
}
print(result) // prints `[0, 2, 2, 3]` note missing "1", double "2", and last is "3"

// Demonstration of `repeat`/`forEach`/`times` like usage - note `(_, _) -> Void?`
result = [Int]()
(0 ..< 3).map { (_, _) -> Void? in
    result.append(1) // Do whatever - in this case append to a global
    return nil // Don't yield any results
}
print(result) // prints `[1, 1, 1]`

Would this be a superior alternative to both `forEach` and `times` in the library and `repeat` as a language feature?

···

Sent from my iPad


(Daniel Duan) #2

In most programming languages, map does not have any side-effect. This is may be the reason forEach was added.

···

On Dec 30, 2015, at 2:10 PM, Howard Lovatt via swift-evolution <swift-evolution@swift.org> wrote:

You could replace `forEach` with a supped up `map` that also allowed `break` and `continue`. The following library function gives `continue` and `break` and also combines `repeat`, `times`, `forEach`, `filter`, `flatMap`, and `map` into one:

public final class MapController<E, R> {
    var results = [R]()
    
    var isContinuing = true
    
    init<C: CollectionType where C.Generator.Element == E>(_ collection: C, sizeEstimate: Int = 0, @noescape mapper: (controller: MapController<E, R>, element: E) throws -> R?) rethrows {
        results.reserveCapacity(sizeEstimate)
        for var generator = collection.generate(), element = generator.next(); element != nil && isContinuing; element = generator.next() {
            let result = try mapper(controller: self, element: element!)
            if let actualResult = result {
                results.append(actualResult)
            }
        }
    }
}

extension CollectionType {
    /// Controllable `map`, additional controls beyond simple `map` are:
    ///
    /// 1. Continue without returning a result (`return nil`)
    /// 2. Return multiple results (`control.results += [...]` then `return nil`)
    /// 3. Break (`control.isContinuing = false` then `return nil`)
    ///
    /// These additional controls allow this `map` to function like `repeat`, `times`, `forEach`, `filter`, `flatMap`, and `map` combined into one as well as providing an early termination (break).
    @warn_unused_result func map<R>(sizeEstimate sizeEstimate: Int = 0, @noescape mapper: (controller: MapController<Self.Generator.Element, R>, element: Self.Generator.Element) throws -> R?) rethrows -> [R] {
        return try MapController(self, sizeEstimate: sizeEstimate, mapper: mapper).results
    }
}

// Demonstration of full functionality including continue, break, and multiple returns
var result = (0 ..< 5).map { (control, index) -> Int? in
    switch index {
    case 1:
        return nil // Continue - skip 1 (`filter`)
    case 2:
        control.results.append(2) // Yield two results - this one and the 'return’ yield (`flatMap`)
    case 3:
        control.isContinuing = false // Break after next yield - which could be `return nil` if there are no more results
    default:
        break
    }
    return index // Yield next result - except for case 1 all the above yield `index`
}
print(result) // prints `[0, 2, 2, 3]` note missing "1", double "2", and last is "3"

// Demonstration of `repeat`/`forEach`/`times` like usage - note `(_, _) -> Void?`
result = [Int]()
(0 ..< 3).map { (_, _) -> Void? in
    result.append(1) // Do whatever - in this case append to a global
    return nil // Don't yield any results
}
print(result) // prints `[1, 1, 1]`

Would this be a superior alternative to both `forEach` and `times` in the library and `repeat` as a language feature?

Sent from my iPad

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


(Lily Ballard) #3

Swift didn't use to have forEach(). It was added fairly late, and I
suspect (though I don't actually know) that it was done so to appease
people who kept abusing map() for the same function, as well as the die-
hard everything-must-be-functional crowd.

Personally, I'd rather we didn't have it because it encourages people to
use it, but I suppose it's better to give people an appropriate tool
than to keep watching them abuse map().

-Kevin Ballard

···

On Wed, Dec 30, 2015, at 04:50 PM, Craig Cruden via swift-evolution wrote:

I don’t see the benefit of taking a simple declarative expression
(map, flatMap, filter) and turning it into a complicated
imperative/iterative loop. You already have the ability to iterate
through a set and do whatever you want to do with with whatever logic
you want to use using. I would have no problem for the most part
removing foreach - it is more of a convenience method for doing an
iterative loop through a collection - and to be quite honest rarely
use outside of maybe putting in a print statement temporarily in there
(but more often just turn the resulting set into comma delimited
output and printing it).

On 2015-12-31, at 5:10:22, Howard Lovatt via swift-evolution <swift- >> evolution@swift.org> wrote:

You could replace `forEach` with a supped up `map` that also allowed
`break` and `continue`. The following library function gives
`continue` and `break` and also combines `repeat`, `times`,
`forEach`, `filter`, `flatMap`, and `map` into one:

public final class MapController<E, R> { var results = [R]()

var isContinuing = true

init<C: CollectionType where C.Generator.Element == E>(_ collection:
C, sizeEstimate: Int = , @noescape mapper: (controller:
MapController<E, R>, element: E) throws -> R?) rethrows {
results.reserveCapacity(sizeEstimate) for var generator =
collection.generate(), element = generator.next(); element != nil &&
isContinuing; element = generator.next() { let result = try
mapper(controller: self, element: element!) if let actualResult =
result { results.append(actualResult) }
} } }

extensionCollectionType { /// Controllable `map`, additional
controls beyond simple `map` are: /// /// 1. Continue without
returning a result (`return nil`) /// 2. Return multiple results
(`control.results += [...]` then `return nil`) /// 3. Break
(`control.isContinuing = false` then `return nil`) /// /// These
additional controls allow this `map` to function like `repeat`,
`times`, `forEach`, `filter`, `flatMap`, and `map` combined into one
as well as providing an early termination (break).
@warn_unused_result func map<R>(sizeEstimate sizeEstimate: Int = ,
@noescape mapper: (controller: MapController<Self.Generator.Element,
>, element: Self.Generator.Element) throws -> R?) rethrows -> [R] {
return try MapController(self, sizeEstimate: sizeEstimate, mapper:
mapper).results } }

// Demonstration of full functionality including continue, break,
and multiple returns var result = ( ..< 5).map { (control, index) ->
Int? in switch index { case 1: returnnil// Continue - skip 1
(`filter`) case 2: control.results.append(2) // Yield two
results - this one and the 'return’ yield (`flatMap`) case 3:
control.isContinuing = false// Break after next yield - which could
be `return nil` if there are no more results default: break }
return index // Yield next result - except for case 1 all the above
yield `index` } print(result) // prints `[0, 2, 2, 3]` note missing
"1", double "2", and last is "3"

// Demonstration of `repeat`/`forEach`/`times` like usage - note
`(_, _) -> Void?` result = [Int]() ( ..< 3).map { (_, _) -> Void? in
result.append(1) // Do whatever - in this case append to a global
returnnil// Don't yield any results } print(result) // prints `[1,
1, 1]`

Would this be a superior alternative to both `forEach` and `times` in
the library and `repeat` as a language feature?

Sent from my iPad

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

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


(Dave Abrahams) #4

-Dave

Swift didn't use to have forEach(). It was added fairly late, and I suspect (though I don't actually know) that it was done so to appease people who kept abusing map() for the same function, as well as the die-hard everything-must-be-functional crowd.

Those are two of the reasons. But the reason that put forEach over the line and convinced me to add it, just slightly, was syntactic:

for x in some.very.long[chain]
  .of.map { $0 }
  .filter { something }.whatever {
  ...
}

reads "inside-out," like nested(free(function(calls())))) vs.

some.very.long[chain]
  .of.map { $0 }
  .filter { something }.whatever
  .forEach { x in
     ...
   }

···

On Dec 30, 2015, at 8:48 PM, Kevin Ballard via swift-evolution <swift-evolution@swift.org> wrote:

Personally, I'd rather we didn't have it because it encourages people to use it, but I suppose it's better to give people an appropriate tool than to keep watching them abuse map().

-Kevin Ballard

On Wed, Dec 30, 2015, at 04:50 PM, Craig Cruden via swift-evolution wrote:

I don’t see the benefit of taking a simple declarative expression (map, flatMap, filter) and turning it into a complicated imperative/iterative loop. You already have the ability to iterate through a set and do whatever you want to do with with whatever logic you want to use using. I would have no problem for the most part removing foreach - it is more of a convenience method for doing an iterative loop through a collection - and to be quite honest rarely use outside of maybe putting in a print statement temporarily in there (but more often just turn the resulting set into comma delimited output and printing it).

On 2015-12-31, at 5:10:22, Howard Lovatt via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

You could replace `forEach` with a supped up `map` that also allowed `break` and `continue`. The following library function gives `continue` and `break` and also combines `repeat`, `times`, `forEach`, `filter`, `flatMap`, and `map` into one:

public final class MapController<E, R> {
var results = [R]()

var isContinuing = true

init<C: CollectionType where C.Generator.Element == E>(_ collection: C, sizeEstimate: Int = 0, @noescape mapper: (controller: MapController<E, R>, element: E) throws -> R?) rethrows {
        results.reserveCapacity(sizeEstimate)
for var generator = collection.generate(), element = generator.next(); element != nil && isContinuing; element = generator.next() {
let result = try mapper(controller: self, element: element!)
if let actualResult = result {
                results.append(actualResult)
            }
        }
    }
}

extensionCollectionType {
/// Controllable `map`, additional controls beyond simple `map` are:
///
/// 1. Continue without returning a result (`return nil`)
/// 2. Return multiple results (`control.results += [...]` then `return nil`)
/// 3. Break (`control.isContinuing = false` then `return nil`)
///
/// These additional controls allow this `map` to function like `repeat`, `times`, `forEach`, `filter`, `flatMap`, and `map` combined into one as well as providing an early termination (break).
@warn_unused_result func map<R>(sizeEstimate sizeEstimate: Int = 0, @noescape mapper: (controller: MapController<Self.Generator.Element, R>, element: Self.Generator.Element) throws -> R?) rethrows -> [R] {
return try MapController(self, sizeEstimate: sizeEstimate, mapper: mapper).results
    }
}

// Demonstration of full functionality including continue, break, and multiple returns
var result = (0 ..< 5).map { (control, index) -> Int? in
switch index {
case 1:
returnnil// Continue - skip 1 (`filter`)
case 2:
        control.results.append(2) // Yield two results - this one and the 'return’ yield (`flatMap`)
case 3:
        control.isContinuing = false// Break after next yield - which could be `return nil` if there are no more results
default:
break
    }
return index // Yield next result - except for case 1 all the above yield `index`
}
print(result) // prints `[0, 2, 2, 3]` note missing "1", double "2", and last is "3"

// Demonstration of `repeat`/`forEach`/`times` like usage - note `(_, _) -> Void?`
result = [Int]()
(0 ..< 3).map { (_, _) -> Void? in
    result.append(1) // Do whatever - in this case append to a global
returnnil// Don't yield any results
}
print(result) // prints `[1, 1, 1]`

Would this be a superior alternative to both `forEach` and `times` in the library and `repeat` as a language feature?

Sent from my iPad

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

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

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


(Craig Cruden) #5

I don’t see the benefit of taking a simple declarative expression (map, flatMap, filter) and turning it into a complicated imperative/iterative loop. You already have the ability to iterate through a set and do whatever you want to do with with whatever logic you want to use using. I would have no problem for the most part removing foreach - it is more of a convenience method for doing an iterative loop through a collection - and to be quite honest rarely use outside of maybe putting in a print statement temporarily in there (but more often just turn the resulting set into comma delimited output and printing it).

···

On 2015-12-31, at 5:10:22, Howard Lovatt via swift-evolution <swift-evolution@swift.org> wrote:

You could replace `forEach` with a supped up `map` that also allowed `break` and `continue`. The following library function gives `continue` and `break` and also combines `repeat`, `times`, `forEach`, `filter`, `flatMap`, and `map` into one:

public final class MapController<E, R> {
    var results = [R]()
    
    var isContinuing = true
    
    init<C: CollectionType where C.Generator.Element == E>(_ collection: C, sizeEstimate: Int = 0, @noescape mapper: (controller: MapController<E, R>, element: E) throws -> R?) rethrows {
        results.reserveCapacity(sizeEstimate)
        for var generator = collection.generate(), element = generator.next(); element != nil && isContinuing; element = generator.next() {
            let result = try mapper(controller: self, element: element!)
            if let actualResult = result {
                results.append(actualResult)
            }
        }
    }
}

extension CollectionType {
    /// Controllable `map`, additional controls beyond simple `map` are:
    ///
    /// 1. Continue without returning a result (`return nil`)
    /// 2. Return multiple results (`control.results += [...]` then `return nil`)
    /// 3. Break (`control.isContinuing = false` then `return nil`)
    ///
    /// These additional controls allow this `map` to function like `repeat`, `times`, `forEach`, `filter`, `flatMap`, and `map` combined into one as well as providing an early termination (break).
    @warn_unused_result func map<R>(sizeEstimate sizeEstimate: Int = 0, @noescape mapper: (controller: MapController<Self.Generator.Element, R>, element: Self.Generator.Element) throws -> R?) rethrows -> [R] {
        return try MapController(self, sizeEstimate: sizeEstimate, mapper: mapper).results
    }
}

// Demonstration of full functionality including continue, break, and multiple returns
var result = (0 ..< 5).map { (control, index) -> Int? in
    switch index {
    case 1:
        return nil // Continue - skip 1 (`filter`)
    case 2:
        control.results.append(2) // Yield two results - this one and the 'return’ yield (`flatMap`)
    case 3:
        control.isContinuing = false // Break after next yield - which could be `return nil` if there are no more results
    default:
        break
    }
    return index // Yield next result - except for case 1 all the above yield `index`
}
print(result) // prints `[0, 2, 2, 3]` note missing "1", double "2", and last is "3"

// Demonstration of `repeat`/`forEach`/`times` like usage - note `(_, _) -> Void?`
result = [Int]()
(0 ..< 3).map { (_, _) -> Void? in
    result.append(1) // Do whatever - in this case append to a global
    return nil // Don't yield any results
}
print(result) // prints `[1, 1, 1]`

Would this be a superior alternative to both `forEach` and `times` in the library and `repeat` as a language feature?

Sent from my iPad

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


(ilya) #6

I like having separate forEach. As already said, forEach produces different
visual grouping of logic compared to for operator. It's especially useful
if you just pass a named function to it.

forEach is also not the same as map:

let block: Int -> Void = ...
[1,2,3].map(block)

Here the result has the type [Void], not Void and the compiler correctly
produces a warning. We'd have to explicitly assign the result to silence
it, which now hides the fact that block wasn't producing anything in the
first place.

This will hold true for any advanced variant of map.

Ilya.

···

On Thu, Dec 31, 2015 at 10:30 Dave Abrahams via swift-evolution < swift-evolution@swift.org> wrote:

-Dave

On Dec 30, 2015, at 8:48 PM, Kevin Ballard via swift-evolution < > swift-evolution@swift.org> wrote:

Swift didn't use to have forEach(). It was added fairly late, and I
suspect (though I don't actually know) that it was done so to appease
people who kept abusing map() for the same function, as well as the
die-hard everything-must-be-functional crowd.

Those are two of the reasons. But the reason that put forEach over the
line and convinced me to add it, just slightly, was syntactic:

for x in some.very.long[chain]
  .of.map { $0 }
  .filter { something }.whatever {
  ...
}

reads "inside-out," like nested(free(function(calls())))) vs.

some.very.long[chain]
  .of.map { $0 }
  .filter { something }.whatever
  .forEach { x in
     ...
   }

Personally, I'd rather we didn't have it because it encourages people to
use it, but I suppose it's better to give people an appropriate tool than
to keep watching them abuse map().

-Kevin Ballard

On Wed, Dec 30, 2015, at 04:50 PM, Craig Cruden via swift-evolution wrote:

I don’t see the benefit of taking a simple declarative expression (map,
flatMap, filter) and turning it into a complicated imperative/iterative
loop. You already have the ability to iterate through a set and do
whatever you want to do with with whatever logic you want to use using. I
would have no problem for the most part removing foreach - it is more of a
convenience method for doing an iterative loop through a collection - and
to be quite honest rarely use outside of maybe putting in a print statement
temporarily in there (but more often just turn the resulting set into comma
delimited output and printing it).

On 2015-12-31, at 5:10:22, Howard Lovatt via swift-evolution < > swift-evolution@swift.org> wrote:

You could replace `forEach` with a supped up `map` that also allowed
`break` and `continue`. The following library function gives `continue`
and `break` and also combines `repeat`, `times`, `forEach`, `filter`,
`flatMap`, and `map` into one:

public final class MapController<E, R> {
var results = [R]()

var isContinuing = true

init<C: CollectionType where C.Generator.Element == E>(_ collection: C,
sizeEstimate: Int = 0, @noescape mapper: (controller: MapController<E,
>, element: E) throws -> R?) rethrows {
        results.reserveCapacity(sizeEstimate)
for var generator = collection.generate(), element = generator.next();
element != nil && isContinuing; element = generator.next() {
let result = try mapper(controller: self, element: element!)
if let actualResult = result {
                results.append(actualResult)
            }
        }
    }
}

extensionCollectionType {
/// Controllable `map`, additional controls beyond simple `map` are:
///
/// 1. Continue without returning a result (`return nil`)
/// 2. Return multiple results (`control.results += [...]` then `return
nil`)
/// 3. Break (`control.isContinuing = false` then `return nil`)
///
/// These additional controls allow this `map` to function like `repeat`,
`times`, `forEach`, `filter`, `flatMap`, and `map` combined into one as
well as providing an early termination (break).
@warn_unused_result func map<R>(sizeEstimate sizeEstimate: Int = 0,
@noescape mapper: (controller: MapController<Self.Generator.Element, R>,
element: Self.Generator.Element) throws -> R?) rethrows -> [R] {
return try MapController(self, sizeEstimate: sizeEstimate, mapper:
mapper).results
    }
}

// Demonstration of full functionality including continue, break, and
multiple returns
var result = (0 ..< 5).map { (control, index) -> Int? in
switch index {
case 1:
returnnil// Continue - skip 1 (`filter`)
case 2:
        control.results.append(2) // Yield two results - this one and the
'return’ yield (`flatMap`)
case 3:
        control.isContinuing = false// Break after next yield - which
could be `return nil` if there are no more results
default:
break
    }
return index // Yield next result - except for case 1 all the above yield
`index`
}
print(result) // prints `[0, 2, 2, 3]` note missing "1", double "2", and
last is "3"

// Demonstration of `repeat`/`forEach`/`times` like usage - note `(_, _)
-> Void?`
result = [Int]()
(0 ..< 3).map { (_, _) -> Void? in
    result.append(1) // Do whatever - in this case append to a global
returnnil// Don't yield any results
}
print(result) // prints `[1, 1, 1]`

Would this be a superior alternative to both `forEach` and `times` in the
library and `repeat` as a language feature?

Sent from my iPad

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

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

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

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


(Howard Lovatt) #7

I suspect that if there were an 'advanced' `map` it would largely eliminate `forEach` since a main use of `forEach` is because of limitation in map like multiple returns, combined map and filtering, etc.

The comment that you have to ignore a warning is however a valid point, perhaps like other languages, e.g. Java, you could have a `@suppress_unused_result_warning` annotation.

···

Sent from my iPad

On 31 Dec 2015, at 9:41 PM, ilya via swift-evolution <swift-evolution@swift.org> wrote:

I like having separate forEach. As already said, forEach produces different visual grouping of logic compared to for operator. It's especially useful if you just pass a named function to it.

forEach is also not the same as map:

let block: Int -> Void = ...
[1,2,3].map(block)

Here the result has the type [Void], not Void and the compiler correctly produces a warning. We'd have to explicitly assign the result to silence it, which now hides the fact that block wasn't producing anything in the first place.

This will hold true for any advanced variant of map.

Ilya.

On Thu, Dec 31, 2015 at 10:30 Dave Abrahams via swift-evolution <swift-evolution@swift.org> wrote:

-Dave

On Dec 30, 2015, at 8:48 PM, Kevin Ballard via swift-evolution <swift-evolution@swift.org> wrote:

Swift didn't use to have forEach(). It was added fairly late, and I suspect (though I don't actually know) that it was done so to appease people who kept abusing map() for the same function, as well as the die-hard everything-must-be-functional crowd.

Those are two of the reasons. But the reason that put forEach over the line and convinced me to add it, just slightly, was syntactic:

for x in some.very.long[chain]
  .of.map { $0 }
  .filter { something }.whatever {
  ...
}

reads "inside-out," like nested(free(function(calls())))) vs.

some.very.long[chain]
  .of.map { $0 }
  .filter { something }.whatever
  .forEach { x in
     ...
   }

Personally, I'd rather we didn't have it because it encourages people to use it, but I suppose it's better to give people an appropriate tool than to keep watching them abuse map().

-Kevin Ballard

On Wed, Dec 30, 2015, at 04:50 PM, Craig Cruden via swift-evolution wrote:
I don’t see the benefit of taking a simple declarative expression (map, flatMap, filter) and turning it into a complicated imperative/iterative loop. You already have the ability to iterate through a set and do whatever you want to do with with whatever logic you want to use using. I would have no problem for the most part removing foreach - it is more of a convenience method for doing an iterative loop through a collection - and to be quite honest rarely use outside of maybe putting in a print statement temporarily in there (but more often just turn the resulting set into comma delimited output and printing it).

On 2015-12-31, at 5:10:22, Howard Lovatt via swift-evolution <swift-evolution@swift.org> wrote:

You could replace `forEach` with a supped up `map` that also allowed `break` and `continue`. The following library function gives `continue` and `break` and also combines `repeat`, `times`, `forEach`, `filter`, `flatMap`, and `map` into one:

public final class MapController<E, R> {
var results = [R]()

var isContinuing = true

init<C: CollectionType where C.Generator.Element == E>(_ collection: C, sizeEstimate: Int = 0, @noescape mapper: (controller: MapController<E, R>, element: E) throws -> R?) rethrows {
        results.reserveCapacity(sizeEstimate)
for var generator = collection.generate(), element = generator.next(); element != nil && isContinuing; element = generator.next() {
let result = try mapper(controller: self, element: element!)
if let actualResult = result {
                results.append(actualResult)
            }
        }
    }
}

extensionCollectionType {
/// Controllable `map`, additional controls beyond simple `map` are:
///
/// 1. Continue without returning a result (`return nil`)
/// 2. Return multiple results (`control.results += [...]` then `return nil`)
/// 3. Break (`control.isContinuing = false` then `return nil`)
///
/// These additional controls allow this `map` to function like `repeat`, `times`, `forEach`, `filter`, `flatMap`, and `map` combined into one as well as providing an early termination (break).
@warn_unused_result func map<R>(sizeEstimate sizeEstimate: Int = 0, @noescape mapper: (controller: MapController<Self.Generator.Element, R>, element: Self.Generator.Element) throws -> R?) rethrows -> [R] {
return try MapController(self, sizeEstimate: sizeEstimate, mapper: mapper).results
    }
}

// Demonstration of full functionality including continue, break, and multiple returns
var result = (0 ..< 5).map { (control, index) -> Int? in
switch index {
case 1:
returnnil// Continue - skip 1 (`filter`)
case 2:
        control.results.append(2) // Yield two results - this one and the 'return’ yield (`flatMap`)
case 3:
        control.isContinuing = false// Break after next yield - which could be `return nil` if there are no more results
default:
break
    }
return index // Yield next result - except for case 1 all the above yield `index`
}
print(result) // prints `[0, 2, 2, 3]` note missing "1", double "2", and last is "3"

// Demonstration of `repeat`/`forEach`/`times` like usage - note `(_, _) -> Void?`
result = [Int]()
(0 ..< 3).map { (_, _) -> Void? in
    result.append(1) // Do whatever - in this case append to a global
returnnil// Don't yield any results
}
print(result) // prints `[1, 1, 1]`

Would this be a superior alternative to both `forEach` and `times` in the library and `repeat` as a language feature?

Sent from my iPad

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

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

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

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

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


(Tim Hawkins) #8

Just my 2 cents,

forEach is a simple concept to understand, it exists in most other
languages that have a language level understanding of containers, retaining
it has no downsides, but has the upside of making swift more approachable.

'map' on the otherhand may be much more powerful a construct, but it is
less famliar as a concept.

Is there any reason why both cant coexist, with one being a simple form of
the other.

···

On Jan 1, 2016 7:31 AM, "Howard Lovatt via swift-evolution" < swift-evolution@swift.org> wrote:

I suspect that if there were an 'advanced' `map` it would largely
eliminate `forEach` since a main use of `forEach` is because of
limitation in map like multiple returns, combined map and filtering, etc.

The comment that you have to ignore a warning is however a valid point,
perhaps like other languages, e.g. Java, you could have a
`@suppress_unused_result_warning` annotation.

Sent from my iPad

On 31 Dec 2015, at 9:41 PM, ilya via swift-evolution < > swift-evolution@swift.org> wrote:

I like having separate forEach. As already said, forEach produces
different visual grouping of logic compared to for operator. It's
especially useful if you just pass a named function to it.

forEach is also not the same as map:

let block: Int -> Void = ...
[1,2,3].map(block)

Here the result has the type [Void], not Void and the compiler correctly
produces a warning. We'd have to explicitly assign the result to silence
it, which now hides the fact that block wasn't producing anything in the
first place.

This will hold true for any advanced variant of map.

Ilya.
On Thu, Dec 31, 2015 at 10:30 Dave Abrahams via swift-evolution < > swift-evolution@swift.org> wrote:

-Dave

On Dec 30, 2015, at 8:48 PM, Kevin Ballard via swift-evolution < >> swift-evolution@swift.org> wrote:

Swift didn't use to have forEach(). It was added fairly late, and I
suspect (though I don't actually know) that it was done so to appease
people who kept abusing map() for the same function, as well as the
die-hard everything-must-be-functional crowd.

Those are two of the reasons. But the reason that put forEach over the
line and convinced me to add it, just slightly, was syntactic:

for x in some.very.long[chain]
  .of.map { $0 }
  .filter { something }.whatever {
  ...
}

reads "inside-out," like nested(free(function(calls())))) vs.

some.very.long[chain]
  .of.map { $0 }
  .filter { something }.whatever
  .forEach { x in
     ...
   }

Personally, I'd rather we didn't have it because it encourages people to
use it, but I suppose it's better to give people an appropriate tool than
to keep watching them abuse map().

-Kevin Ballard

On Wed, Dec 30, 2015, at 04:50 PM, Craig Cruden via swift-evolution wrote:

I don’t see the benefit of taking a simple declarative expression (map,
flatMap, filter) and turning it into a complicated imperative/iterative
loop. You already have the ability to iterate through a set and do
whatever you want to do with with whatever logic you want to use using. I
would have no problem for the most part removing foreach - it is more of a
convenience method for doing an iterative loop through a collection - and
to be quite honest rarely use outside of maybe putting in a print statement
temporarily in there (but more often just turn the resulting set into comma
delimited output and printing it).

On 2015-12-31, at 5:10:22, Howard Lovatt via swift-evolution < >> swift-evolution@swift.org> wrote:

You could replace `forEach` with a supped up `map` that also allowed
`break` and `continue`. The following library function gives `continue`
and `break` and also combines `repeat`, `times`, `forEach`, `filter`,
`flatMap`, and `map` into one:

public final class MapController<E, R> {
var results = [R]()

var isContinuing = true

init<C: CollectionType where C.Generator.Element == E>(_ collection: C,
sizeEstimate: Int = 0, @noescape mapper: (controller: MapController<E,
>, element: E) throws -> R?) rethrows {
        results.reserveCapacity(sizeEstimate)
for var generator = collection.generate(), element = generator.next();
element != nil && isContinuing; element = generator.next() {
let result = try mapper(controller: self, element: element!)
if let actualResult = result {
                results.append(actualResult)
            }
        }
    }
}

extensionCollectionType {
/// Controllable `map`, additional controls beyond simple `map` are:
///
/// 1. Continue without returning a result (`return nil`)
/// 2. Return multiple results (`control.results += [...]` then `return
nil`)
/// 3. Break (`control.isContinuing = false` then `return nil`)
///
/// These additional controls allow this `map` to function like `repeat`,
`times`, `forEach`, `filter`, `flatMap`, and `map` combined into one as
well as providing an early termination (break).
@warn_unused_result func map<R>(sizeEstimate sizeEstimate: Int = 0,
@noescape mapper: (controller: MapController<Self.Generator.Element, R>,
element: Self.Generator.Element) throws -> R?) rethrows -> [R] {
return try MapController(self, sizeEstimate: sizeEstimate, mapper:
mapper).results
    }
}

// Demonstration of full functionality including continue, break, and
multiple returns
var result = (0 ..< 5).map { (control, index) -> Int? in
switch index {
case 1:
returnnil// Continue - skip 1 (`filter`)
case 2:
        control.results.append(2) // Yield two results - this one and
the 'return’ yield (`flatMap`)
case 3:
        control.isContinuing = false// Break after next yield - which
could be `return nil` if there are no more results
default:
break
    }
return index // Yield next result - except for case 1 all the above
yield `index`
}
print(result) // prints `[0, 2, 2, 3]` note missing "1", double "2", and
last is "3"

// Demonstration of `repeat`/`forEach`/`times` like usage - note `(_, _)
-> Void?`
result = [Int]()
(0 ..< 3).map { (_, _) -> Void? in
    result.append(1) // Do whatever - in this case append to a global
returnnil// Don't yield any results
}
print(result) // prints `[1, 1, 1]`

Would this be a superior alternative to both `forEach` and `times` in the
library and `repeat` as a language feature?

Sent from my iPad

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

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

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

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

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

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


(Howard Lovatt) #9

You make a good point. `forEach` is simple for people new to the language and is relatively innocuous. It is also a 'stepping stone drug' to using `map`, `filter`, etc. :slight_smile:

···

Sent from my iPad

On 1 Jan 2016, at 11:55 AM, Tim Hawkins <tim.thawkins@gmail.com> wrote:

Just my 2 cents,

forEach is a simple concept to understand, it exists in most other languages that have a language level understanding of containers, retaining it has no downsides, but has the upside of making swift more approachable.

'map' on the otherhand may be much more powerful a construct, but it is less famliar as a concept.

Is there any reason why both cant coexist, with one being a simple form of the other.

On Jan 1, 2016 7:31 AM, "Howard Lovatt via swift-evolution" <swift-evolution@swift.org> wrote:
I suspect that if there were an 'advanced' `map` it would largely eliminate `forEach` since a main use of `forEach` is because of limitation in map like multiple returns, combined map and filtering, etc.

The comment that you have to ignore a warning is however a valid point, perhaps like other languages, e.g. Java, you could have a `@suppress_unused_result_warning` annotation.

Sent from my iPad

On 31 Dec 2015, at 9:41 PM, ilya via swift-evolution <swift-evolution@swift.org> wrote:

I like having separate forEach. As already said, forEach produces different visual grouping of logic compared to for operator. It's especially useful if you just pass a named function to it.

forEach is also not the same as map:

let block: Int -> Void = ...
[1,2,3].map(block)

Here the result has the type [Void], not Void and the compiler correctly produces a warning. We'd have to explicitly assign the result to silence it, which now hides the fact that block wasn't producing anything in the first place.

This will hold true for any advanced variant of map.

Ilya.

On Thu, Dec 31, 2015 at 10:30 Dave Abrahams via swift-evolution <swift-evolution@swift.org> wrote:

-Dave

On Dec 30, 2015, at 8:48 PM, Kevin Ballard via swift-evolution <swift-evolution@swift.org> wrote:

Swift didn't use to have forEach(). It was added fairly late, and I suspect (though I don't actually know) that it was done so to appease people who kept abusing map() for the same function, as well as the die-hard everything-must-be-functional crowd.

Those are two of the reasons. But the reason that put forEach over the line and convinced me to add it, just slightly, was syntactic:

for x in some.very.long[chain]
  .of.map { $0 }
  .filter { something }.whatever {
  ...
}

reads "inside-out," like nested(free(function(calls())))) vs.

some.very.long[chain]
  .of.map { $0 }
  .filter { something }.whatever
  .forEach { x in
     ...
   }

Personally, I'd rather we didn't have it because it encourages people to use it, but I suppose it's better to give people an appropriate tool than to keep watching them abuse map().

-Kevin Ballard

On Wed, Dec 30, 2015, at 04:50 PM, Craig Cruden via swift-evolution wrote:
I don’t see the benefit of taking a simple declarative expression (map, flatMap, filter) and turning it into a complicated imperative/iterative loop. You already have the ability to iterate through a set and do whatever you want to do with with whatever logic you want to use using. I would have no problem for the most part removing foreach - it is more of a convenience method for doing an iterative loop through a collection - and to be quite honest rarely use outside of maybe putting in a print statement temporarily in there (but more often just turn the resulting set into comma delimited output and printing it).

On 2015-12-31, at 5:10:22, Howard Lovatt via swift-evolution <swift-evolution@swift.org> wrote:

You could replace `forEach` with a supped up `map` that also allowed `break` and `continue`. The following library function gives `continue` and `break` and also combines `repeat`, `times`, `forEach`, `filter`, `flatMap`, and `map` into one:

public final class MapController<E, R> {
var results = [R]()

var isContinuing = true

init<C: CollectionType where C.Generator.Element == E>(_ collection: C, sizeEstimate: Int = 0, @noescape mapper: (controller: MapController<E, R>, element: E) throws -> R?) rethrows {
        results.reserveCapacity(sizeEstimate)
for var generator = collection.generate(), element = generator.next(); element != nil && isContinuing; element = generator.next() {
let result = try mapper(controller: self, element: element!)
if let actualResult = result {
                results.append(actualResult)
            }
        }
    }
}

extensionCollectionType {
/// Controllable `map`, additional controls beyond simple `map` are:
///
/// 1. Continue without returning a result (`return nil`)
/// 2. Return multiple results (`control.results += [...]` then `return nil`)
/// 3. Break (`control.isContinuing = false` then `return nil`)
///
/// These additional controls allow this `map` to function like `repeat`, `times`, `forEach`, `filter`, `flatMap`, and `map` combined into one as well as providing an early termination (break).
@warn_unused_result func map<R>(sizeEstimate sizeEstimate: Int = 0, @noescape mapper: (controller: MapController<Self.Generator.Element, R>, element: Self.Generator.Element) throws -> R?) rethrows -> [R] {
return try MapController(self, sizeEstimate: sizeEstimate, mapper: mapper).results
    }
}

// Demonstration of full functionality including continue, break, and multiple returns
var result = (0 ..< 5).map { (control, index) -> Int? in
switch index {
case 1:
returnnil// Continue - skip 1 (`filter`)
case 2:
        control.results.append(2) // Yield two results - this one and the 'return’ yield (`flatMap`)
case 3:
        control.isContinuing = false// Break after next yield - which could be `return nil` if there are no more results
default:
break
    }
return index // Yield next result - except for case 1 all the above yield `index`
}
print(result) // prints `[0, 2, 2, 3]` note missing "1", double "2", and last is "3"

// Demonstration of `repeat`/`forEach`/`times` like usage - note `(_, _) -> Void?`
result = [Int]()
(0 ..< 3).map { (_, _) -> Void? in
    result.append(1) // Do whatever - in this case append to a global
returnnil// Don't yield any results
}
print(result) // prints `[1, 1, 1]`

Would this be a superior alternative to both `forEach` and `times` in the library and `repeat` as a language feature?

Sent from my iPad

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

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

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

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

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

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