Removing enumerated?


(Matthew Johnson) #21

It seems that you are leaning towards removing enumerated().

I’m actually kind of conflicted.

Replacing enumerated() with indexed() feels replacing one problem for another. Sometimes people want to number things, and might assume indexed() will be zero-based for slices.

Adding indexed() while keeping enumerated() seems too much clutter on the API though. Once we have 0… both can be expressed simply with zip, and in my view is zip(a, 0…), zip(a, a.indices), zip(1…, a) just as clear, maybe clearer in some cases as they will encourage code to show intent more (i.e. are you counting or indexing? even when they are the same, it’s better to say which). Encouraging learning about zip will also help introduce people to better ways of expressing other similar-but-different loops.

The trouble with zip is it isn’t discoverable – another entry that probably belongs on that list of criteria. Unlike enumerated, users aren’t going to stumble over it.

Maybe moving zip to be a method on Sequence rather than a free function would help with this? e.g. something like a.zipped(with: 0…), a.zipped(with: a.indices). The documentation for it could even explicitly mention the counting and index use cases. The main downside is it pushes the order of the lhs/rhs to be self first.

I think the downside is significant. I think the visual relationship of `zip(a, 0…)` with the tuples it produces is pretty important. It also scales well if (when?) it becomes variadic.

Discoverability is a problem, but no more so than with any other top level operator somebody might not be familiar with (Swift has a few of those depending on somebody’s background)

I think the discoverability problem is best addressed by identifying and raising awareness of common use cases for zip rather than just moving it so it appears in autocomplete. When people learn how it can make their code better they will use it.

···

On Feb 3, 2017, at 3:41 PM, Ben Cohen via swift-evolution <swift-evolution@swift.org> wrote:

On Feb 2, 2017, at 8:46 PM, Chris Lattner <clattner@nondot.org <mailto:clattner@nondot.org>> wrote:

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


(Ole Begemann) #22

This will work once SE-0142 "Permit where clauses to constrain associated types" (https://github.com/apple/swift-evolution/blob/master/proposals/0142-associated-types-constraints.md) is implemented and the standard library takes advantage of this.

For the time being, you need to add an explicit constraint to the function:

    func function<C: Collection>(c: C)
        where C.Indices.Iterator.Element == C.Index {
        for index in c.indices {
            print(c[index])
        }
    }

···

On 17 Feb 2017, at 16:53, Vladimir.S via swift-evolution <swift-evolution@swift.org> wrote:

Btw, in context of discussion of indices,
should this be fixed soon:

func function<C: Collection>(c: C) {
for index in c.indices {
   print(c[index])
}
}
ERROR: cannot subscript a value of type 'C' with an index of type 'C.Indices.Iterator.Element'

?
(have access for Swift 3.0.2 Release only for now, so probably this already fixed in dev version)


(Matthew Johnson) #23

I totally sympathize with users being confused. It's an interesting idea to move it to Array only.

The thing is, it does make sense (and wouldn't be confusing) to enumerate a dictionary or set. Moreover, the behavior is _exactly_ what it says on the tin: when you enumerate something in real life, there is no sense in which the number is related to some sort of index. Can we fix this by documentation? Like, a big blaring "don't use this when you want the index”?

+1. A similar method on collection that provides indices might be useful but that doesn’t mean we should remove `enumerated`. User confusion should be addressed by documentation.

···

On Jan 31, 2017, at 10:36 AM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org> wrote:

On Tue, Jan 31, 2017 at 09:35 Ole Begemann via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
On 31/01/2017 16:19, Ole Begemann via swift-evolution wrote:
> Here are three previous discussion about this topic:
>
> 1) December 2015: [Idea] Add an (Index, Element) sequence to
> CollectionType
> https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151221/004561.html
> and
> https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151228/004626.html
>
>
> 2) April 2016: [Idea] Replace enumerate() with something more explicit
> https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160411/015074.html
>
>
> 3) September 2016: [Proposal draft] Introducing `indexed()` collections
> https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160926/027355.html

To clarify, the discussions I linked to don't all propose to remove or
replace `enumerated()`, but they all talk about the potential confusion
about what `enumerated()` does and does not do.

_______________________________________________
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


(Sean Heber) #24

In practical usage, I rarely use enumerated() - and almost every time I do use it, I end up refactoring 20 minutes later to not use it. Maybe that's just me, though. I imagine this isn’t helpful feedback. :stuck_out_tongue: But perhaps we (or I) need to understand what it’s being used for or if there’s a different way to think about it.

l8r
Sean

···

On Jan 31, 2017, at 10:36 AM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org> wrote:

I totally sympathize with users being confused. It's an interesting idea to move it to Array only.

The thing is, it does make sense (and wouldn't be confusing) to enumerate a dictionary or set. Moreover, the behavior is _exactly_ what it says on the tin: when you enumerate something in real life, there is no sense in which the number is related to some sort of index. Can we fix this by documentation? Like, a big blaring "don't use this when you want the index"?
On Tue, Jan 31, 2017 at 09:35 Ole Begemann via swift-evolution <swift-evolution@swift.org> wrote:
On 31/01/2017 16:19, Ole Begemann via swift-evolution wrote:
> Here are three previous discussion about this topic:
>
> 1) December 2015: [Idea] Add an (Index, Element) sequence to
> CollectionType
> https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151221/004561.html
> and
> https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151228/004626.html
>
>
> 2) April 2016: [Idea] Replace enumerate() with something more explicit
> https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160411/015074.html
>
>
> 3) September 2016: [Proposal draft] Introducing `indexed()` collections
> https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160926/027355.html

To clarify, the discussions I linked to don't all propose to remove or
replace `enumerated()`, but they all talk about the potential confusion
about what `enumerated()` does and does not do.

_______________________________________________
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


(Chris Eidhof) #25

I agree that it's very useful. I use it regularly. The documentation isn't
that unclear, imo. To me, the underlying problem isn't enumerated. I think
the underlying cause is that collections aren't indexed with zero based
indices.if you don't understand this (which is the case for many
experienced programmers new to Swift) it's hard to understand, and (too)
easy to make a mistake.

···

On Tue, 31 Jan 2017 at 17:41, Matthew Johnson via swift-evolution < swift-evolution@swift.org> wrote:

On Jan 31, 2017, at 10:36 AM, Xiaodi Wu via swift-evolution < > swift-evolution@swift.org> wrote:

I totally sympathize with users being confused. It's an interesting idea
to move it to Array only.

The thing is, it does make sense (and wouldn't be confusing) to enumerate
a dictionary or set. Moreover, the behavior is _exactly_ what it says on
the tin: when you enumerate something in real life, there is no sense in
which the number is related to some sort of index. Can we fix this by
documentation? Like, a big blaring "don't use this when you want the index”?

+1. A similar method on collection that provides indices might be useful
but that doesn’t mean we should remove `enumerated`. User confusion should
be addressed by documentation.

On Tue, Jan 31, 2017 at 09:35 Ole Begemann via swift-evolution < > swift-evolution@swift.org> wrote:

On 31/01/2017 16:19, Ole Begemann via swift-evolution wrote:
> Here are three previous discussion about this topic:
>
> 1) December 2015: [Idea] Add an (Index, Element) sequence to
> CollectionType
>
https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151221/004561.html
> and
>
https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151228/004626.html
>
>
> 2) April 2016: [Idea] Replace enumerate() with something more explicit
>
https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160411/015074.html
>
>
> 3) September 2016: [Proposal draft] Introducing `indexed()`
collections
>
https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160926/027355.html

To clarify, the discussions I linked to don't all propose to remove or
replace `enumerated()`, but they all talk about the potential confusion
about what `enumerated()` does and does not do.

_______________________________________________
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

--
Sent from my phone


(Ben Cohen) #26

I don’t know if that’s true.

Here’s an example (the only use of enumerated) from Alamofire:

let acceptLanguage = Locale.preferredLanguages.prefix(6).enumerated().map { index, languageCode in
    let quality = 1.0 - (Double(index) * 0.1)
    return "\(languageCode);q=\(quality)"
}.joined(separator: ", ")

Here the intent is a counter, not indices. They just happen to be the same. But if they’d used indexed() it would certainly hurt readability, albeit midly.

Suppose there wasn’t an enumerate or an indexed, and zipped was the standard way of doing it. That might lead to another solution:

let qualities = stride(from: 1.0, to: 0.4, by: -0.1)
let acceptLanguage = Locale.preferredLanguages.zipped(with: qualities).map {
    languageCode, quality in "\(languageCode);q=\(quality)"
}.joined(separator: ", ")

The use of stride here feels more what was intended, rather than backing into the quality via an “index” value. And avoids any risk with indexed of this getting applied incorrectly to slices.

···

On Feb 3, 2017, at 11:12 AM, Erica Sadun via swift-evolution <swift-evolution@swift.org> wrote:

I believe what people *want* is `indexed` over `enumerated`, and consistently for both array and array slices.


(Ben Cohen) #27

Hmm, that doesn’t sound very natural to me.

Then again the problem with “zip(with:)" is it’s already kind of a term of art for a version that takes a function to combine the two values.

There’s also the question of how to spell a non-truncating versions (returning optionals or taking a pad value).

···

On Feb 3, 2017, at 3:27 PM, Dave Abrahams via swift-evolution <swift-evolution@swift.org> wrote:

I don't always make zip a method, but when I do, its argument label is
“to:”


(Matthew Johnson) #28

I agree that it's very useful. I use it regularly. The documentation isn't that unclear, imo. To me, the underlying problem isn't enumerated. I think the underlying cause is that collections aren't indexed with zero based indices.if you don't understand this (which is the case for many experienced programmers new to Swift) it's hard to understand, and (too) easy to make a mistake.

This indicates that the underlying problem is *not* enumerated at all. The underlying problem is that Swift is still a relatively new language and it does some things differently than other languages (for very good reasons).

You’re making a great case for the need to continue spreading knowledge about Swift’s collection model through the community.

I don’t think it’s problematic for an experienced programmer who is new to the language to bump up against this when the reach for `enumerated`. They’re going to need to learn the collection model sooner or later.

···

On Jan 31, 2017, at 10:46 AM, Chris Eidhof <chris@eidhof.nl> wrote:

On Tue, 31 Jan 2017 at 17:41, Matthew Johnson via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Jan 31, 2017, at 10:36 AM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I totally sympathize with users being confused. It's an interesting idea to move it to Array only.

The thing is, it does make sense (and wouldn't be confusing) to enumerate a dictionary or set. Moreover, the behavior is _exactly_ what it says on the tin: when you enumerate something in real life, there is no sense in which the number is related to some sort of index. Can we fix this by documentation? Like, a big blaring "don't use this when you want the index”?

+1. A similar method on collection that provides indices might be useful but that doesn’t mean we should remove `enumerated`. User confusion should be addressed by documentation.

On Tue, Jan 31, 2017 at 09:35 Ole Begemann via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
On 31/01/2017 16:19, Ole Begemann via swift-evolution wrote:
> Here are three previous discussion about this topic:
>
> 1) December 2015: [Idea] Add an (Index, Element) sequence to
> CollectionType
> https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151221/004561.html
> and
> https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151228/004626.html
>
>
> 2) April 2016: [Idea] Replace enumerate() with something more explicit
> https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160411/015074.html
>
>
> 3) September 2016: [Proposal draft] Introducing `indexed()` collections
> https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160926/027355.html

To clarify, the discussions I linked to don't all propose to remove or
replace `enumerated()`, but they all talk about the potential confusion
about what `enumerated()` does and does not do.

_______________________________________________
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 <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution
--
Sent from my phone


(Jacob Bandes-Storch) #29

I'm still a fan of that indexed() proposal...

···

On Tue, Jan 31, 2017 at 8:52 AM Matthew Johnson via swift-evolution < swift-evolution@swift.org> wrote:

On Jan 31, 2017, at 10:46 AM, Chris Eidhof <chris@eidhof.nl> wrote:

I agree that it's very useful. I use it regularly. The documentation isn't
that unclear, imo. To me, the underlying problem isn't enumerated. I think
the underlying cause is that collections aren't indexed with zero based
indices.if you don't understand this (which is the case for many
experienced programmers new to Swift) it's hard to understand, and (too)
easy to make a mistake.

This indicates that the underlying problem is *not* enumerated at all.
The underlying problem is that Swift is still a relatively new language and
it does some things differently than other languages (for very good
reasons).

You’re making a great case for the need to continue spreading knowledge
about Swift’s collection model through the community.

I don’t think it’s problematic for an experienced programmer who is new to
the language to bump up against this when the reach for `enumerated`.
They’re going to need to learn the collection model sooner or later.

On Tue, 31 Jan 2017 at 17:41, Matthew Johnson via swift-evolution < > swift-evolution@swift.org> wrote:

On Jan 31, 2017, at 10:36 AM, Xiaodi Wu via swift-evolution < > swift-evolution@swift.org> wrote:

I totally sympathize with users being confused. It's an interesting idea
to move it to Array only.

The thing is, it does make sense (and wouldn't be confusing) to enumerate
a dictionary or set. Moreover, the behavior is _exactly_ what it says on
the tin: when you enumerate something in real life, there is no sense in
which the number is related to some sort of index. Can we fix this by
documentation? Like, a big blaring "don't use this when you want the index”?

+1. A similar method on collection that provides indices might be useful
but that doesn’t mean we should remove `enumerated`. User confusion should
be addressed by documentation.

On Tue, Jan 31, 2017 at 09:35 Ole Begemann via swift-evolution < > swift-evolution@swift.org> wrote:

On 31/01/2017 16:19, Ole Begemann via swift-evolution wrote:
> Here are three previous discussion about this topic:
>
> 1) December 2015: [Idea] Add an (Index, Element) sequence to
> CollectionType
>
https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151221/004561.html
> and
>
https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151228/004626.html
>
>
> 2) April 2016: [Idea] Replace enumerate() with something more explicit
>
https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160411/015074.html
>
>
> 3) September 2016: [Proposal draft] Introducing `indexed()`
collections
>
https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160926/027355.html

To clarify, the discussions I linked to don't all propose to remove or
replace `enumerated()`, but they all talk about the potential confusion
about what `enumerated()` does and does not do.

_______________________________________________
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

--
Sent from my phone

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


(Erica Sadun) #30

I think enumerated as it stands is an attractive nuisance / moral hazard. Most of the language learners I interact with miss the point and the nuance of how it works.

let list = [0, 1, 2, 3, 4]
let slice = list[2...3]
for (idx, value) in slice.enumerated() {
    print(idx, value)
}

I think people would not expect 0, 2 / 1, 3. I also don’t think they’d expect the actual outcome from a dictionary, whether index or enumeration because there’s no inherent semantic “enumeration” of dictionary values:

let dict = [0:"a", 1:"b", 2:"c"]
for (idx, value) in dict.enumerated() {
    print(idx, value)
}

0 (2, "c")
1 (0, "a")
2 (1, "b")

I’d like to see enumerated gone and I have a mild preference for introducing indexed, either under its own name or as a new behavior for enumerated (although T where T.Iterator.Element is Int)

120 gists with “enumerated”, of which a casual scan shows that almost none of them are actually using it meaningfully. (Take a look.) I think I did this API right: https://api.github.com/search/repositories?q=enumerate+language:swift <https://api.github.com/search/repositories?q=enumerate+language:swift> and if so, not much in repos.

···

On Feb 3, 2017, at 2:58 PM, Ben Cohen <ben_cohen@apple.com> wrote:

On Feb 3, 2017, at 11:12 AM, Erica Sadun via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I believe what people *want* is `indexed` over `enumerated`, and consistently for both array and array slices.

I don’t know if that’s true.

Here’s an example (the only use of enumerated) from Alamofire:

let acceptLanguage = Locale.preferredLanguages.prefix(6).enumerated().map { index, languageCode in
    let quality = 1.0 - (Double(index) * 0.1)
    return "\(languageCode);q=\(quality)"
}.joined(separator: ", ")

Here the intent is a counter, not indices. They just happen to be the same. But if they’d used indexed() it would certainly hurt readability, albeit midly.

Suppose there wasn’t an enumerate or an indexed, and zipped was the standard way of doing it. That might lead to another solution:

let qualities = stride(from: 1.0, to: 0.4, by: -0.1)
let acceptLanguage = Locale.preferredLanguages.zipped(with: qualities).map {
    languageCode, quality in "\(languageCode);q=\(quality)"
}.joined(separator: ", ")

The use of stride here feels more what was intended, rather than backing into the quality via an “index” value. And avoids any risk with indexed of this getting applied incorrectly to slices.


(Dave Abrahams) #31

Is there a use-case for such a zip?

···

on Fri Feb 03 2017, Ben Cohen <swift-evolution@swift.org> wrote:

On Feb 3, 2017, at 3:27 PM, Dave Abrahams via swift-evolution >> <swift-evolution@swift.org> wrote:

I don't always make zip a method, but when I do, its argument label is
“to:”

Hmm, that doesn’t sound very natural to me.

Then again the problem with “zip(with:)" is it’s already kind of a
term of art for a version that takes a function to combine the two
values.

There’s also the question of how to spell a non-truncating versions
(returning optionals or taking a pad value).

--
-Dave


(Dave Abrahams) #32

Ben's not arguing that enumerated should stay. He's just saying that
there's no good reason to provide indexed(), and I agree with that.

···

on Fri Feb 03 2017, Erica Sadun <erica-AT-ericasadun.com> wrote:

On Feb 3, 2017, at 2:58 PM, Ben Cohen <ben_cohen@apple.com> wrote:

On Feb 3, 2017, at 11:12 AM, Erica Sadun via swift-evolution <swift-evolution@swift.org > <mailto:swift-evolution@swift.org>> wrote:

I believe what people *want* is `indexed` over `enumerated`, and consistently for both array and array slices.

I don’t know if that’s true.

Here’s an example (the only use of enumerated) from Alamofire:

let acceptLanguage = Locale.preferredLanguages.prefix(6).enumerated().map { index, languageCode in
    let quality = 1.0 - (Double(index) * 0.1)
    return "\(languageCode);q=\(quality)"
}.joined(separator: ", ")

Here the intent is a counter, not indices. They just happen to be the same. But if they’d used indexed() it would certainly hurt readability, albeit midly.

Suppose there wasn’t an enumerate or an indexed, and zipped was the standard way of doing it. That might lead to another solution:

let qualities = stride(from: 1.0, to: 0.4, by: -0.1)
let acceptLanguage = Locale.preferredLanguages.zipped(with: qualities).map {
    languageCode, quality in "\(languageCode);q=\(quality)"
}.joined(separator: ", ")

The use of stride here feels more what was intended, rather than
backing into the quality via an “index” value. And avoids any risk
with indexed of this getting applied incorrectly to slices.

I think enumerated as it stands is an attractive nuisance / moral
hazard. Most of the language learners I interact with miss the point
and the nuance of how it works.

let list = [0, 1, 2, 3, 4]
let slice = list[2...3]
for (idx, value) in slice.enumerated() {
    print(idx, value)
}

I think people would not expect 0, 2 / 1, 3. I also don’t think they’d
expect the actual outcome from a dictionary, whether index or
enumeration because there’s no inherent semantic “enumeration” of
dictionary values:

let dict = [0:"a", 1:"b", 2:"c"]
for (idx, value) in dict.enumerated() {
    print(idx, value)
}

0 (2, "c")
1 (0, "a")
2 (1, "b")

I’d like to see enumerated gone and I have a mild preference for
introducing indexed, either under its own name or as a new behavior
for enumerated (although T where T.Iterator.Element is Int)

120 gists with “enumerated”, of which a casual scan shows that almost
none of them are actually using it meaningfully. (Take a look.) I
think I did this API right:
https://api.github.com/search/repositories?q=enumerate+language:swift
<https://api.github.com/search/repositories?q=enumerate+language:swift>
and if so, not much in repos.

--
-Dave


(Erica Sadun) #33

And I think I just argued my way to agreeing with him.

-- E

···

On Feb 3, 2017, at 4:20 PM, Dave Abrahams <dabrahams@apple.com> wrote:

on Fri Feb 03 2017, Erica Sadun <erica-AT-ericasadun.com> wrote:

On Feb 3, 2017, at 2:58 PM, Ben Cohen <ben_cohen@apple.com> wrote:

On Feb 3, 2017, at 11:12 AM, Erica Sadun via swift-evolution <swift-evolution@swift.org >> <mailto:swift-evolution@swift.org>> wrote:

I believe what people *want* is `indexed` over `enumerated`, and consistently for both array and array slices.

I don’t know if that’s true.

Here’s an example (the only use of enumerated) from Alamofire:

let acceptLanguage = Locale.preferredLanguages.prefix(6).enumerated().map { index, languageCode in
   let quality = 1.0 - (Double(index) * 0.1)
   return "\(languageCode);q=\(quality)"
}.joined(separator: ", ")

Here the intent is a counter, not indices. They just happen to be the same. But if they’d used indexed() it would certainly hurt readability, albeit midly.

Suppose there wasn’t an enumerate or an indexed, and zipped was the standard way of doing it. That might lead to another solution:

let qualities = stride(from: 1.0, to: 0.4, by: -0.1)
let acceptLanguage = Locale.preferredLanguages.zipped(with: qualities).map {
   languageCode, quality in "\(languageCode);q=\(quality)"
}.joined(separator: ", ")

The use of stride here feels more what was intended, rather than
backing into the quality via an “index” value. And avoids any risk
with indexed of this getting applied incorrectly to slices.

I think enumerated as it stands is an attractive nuisance / moral
hazard. Most of the language learners I interact with miss the point
and the nuance of how it works.

let list = [0, 1, 2, 3, 4]
let slice = list[2...3]
for (idx, value) in slice.enumerated() {
   print(idx, value)
}

I think people would not expect 0, 2 / 1, 3. I also don’t think they’d
expect the actual outcome from a dictionary, whether index or
enumeration because there’s no inherent semantic “enumeration” of
dictionary values:

let dict = [0:"a", 1:"b", 2:"c"]
for (idx, value) in dict.enumerated() {
   print(idx, value)
}

0 (2, "c")
1 (0, "a")
2 (1, "b")

I’d like to see enumerated gone and I have a mild preference for
introducing indexed, either under its own name or as a new behavior
for enumerated (although T where T.Iterator.Element is Int)

120 gists with “enumerated”, of which a casual scan shows that almost
none of them are actually using it meaningfully. (Take a look.) I
think I did this API right:
https://api.github.com/search/repositories?q=enumerate+language:swift
<https://api.github.com/search/repositories?q=enumerate+language:swift>
and if so, not much in repos.

Ben's not arguing that enumerated should stay. He's just saying that
there's no good reason to provide indexed(), and I agree with that.

--
-Dave


(Ben Cohen) #34

I don't always make zip a method, but when I do, its argument label is
“to:”

Hmm, that doesn’t sound very natural to me.

Then again the problem with “zip(with:)" is it’s already kind of a
term of art for a version that takes a function to combine the two
values.

There’s also the question of how to spell a non-truncating versions
(returning optionals or taking a pad value).

Is there a use-case for such a zip?

Whenever it's not OK to not silently discard the elements in the longer list (which can be a correctness trap of zip if you're not careful). Say you're matching up contestants from two groups, but want to give byes to the unmatched contestants in the larger group. Or you're generating a list of positioned racers in a 8-car race, putting in a computer player when you run out of real players.

···

On Feb 4, 2017, at 14:43, Dave Abrahams via swift-evolution <swift-evolution@swift.org> wrote:
on Fri Feb 03 2017, Ben Cohen <swift-evolution@swift.org> wrote:

On Feb 3, 2017, at 3:27 PM, Dave Abrahams via swift-evolution >>> <swift-evolution@swift.org> wrote:

--
-Dave

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


(Shawn Erickson) #35

I use enumerated in many location in my code and have never expected it to
be indexes but a counting of how many times I have looped. It says clearly
what is does on the tin: "Returns a sequence of pairs (n, x), where n
represents a consecutive integer starting at zero, and x represents an
element of the sequence.". I get some new folks have confusion but reading
the docs is always part of learning IMHO.

I would hate to see it removed without strong replacement that is
reasonably readable. I think leveraging zip and the potential range style
is on the edge of being readable but is learnable.

-Shawn

···

On Fri, Feb 3, 2017 at 4:11 PM Erica Sadun via swift-evolution < swift-evolution@swift.org> wrote:

> On Feb 3, 2017, at 4:20 PM, Dave Abrahams <dabrahams@apple.com> wrote:
>
>
> on Fri Feb 03 2017, Erica Sadun <erica-AT-ericasadun.com> wrote:
>
>>> On Feb 3, 2017, at 2:58 PM, Ben Cohen <ben_cohen@apple.com> wrote:
>>>
>>>
>>>> On Feb 3, 2017, at 11:12 AM, Erica Sadun via swift-evolution < > swift-evolution@swift.org > >> <mailto:swift-evolution@swift.org>> wrote:
>>>>
>>
>>>> I believe what people *want* is `indexed` over `enumerated`, and
consistently for both array and array slices.
>>>>
>>>
>>> I don’t know if that’s true.
>>>
>>> Here’s an example (the only use of enumerated) from Alamofire:
>>>
>>> let acceptLanguage =
Locale.preferredLanguages.prefix(6).enumerated().map { index, languageCode
in
>>> let quality = 1.0 - (Double(index) * 0.1)
>>> return "\(languageCode);q=\(quality)"
>>> }.joined(separator: ", ")
>>>
>>> Here the intent is a counter, not indices. They just happen to be the
same. But if they’d used indexed() it would certainly hurt readability,
albeit midly.
>>>
>>> Suppose there wasn’t an enumerate or an indexed, and zipped was the
standard way of doing it. That might lead to another solution:
>>>
>>> let qualities = stride(from: 1.0, to: 0.4, by: -0.1)
>>> let acceptLanguage = Locale.preferredLanguages.zipped(with:
qualities).map {
>>> languageCode, quality in "\(languageCode);q=\(quality)"
>>> }.joined(separator: ", ")
>>>
>>> The use of stride here feels more what was intended, rather than
>>> backing into the quality via an “index” value. And avoids any risk
>>> with indexed of this getting applied incorrectly to slices.
>>>
>>
>> I think enumerated as it stands is an attractive nuisance / moral
>> hazard. Most of the language learners I interact with miss the point
>> and the nuance of how it works.
>>
>> let list = [0, 1, 2, 3, 4]
>> let slice = list[2...3]
>> for (idx, value) in slice.enumerated() {
>> print(idx, value)
>> }
>>
>> I think people would not expect 0, 2 / 1, 3. I also don’t think they’d
>> expect the actual outcome from a dictionary, whether index or
>> enumeration because there’s no inherent semantic “enumeration” of
>> dictionary values:
>>
>> let dict = [0:"a", 1:"b", 2:"c"]
>> for (idx, value) in dict.enumerated() {
>> print(idx, value)
>> }
>>
>> 0 (2, "c")
>> 1 (0, "a")
>> 2 (1, "b")
>>
>> I’d like to see enumerated gone and I have a mild preference for
>> introducing indexed, either under its own name or as a new behavior
>> for enumerated (although T where T.Iterator.Element is Int)
>>
>> 120 gists with “enumerated”, of which a casual scan shows that almost
>> none of them are actually using it meaningfully. (Take a look.) I
>> think I did this API right:
>> https://api.github.com/search/repositories?q=enumerate+language:swift
>> <https://api.github.com/search/repositories?q=enumerate+language:swift>
>> and if so, not much in repos.
>
> Ben's not arguing that enumerated should stay. He's just saying that
> there's no good reason to provide indexed(), and I agree with that.
>
> --
> -Dave

And I think I just argued my way to agreeing with him.

-- E

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


(Ben Cohen) #36

I don't always make zip a method, but when I do, its argument label is
“to:”

Hmm, that doesn’t sound very natural to me.

Then again the problem with “zip(with:)" is it’s already kind of a
term of art for a version that takes a function to combine the two
values.

There’s also the question of how to spell a non-truncating versions
(returning optionals or taking a pad value).

Is there a use-case for such a zip?

Whenever it's not OK to not silently discard the elements in the longer list (which can be a correctness trap of zip if you're not careful). Say you're matching up contestants from two groups, but want to give byes to the unmatched contestants in the larger group. Or you're generating a list of positioned racers in a 8-car race, putting in a computer player when you run out of real players.

Gah, accidental double-negation, meant "not OK to silently discard"

···

On Feb 5, 2017, at 08:12, Ben Cohen <ben_cohen@apple.com> wrote:

On Feb 4, 2017, at 14:43, Dave Abrahams via swift-evolution <swift-evolution@swift.org> wrote:
on Fri Feb 03 2017, Ben Cohen <swift-evolution@swift.org> wrote:

On Feb 3, 2017, at 3:27 PM, Dave Abrahams via swift-evolution >>>> <swift-evolution@swift.org> wrote:

--
-Dave

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


(Patrick Pijnappel) #37

Note that zip(a.indices, a) requires advancing the index twice each
iteration, which for non-array collections is not necessarily super
trivial. Considering we're talking about such a low-level operation as
iterating through a collection this might be worth considering.

Personally I'm in favor of adding indexed().

···

On Mon, Feb 6, 2017 at 3:14 AM, Ben Cohen via swift-evolution < swift-evolution@swift.org> wrote:

On Feb 5, 2017, at 08:12, Ben Cohen <ben_cohen@apple.com> wrote:

>> On Feb 4, 2017, at 14:43, Dave Abrahams via swift-evolution < > swift-evolution@swift.org> wrote:
>>
>>
>> on Fri Feb 03 2017, Ben Cohen <swift-evolution@swift.org> wrote:
>>
>>>> On Feb 3, 2017, at 3:27 PM, Dave Abrahams via swift-evolution > >>>> <swift-evolution@swift.org> wrote:
>>>>
>>>> I don't always make zip a method, but when I do, its argument label is
>>>> “to:”
>>>
>>> Hmm, that doesn’t sound very natural to me.
>>>
>>> Then again the problem with “zip(with:)" is it’s already kind of a
>>> term of art for a version that takes a function to combine the two
>>> values.
>>>
>>> There’s also the question of how to spell a non-truncating versions
>>> (returning optionals or taking a pad value).
>>
>> Is there a use-case for such a zip?
>>
>
> Whenever it's not OK to not silently discard the elements in the longer
list (which can be a correctness trap of zip if you're not careful). Say
you're matching up contestants from two groups, but want to give byes to
the unmatched contestants in the larger group. Or you're generating a list
of positioned racers in a 8-car race, putting in a computer player when you
run out of real players.
>

Gah, accidental double-negation, meant "not OK to silently discard"

>> --
>> -Dave
>>
>> _______________________________________________
>> 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


(Ben Cohen) #38

Note that zip(a.indices, a) requires advancing the index twice each iteration, which for non-array collections is not necessarily super trivial. Considering we're talking about such a low-level operation as iterating through a collection this might be worth considering.

Personally I'm in favor of adding indexed().

This isn't necessarily as much of a slam-dunk as you might think. Just as index advancement can theoretically be expensive, so can subscripting (though both need to be constant time, the constant factor could be high for either). Unless we made indexed() a protocol customization point rather than an extension (unlikely – we need to keep the number of those under control) and the collection provided a customized version, indexed() would need to use subscripting to return the element given the index it's tracking. Whereas iteration, being a much more limited forward-only API, might be implemented to be more efficient. So it's one hypothetical cost vs another. Alternatively, indexed() could be implemented to use an iterator for the elements part in parallel to the index advancement, in which case it's identical to the zip version. At least with the zip version, it's transparent which strategy is being used. In practice, for any Collection both costs should ideally be kept as small as possible, and so their cost is hopefully not material often enough to be factored into the decision of whether indexed() should exist on Collection, which should be decided on API ergonomics grounds.

PS for the standard library collections at least, any benchmarks that find that indexing/subscripting is significantly faster than iteration should be raised as bugs against the std lib/compiler/optimizer depending on where the problem is :slight_smile:

···

On Feb 5, 2017, at 16:47, Patrick Pijnappel <patrickpijnappel@gmail.com> wrote:

On Mon, Feb 6, 2017 at 3:14 AM, Ben Cohen via swift-evolution <swift-evolution@swift.org> wrote:
On Feb 5, 2017, at 08:12, Ben Cohen <ben_cohen@apple.com> wrote:

>> On Feb 4, 2017, at 14:43, Dave Abrahams via swift-evolution <swift-evolution@swift.org> wrote:
>>
>>
>> on Fri Feb 03 2017, Ben Cohen <swift-evolution@swift.org> wrote:
>>
>>>> On Feb 3, 2017, at 3:27 PM, Dave Abrahams via swift-evolution >> >>>> <swift-evolution@swift.org> wrote:
>>>>
>>>> I don't always make zip a method, but when I do, its argument label is
>>>> “to:”
>>>
>>> Hmm, that doesn’t sound very natural to me.
>>>
>>> Then again the problem with “zip(with:)" is it’s already kind of a
>>> term of art for a version that takes a function to combine the two
>>> values.
>>>
>>> There’s also the question of how to spell a non-truncating versions
>>> (returning optionals or taking a pad value).
>>
>> Is there a use-case for such a zip?
>>
>
> Whenever it's not OK to not silently discard the elements in the longer list (which can be a correctness trap of zip if you're not careful). Say you're matching up contestants from two groups, but want to give byes to the unmatched contestants in the larger group. Or you're generating a list of positioned racers in a 8-car race, putting in a computer player when you run out of real players.
>

Gah, accidental double-negation, meant "not OK to silently discard"

>> --
>> -Dave
>>
>> _______________________________________________
>> 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) #39

Note that zip(a.indices, a) requires advancing the index twice each
iteration, which for non-array collections is not necessarily super
trivial. Considering we're talking about such a low-level operation as
iterating through a collection this might be worth considering.

If you're worried about this, there's a really easy solution: write your
loop over a.indices and the subscript a using those indices to get the
element.

Personally I'm in favor of adding indexed().

As mentioned earlier in the thread, I'm strongly opposed to adding it at
this time.

···

on Sun Feb 05 2017, Patrick Pijnappel <swift-evolution@swift.org> wrote:

--
-Dave


#40

enumerated() is really useful. But leads to risky situations as described in the OP.

Couldn't it be named differently for Array and ArraySlice ?

The following code will crash at runtime, because b is ArraySlice and not Array (which can easily get unnoticed)
var a = ["Hello", "Cruel", "World!"]
var b = a.dropFirst()
for (i, e) in b.enumerated() {
if e.hasPrefix("C") {
b[i] = e.uppercased()
}
}

But this works without problem
var a = ["Hello", "Cruel", "World!"]
var b = Array(a.dropFirst())
for (i, e) in b.enumerated() {
if e.hasPrefix("C") {
b[i] = e.uppercased()
}
}

If enumerated was changed to enumeratedSlice(), such an error would be detected at compile time.