[Idea] Replace enumerate() with something more explicit


(Brent Royal-Gordon) #1

A discussion in the "mapValues" thread reminded me of a longstanding issue I have with Swift.

`enumerate()` is an attractive nuisance. (That is, it looks like the thing you want, but it's not actually the right one.) Most people use it because they want to enumerate over indices and elements at the same time. In reality, though, the first element of an `enumerate()` tuple is not the index—it's a monotonically increasing integer starting at 0. That *happens* to work for `Array`:

    1> let array = Array(0..<10)
    2. for (i, elem) in array.enumerate() {
    3. print(array[i])
    4. }
  0
  1
  2
  3
  4
  5
  6
  7
  8
  9

But if you stray even a little bit from the golden path, things start to go wrong:

    5> let range = array[2..<8]
    6. for (i, elem) in range.enumerate() {
    7. print(range[i])
    8. }
  fatal error: Index out of bounds

You can scarcely blame users for making this mistake, though—"The Swift Programming Language" encourages the misconception. "Iterating Over an Array" in "Collection Types":

If you need the integer index of each item as well as its value, use the `enumerate()` method to iterate over the array instead. For each item in the array, the `enumerate()` method returns a tuple composed of the index and the value for that item.

While the text is technically accurate—it only talks about iterating over arrays and the numbers generated by `enumerate()` happen to correspond to array indices—it creates a false implication that `enumerate()` is defined to return indices, which isn't true of other collections.

This is made worse by the fact that `enumerate()` is not really a good name. It is not a common word, so people don't read it and immediately understand what it does; they memorize a Swift-specific meaning, and that meaning may incorporate the misconception that `enumerate()` includes indices. It is also not technically accurate: although it has "number" in its Latin roots, "enumerate" means either "to count" or "to list", not "to number" (i.e. assign numbers to). I know `enumerate()` is used in a couple of other languages (certainly Python, possibly others), but I don't think that overrides the fact that it's confusing.

I have three possible solutions to propose.

OPTION ONE

* Remove `enumerate()`.

* Provide a type and postfix operator which allows you to write an infinite sequence as `0...`. (This might call a ClosedRange constructor which constrains the type to a protocol which provides `max`. This would imply that `FloatingPoint` and `Integer` protocols would need to conform to a common protocol declaring a `max` property, and in the case of `FloatingPoint`, `max` should probably be positive infinity.)

* Fix-It calls to `x.enumerate()` as `zip(0..., x)`. This is more complicated to look at, but it's *far* more explicit about what the operation actually does. (For bonus points, we could perhaps look at how the EnumerateSequence is used, and if the number is used as an index, go with `zip(x.indices, x)` instead.)

OPTION TWO

* Rename `enumerate()` to something more explicit, like `withIntegers()` or `numbered()`. (It might even make sense to add a `from:` parameter which defaults to 0.)

* Add to Collection an equivalent method with a similar name that provides indices, like `withIndices()` or `indexed()`.

* Fix-It calls to `enumerate()` into either `numbered()` or `indexed()` (or whatever they end up being called) depending on the way they are used.

OPTION THREE

Combine the other two options:

* Provide the infinite numeric sequence type from Option One.

* Provide `numbered()` and `indexed()` (or whatever) methods which are explicitly typed to merely zip over the sequence/collection with an infinite integer sequence/Indices collection.

···

--
Brent Royal-Gordon
Architechies


Add forEachWithIndex to Array
(Andrew Bennett) #2

I'm in support of a method of getting a sequence of (Index,Value) pairs (as
you know from the other thread). I think I'm leaning toward option three if
it's equivalent to `zip(self.indices, self)`, ideally as a concise property
like keys/values on a Dictionary.

I've been using zip in production. I presumed enumerate was intended for
enumerating indices, and that it had just been forgotten when the slice
changes went through. I'm in favour of removing it.

···

On Sat, Apr 16, 2016 at 7:59 AM, Brent Royal-Gordon via swift-evolution < swift-evolution@swift.org> wrote:

A discussion in the "mapValues" thread reminded me of a longstanding issue
I have with Swift.

`enumerate()` is an attractive nuisance. (That is, it looks like the thing
you want, but it's not actually the right one.) Most people use it because
they want to enumerate over indices and elements at the same time. In
reality, though, the first element of an `enumerate()` tuple is not the
index—it's a monotonically increasing integer starting at 0. That *happens*
to work for `Array`:

> 1> let array = Array(0..<10)
> 2. for (i, elem) in array.enumerate() {
> 3. print(array[i])
> 4. }
> 0
> 1
> 2
> 3
> 4
> 5
> 6
> 7
> 8
> 9

But if you stray even a little bit from the golden path, things start to
go wrong:

> 5> let range = array[2..<8]
> 6. for (i, elem) in range.enumerate() {
> 7. print(range[i])
> 8. }
> fatal error: Index out of bounds

You can scarcely blame users for making this mistake, though—"The Swift
Programming Language" encourages the misconception. "Iterating Over an
Array" in "Collection Types":

> If you need the integer index of each item as well as its value, use the
`enumerate()` method to iterate over the array instead. For each item in
the array, the `enumerate()` method returns a tuple composed of the index
and the value for that item.

While the text is technically accurate—it only talks about iterating over
arrays and the numbers generated by `enumerate()` happen to correspond to
array indices—it creates a false implication that `enumerate()` is defined
to return indices, which isn't true of other collections.

This is made worse by the fact that `enumerate()` is not really a good
name. It is not a common word, so people don't read it and immediately
understand what it does; they memorize a Swift-specific meaning, and that
meaning may incorporate the misconception that `enumerate()` includes
indices. It is also not technically accurate: although it has "number" in
its Latin roots, "enumerate" means either "to count" or "to list", not "to
number" (i.e. assign numbers to). I know `enumerate()` is used in a couple
of other languages (certainly Python, possibly others), but I don't think
that overrides the fact that it's confusing.

I have three possible solutions to propose.

OPTION ONE

* Remove `enumerate()`.

* Provide a type and postfix operator which allows you to write an
infinite sequence as `0...`. (This might call a ClosedRange constructor
which constrains the type to a protocol which provides `max`. This would
imply that `FloatingPoint` and `Integer` protocols would need to conform to
a common protocol declaring a `max` property, and in the case of
`FloatingPoint`, `max` should probably be positive infinity.)

* Fix-It calls to `x.enumerate()` as `zip(0..., x)`. This is more
complicated to look at, but it's *far* more explicit about what the
operation actually does. (For bonus points, we could perhaps look at how
the EnumerateSequence is used, and if the number is used as an index, go
with `zip(x.indices, x)` instead.)

OPTION TWO

* Rename `enumerate()` to something more explicit, like `withIntegers()`
or `numbered()`. (It might even make sense to add a `from:` parameter which
defaults to 0.)

* Add to Collection an equivalent method with a similar name that provides
indices, like `withIndices()` or `indexed()`.

* Fix-It calls to `enumerate()` into either `numbered()` or `indexed()`
(or whatever they end up being called) depending on the way they are used.

OPTION THREE

Combine the other two options:

* Provide the infinite numeric sequence type from Option One.

* Provide `numbered()` and `indexed()` (or whatever) methods which are
explicitly typed to merely zip over the sequence/collection with an
infinite integer sequence/Indices collection.

--
Brent Royal-Gordon
Architechies

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


(Patrick Gili) #3

I have to agree with those that simply want enumerate() fixed. There are many modern languages that have a similar function, such as Ruby.


(plx) #4

If something is to happen I cast my vote for #2.

I actually—and intentionally—use what that would call `numbered()` rather frequently; it’s not hard to write but not having to write it is nicer.

Since the “indexed” version is also useful it’d be nice to have both, and with unambiguous names.

···

On Apr 15, 2016, at 4:59 PM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

A discussion in the "mapValues" thread reminded me of a longstanding issue I have with Swift.

`enumerate()` is an attractive nuisance. (That is, it looks like the thing you want, but it's not actually the right one.) Most people use it because they want to enumerate over indices and elements at the same time. In reality, though, the first element of an `enumerate()` tuple is not the index—it's a monotonically increasing integer starting at 0. That *happens* to work for `Array`:

    1> let array = Array(0..<10)
    2. for (i, elem) in array.enumerate() {
    3. print(array[i])
    4. }
  0
  1
  2
  3
  4
  5
  6
  7
  8
  9

But if you stray even a little bit from the golden path, things start to go wrong:

    5> let range = array[2..<8]
    6. for (i, elem) in range.enumerate() {
    7. print(range[i])
    8. }
  fatal error: Index out of bounds

You can scarcely blame users for making this mistake, though—"The Swift Programming Language" encourages the misconception. "Iterating Over an Array" in "Collection Types":

If you need the integer index of each item as well as its value, use the `enumerate()` method to iterate over the array instead. For each item in the array, the `enumerate()` method returns a tuple composed of the index and the value for that item.

While the text is technically accurate—it only talks about iterating over arrays and the numbers generated by `enumerate()` happen to correspond to array indices—it creates a false implication that `enumerate()` is defined to return indices, which isn't true of other collections.

This is made worse by the fact that `enumerate()` is not really a good name. It is not a common word, so people don't read it and immediately understand what it does; they memorize a Swift-specific meaning, and that meaning may incorporate the misconception that `enumerate()` includes indices. It is also not technically accurate: although it has "number" in its Latin roots, "enumerate" means either "to count" or "to list", not "to number" (i.e. assign numbers to). I know `enumerate()` is used in a couple of other languages (certainly Python, possibly others), but I don't think that overrides the fact that it's confusing.

I have three possible solutions to propose.

OPTION ONE

* Remove `enumerate()`.

* Provide a type and postfix operator which allows you to write an infinite sequence as `0...`. (This might call a ClosedRange constructor which constrains the type to a protocol which provides `max`. This would imply that `FloatingPoint` and `Integer` protocols would need to conform to a common protocol declaring a `max` property, and in the case of `FloatingPoint`, `max` should probably be positive infinity.)

* Fix-It calls to `x.enumerate()` as `zip(0..., x)`. This is more complicated to look at, but it's *far* more explicit about what the operation actually does. (For bonus points, we could perhaps look at how the EnumerateSequence is used, and if the number is used as an index, go with `zip(x.indices, x)` instead.)

OPTION TWO

* Rename `enumerate()` to something more explicit, like `withIntegers()` or `numbered()`. (It might even make sense to add a `from:` parameter which defaults to 0.)

* Add to Collection an equivalent method with a similar name that provides indices, like `withIndices()` or `indexed()`.

* Fix-It calls to `enumerate()` into either `numbered()` or `indexed()` (or whatever they end up being called) depending on the way they are used.

OPTION THREE

Combine the other two options:

* Provide the infinite numeric sequence type from Option One.

* Provide `numbered()` and `indexed()` (or whatever) methods which are explicitly typed to merely zip over the sequence/collection with an infinite integer sequence/Indices collection.

--
Brent Royal-Gordon
Architechies

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


#5

I see this purely as a documentation issue. It should clearly state the number is not to be used to subscript into the array. Even 'indices' likely warrant a warning that it is pointless to use in some construct such as:

for i in array.filter({ $0 % 2 == 0 }).indices { print("\(i) is not to index array") }

Which of course can be safely be written as

for i in array.indices where array[i] % 2 == 0 { print("\(i) can be used as subscript") }

If we want to have a way to safely subscript into the original array for all possible use cases, we may need a way to "dictionarize" the array.

Providing a new 'enumerated()' which returns the proper array subscript for the simple case where the array is not post-processed (with filter for example) could lead to a false sense of safety when using such construct as subscript into the array.

Dany

···

Le 15 avr. 2016 à 17:59, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> a écrit :

A discussion in the "mapValues" thread reminded me of a longstanding issue I have with Swift.

`enumerate()` is an attractive nuisance. (That is, it looks like the thing you want, but it's not actually the right one.) Most people use it because they want to enumerate over indices and elements at the same time. In reality, though, the first element of an `enumerate()` tuple is not the index—it's a monotonically increasing integer starting at 0. That *happens* to work for `Array`:

   1> let array = Array(0..<10)
   2. for (i, elem) in array.enumerate() {
   3. print(array[i])
   4. }
0
1
2
3
4
5
6
7
8
9

But if you stray even a little bit from the golden path, things start to go wrong:

   5> let range = array[2..<8]
   6. for (i, elem) in range.enumerate() {
   7. print(range[i])
   8. }
fatal error: Index out of bounds

You can scarcely blame users for making this mistake, though—"The Swift Programming Language" encourages the misconception. "Iterating Over an Array" in "Collection Types":

If you need the integer index of each item as well as its value, use the `enumerate()` method to iterate over the array instead. For each item in the array, the `enumerate()` method returns a tuple composed of the index and the value for that item.

While the text is technically accurate—it only talks about iterating over arrays and the numbers generated by `enumerate()` happen to correspond to array indices—it creates a false implication that `enumerate()` is defined to return indices, which isn't true of other collections.

This is made worse by the fact that `enumerate()` is not really a good name. It is not a common word, so people don't read it and immediately understand what it does; they memorize a Swift-specific meaning, and that meaning may incorporate the misconception that `enumerate()` includes indices. It is also not technically accurate: although it has "number" in its Latin roots, "enumerate" means either "to count" or "to list", not "to number" (i.e. assign numbers to). I know `enumerate()` is used in a couple of other languages (certainly Python, possibly others), but I don't think that overrides the fact that it's confusing.


(Dave Abrahams) #6

A discussion in the "mapValues" thread reminded me of a longstanding issue I have with Swift.

`enumerate()` is an attractive nuisance. (That is, it looks like the
thing you want, but it's not actually the right one.)

It depends what you want. It exists in part because it's nontrivial to
write a method that does what enumerate does, and does it efficiently.

Most people use it because they want to enumerate over indices and
elements at the same time.

If what you want is zip(a.indices, a), it's easy enough to write that.

In reality, though, the first element of an
`enumerate()` tuple is not the index—it's a monotonically increasing
integer starting at 0. That *happens* to work for `Array`:

    1> let array = Array(0..<10)
    2. for (i, elem) in array.enumerate() {
    3. print(array[i])
    4. }
  0
  1
  2
  3
  4
  5
  6
  7
  8
  9

But if you stray even a little bit from the golden path, things start to go wrong:

    5> let range = array[2..<8]
    6. for (i, elem) in range.enumerate() {
    7. print(range[i])
    8. }
  fatal error: Index out of bounds

I think the potential for this confusion is inherent in the combination
of two factors:

* Arrays are indexed with integers

* The indices of a SubSequence are the indices of corresponding elements
  in the thing it was sliced from.

I also think it has nothing whatsoever to do with enumerate.

You can scarcely blame users for making this mistake, though—"The
Swift Programming Language" encourages the misconception. "Iterating
Over an Array" in "Collection Types":

If you need the integer index of each item as well as its value, use
the `enumerate()` method to iterate over the array instead. For each
item in the array, the `enumerate()` method returns a tuple composed
of the index and the value for that item.

While the text is technically accurate—it only talks about iterating
over arrays and the numbers generated by `enumerate()` happen to
correspond to array indices—it creates a false implication that
`enumerate()` is defined to return indices, which isn't true of other
collections.

You should consider filing a bug report against TSPL, I think.

This is made worse by the fact that `enumerate()` is not really a good
name.

Totally open to better names. It's precedented in Python with exactly
these semantics, which is why we used it.

It is not a common word, so people don't read it and immediately
understand what it does; they memorize a Swift-specific meaning, and
that meaning may incorporate the misconception that `enumerate()`
includes indices. It is also not technically accurate: although it has
"number" in its Latin roots, "enumerate" means either "to count" or
"to list", not "to number" (i.e. assign numbers to). I know
`enumerate()` is used in a couple of other languages (certainly
Python, possibly others), but I don't think that overrides the fact
that it's confusing.

Maybe "numbered()" would be a better name.

I have three possible solutions to propose.

OPTION ONE

* Remove `enumerate()`.

I'm open to doing this if nobody feels the current semantics are useful
and we can get some real-world data showing that they're not.

* Provide a type and postfix operator which allows you to write an
infinite sequence as `0...`. (This might call a ClosedRange
constructor which constrains the type to a protocol which provides
`max`. This would imply that `FloatingPoint` and `Integer` protocols
would need to conform to a common protocol declaring a `max` property,
and in the case of `FloatingPoint`, `max` should probably be positive
infinity.)

+1; I want this feature “anyway.” I also want to use it for slicing,
e.g. in lieu of x.dropFirst(3), x[3...]

* Fix-It calls to `x.enumerate()` as `zip(0..., x)`. This is more
complicated to look at, but it's *far* more explicit about what the
operation actually does. (For bonus points, we could perhaps look at
how the EnumerateSequence is used, and if the number is used as an
index, go with `zip(x.indices, x)` instead.)

OPTION TWO

* Rename `enumerate()` to something more explicit, like
`withIntegers()` or `numbered()`. (It might even make sense to add a
`from:` parameter which defaults to 0.)

Hey (if we keep it) good idea!

* Add to Collection an equivalent method with a similar name that
provides indices, like `withIndices()` or `indexed()`.

Less enthused about this one, since you can get it trivially by
combining primitives.

···

on Fri Apr 15 2016, Brent Royal-Gordon <swift-evolution@swift.org> wrote:

* Fix-It calls to `enumerate()` into either `numbered()` or
`indexed()` (or whatever they end up being called) depending on the
way they are used.

OPTION THREE

Combine the other two options:

* Provide the infinite numeric sequence type from Option One.

* Provide `numbered()` and `indexed()` (or whatever) methods which are
explicitly typed to merely zip over the sequence/collection with an
infinite integer sequence/Indices collection.

--
Dave


(Charles Srstka) #7

Instead of removing it, how about just fixing it so that it returns the proper indexes rather than arbitrary numbers?

Charles

···

On Apr 15, 2016, at 8:09 PM, Andrew Bennett via swift-evolution <swift-evolution@swift.org> wrote:

I'm in support of a method of getting a sequence of (Index,Value) pairs (as you know from the other thread). I think I'm leaning toward option three if it's equivalent to `zip(self.indices, self)`, ideally as a concise property like keys/values on a Dictionary.

I've been using zip in production. I presumed enumerate was intended for enumerating indices, and that it had just been forgotten when the slice changes went through. I'm in favour of removing it.


(Andrew Bennett) #8

I support a fixed version. I don't think that's mutually exclusive with
removing enumerate.

This thread seems to be a result of a misused API, so I think it's
necessary to make it obvious when that API changes. I think we'd want a
period of deprecation with both enumerate() and a new (hopefully more clear
and concise) method, so that people can fix their code.

I think this would be a good change:

+ @available(*, deprecated, message="it will be removed in Swift 3")
  public func enumerate() -> EnumerateSequence<Self>
+ public func indexed() -> Zip2Sequence<Self.Index,Self> { return
zip(self.indices, self) }

···

On Sat, Apr 16, 2016 at 11:12 AM, Charles Srstka <cocoadev@charlessoft.com> wrote:

On Apr 15, 2016, at 8:09 PM, Andrew Bennett via swift-evolution < > swift-evolution@swift.org> wrote:

I'm in support of a method of getting a sequence of (Index,Value) pairs
(as you know from the other thread). I think I'm leaning toward option
three if it's equivalent to `zip(self.indices, self)`, ideally as a concise
property like keys/values on a Dictionary.

I've been using zip in production. I presumed enumerate was intended for
enumerating indices, and that it had just been forgotten when the slice
changes went through. I'm in favour of removing it.

Instead of removing it, how about just fixing it so that it returns the
proper indexes rather than arbitrary numbers?

Charles


(Howard Lovatt) #9

I would suggest an alternative; changing Range so that it is indexed like
an array, an Int from 0 to count - 1. Such that aRange[index] is defined as
start + index * stride. This will eliminate the problems with enumerate. A
Range's generic type would be constrained to be an Arithmetic type, see
extended floating point proposal.

···

On Saturday, 16 April 2016, Andrew Bennett via swift-evolution < swift-evolution@swift.org> wrote:

I support a fixed version. I don't think that's mutually exclusive with
removing enumerate.

This thread seems to be a result of a misused API, so I think it's
necessary to make it obvious when that API changes. I think we'd want a
period of deprecation with both enumerate() and a new (hopefully more clear
and concise) method, so that people can fix their code.

I think this would be a good change:

+ @available(*, deprecated, message="it will be removed in Swift 3")
  public func enumerate() -> EnumerateSequence<Self>
+ public func indexed() -> Zip2Sequence<Self.Index,Self> { return zip(self.indices, self) }

On Sat, Apr 16, 2016 at 11:12 AM, Charles Srstka <cocoadev@charlessoft.com > <javascript:_e(%7B%7D,'cvml','cocoadev@charlessoft.com');>> wrote:

On Apr 15, 2016, at 8:09 PM, Andrew Bennett via swift-evolution < >> swift-evolution@swift.org >> <javascript:_e(%7B%7D,'cvml','swift-evolution@swift.org');>> wrote:

I'm in support of a method of getting a sequence of (Index,Value) pairs
(as you know from the other thread). I think I'm leaning toward option
three if it's equivalent to `zip(self.indices, self)`, ideally as a concise
property like keys/values on a Dictionary.

I've been using zip in production. I presumed enumerate was intended for
enumerating indices, and that it had just been forgotten when the slice
changes went through. I'm in favour of removing it.

Instead of removing it, how about just fixing it so that it returns the
proper indexes rather than arbitrary numbers?

Charles

--
-- Howard.


(Brent Royal-Gordon) #10

I would suggest an alternative; changing Range so that it is indexed like an array, an Int from 0 to count - 1. Such that aRange[index] is defined as start + index * stride. This will eliminate the problems with enumerate. A Range's generic type would be constrained to be an Arithmetic type, see extended floating point proposal.

That papers over the problem for Slice/Range specifically, but it doesn't fix it for types with non-integer indices, like Dictionary and Set. enumerate() is not meant to be used for the purpose to which it is usually put.


(Howard Lovatt) #11

I would suggest enumerate only for types that are subscriptable and that it
be defined as their (index, value) set that iterates in the collection's
indices order. IE:

    for index in collection.indices {
        let value = collection[index]
        ...
    }

and

    for (index, value) in collection.enumerate {
        ...
    }

are equivalent.

With the above definition I would suggest a name change to entries, since a
Dictionary's keys are not necessarily numbers, hence enumerate is
misleading.

Nothing for Set since it isn't subscriptable.

···

On Saturday, 16 April 2016, Brent Royal-Gordon <brent@architechies.com> wrote:

> I would suggest an alternative; changing Range so that it is indexed
like an array, an Int from 0 to count - 1. Such that aRange[index] is
defined as start + index * stride. This will eliminate the problems with
enumerate. A Range's generic type would be constrained to be an Arithmetic
type, see extended floating point proposal.

That papers over the problem for Slice/Range specifically, but it doesn't
fix it for types with non-integer indices, like Dictionary and Set.
enumerate() is not meant to be used for the purpose to which it is usually
put.

--
-- Howard.


(Dave Abrahams) #12

It also breaks an important “law of slicing,” that there is an index
correspondence between the slice and the outer collection. That makes
it possible, e.g, to do

   let firstOne = c.index(of: something)
   let secondOne = c[firstOne..<c.endIndex].index(of: somethingElse)
   doSomethingWith(c[firstOne..<secondOne])

···

on Fri Apr 15 2016, Brent Royal-Gordon <swift-evolution@swift.org> wrote:

I would suggest an alternative; changing Range so that it is indexed

like an array, an Int from 0 to count - 1. Such that aRange[index] is
defined as start + index * stride. This will eliminate the problems
with enumerate. A Range's generic type would be constrained to be an
Arithmetic type, see extended floating point proposal.

That papers over the problem for Slice/Range specifically, but it
doesn't fix it for types with non-integer indices, like Dictionary and
Set. enumerate() is not meant to be used for the purpose to which it
is usually put.

--
Dave


(Brent Royal-Gordon) #13

With the above definition I would suggest a name change to entries, since a Dictionary's keys are not necessarily numbers, hence enumerate is misleading.

Nothing for Set since it isn't subscriptable.

I think you're slightly confused. All Collections have an Index. Dictionary's Index is not its Key; it is an opaque type which references an entry in its internal table. Set also has an Index; again, it is an opaque type which references an entry in its internal table. Your `enumerate()` (or my `indexed()`) would return these opaque `Index`es on all of these types.

If you want Array to return `(Int, Element)`, Dictionary to return `(Key, Value)`, and Set to not have the operation at all, you're describing something ad-hoc and entirely disconnected from the Collection type.

···

--
Brent Royal-Gordon
Architechies


(Howard Lovatt) #14

Yes I am proposing something like protocol Subscriptable that defines the
subscript operation and indices and entries properties.

···

On Sunday, 17 April 2016, Brent Royal-Gordon <brent@architechies.com> wrote:

> With the above definition I would suggest a name change to entries,
since a Dictionary's keys are not necessarily numbers, hence enumerate is
misleading.
>
> Nothing for Set since it isn't subscriptable.

I think you're slightly confused. All Collections have an Index.
Dictionary's Index is not its Key; it is an opaque type which references an
entry in its internal table. Set also has an Index; again, it is an opaque
type which references an entry in its internal table. Your `enumerate()`
(or my `indexed()`) would return these opaque `Index`es on all of these
types.

If you want Array to return `(Int, Element)`, Dictionary to return `(Key,
Value)`, and Set to not have the operation at all, you're describing
something ad-hoc and entirely disconnected from the Collection type.

--
Brent Royal-Gordon
Architechies

--
-- Howard.


(Dave Abrahams) #15

Yes I am proposing something like protocol Subscriptable that defines the
subscript operation and indices and entries properties.

That sounds like a protocol that is just a bag of syntax without
semantic requirements, which—forgive my pretentious tone—goes against
the principles of generic programming on which the standard library was
founded. We won't have a “plusable” for things you can apply the “+”
operator to, for similar reasons.

···

on Sun Apr 17 2016, Howard Lovatt <swift-evolution@swift.org> wrote:

On Sunday, 17 April 2016, Brent Royal-Gordon > <brent@architechies.com> wrote:

    > With the above definition I would suggest a name change to entries, since
    a Dictionary's keys are not necessarily numbers, hence enumerate is
    misleading.
    >
    > Nothing for Set since it isn't subscriptable.

    I think you're slightly confused. All Collections have an Index.
    Dictionary's Index is not its Key; it is an opaque type which references an
    entry in its internal table. Set also has an Index; again, it is an opaque
    type which references an entry in its internal table. Your `enumerate()` (or
    my `indexed()`) would return these opaque `Index`es on all of these types.

    If you want Array to return `(Int, Element)`, Dictionary to return `(Key,
    Value)`, and Set to not have the operation at all, you're describing
    something ad-hoc and entirely disconnected from the Collection type.

    --
    Brent Royal-Gordon
    Architechies

--
Dave