[Pitch] Eliminate tuples - unify member access syntax


(Freak Show) #1

FWIW, I searched the previous proposals for any kind of mention of tuple and found nothing so forgive me if this has been discussed before.

Swift currently has 5 ways to represent multi-values.

Classes, structs, arrays, dictionaries, and tuples.

Of these, classes are well established and unique in that they support inheritance and often have identity.

The others, however, are primarily used as value types in their immutable incarnations. Consider a desire to pass around a cartesian coordinate. The following representations are available:

let x = "x"
let y = "y"

struct XY { var x: Int; var y: Int }

var txy = (x: 1, y: 2) // (.0 1,.1 2)
var sxy = XY(x: 1, y: 2 ) // XY
var dxy = [ x: 1, y: 2] // ["y": 2, "x": 1]
var axy = [ 1, 2 ] // [1,2]
var taxy = ( 1, 2 ) // (.0 1,.1 2)

Their print strings are in the comment to the right - that could be a source of a whole other proposal.

A developer might choose from any one of these to represent a Point. They all represent the same information. Yet the syntax required to make use of them is annoyingly different.

Consider the tuple with named fields which is being used as a sort of anonymous ad hoc struct:

txy.x // 1
txy.0 // 1
txy[x] // error
txy[0] // error

vs the declared struct

sxy.x
sxy.0 //error
sxy[x] //error
sxy[0] //error

vs the dictionary

dxy.x // error
dxy.0 // error
dxy[x] // 1
dxy[0] // error

and then we have the tuple with unnamed fields

taxy.0 // 1
taxy[0] // error

vs the array
axy.0 // error
axy[0] // 1

It is worth observing that, to the programmer the variable sxy is indistinguishable from txy. They represent the same informtion, they are accessed in the exactly the same way. They are completely equivalent. However this fails:

sxy = txy // error

this succeeds (which may or may not be good depending on context and coincidence):

var txy = (x: 1, y: 2)
var txy2 = ( x: 2, y: 3)
txy = txy2 // OK

but this fails (which I think is a good thing)

struct XY { var x: Int; var y: Int }
struct XY2 { var x: Int; var y: Int }

var sxy2 = XY2(x: 2, y: 3)
var sxy = XY(x: 1, y: 2 )

sxy=sxy2 // error

The point of this comparison is to point out that the anonymous type generating tuple is a) superfluous and b) a source of confusion and gratuitous complexity. It assumes the role at times of immutable arrays, anonymous structs, and immutable dictionaries depending on context and we already have all of those things.

Proposal:

1) Eliminate the tuple as a first level concept.
2) Replace tuples that have named fields with structs or immutable dictionaries.
3) Replace tuples without named fields with immutable arrays.
4) Disallow tuple expressions that mix named and unnamed fields - IOW - this is an abomination:

var mixed = (z: 1, 2)

If nothing else in this proposal resonates at all - that should be adopted.

5) Unify field access syntax. Pick one or allow both on everything (similar to javascript).

FWIW, this isn't hard to do already in Objective C. It is possible to make use of default handlers to allow dict.field access on NSDictionary with a very small amount of code. Javascript also supports both dictionary oriented access (obj['field']) and dot notation (obj.field) so there is precedent for this kind of flexibility.

6) Unify iteration over all the fields of all the kinds of things that have fields. In this case, tuples are kind of like sequenced collections that have been lobotomized.

Conclusion

There is a lot of overlap among the concepts of structs, immutable dictionaries, immutable arrays and tuples. They are a source of gratuitous complexity in the language and a likely ongoing source of frustration as developers choose different, equivalent, but incompatible representations for common types of data. My primary fear is that anonymous tuples will tend to phase out named struct types and an important source of documentation of intent will be lost.

It should also be noted that I am not a compiler or VM writer and I don't give a fig how the language does things at the implementation level and this is primarily a conceptual/syntactic proposal. The goal is to make consuming data types predictable and simple and eliminate gratuitous complexity at the conceptual level through generalization of special cases.

Thanks for reading. Still reading the process document to figure out how to make this "official".


(David Sweeris) #2

It should also be noted that I am not a compiler or VM writer and I don't give a fig how the language does things at the implementation level and this is primarily a conceptual/syntactic proposal.

Conceptual/syntactic proposals do need to recognize implementation realities, though, regardless of how much fruit may or may not be involved.

The goal is to make consuming data types predictable and simple and eliminate gratuitous complexity at the conceptual level through generalization of special cases.

Tuples can't use the "xyz[0]" syntax because the "[]" part is a function call which can only return a single type, but tuples can contain as many types as there are elements.

Array (and dictionaries) can't use the "xyz.0" syntax because the ".0" is essentially a property on the type, but those all need to be defined at compile-time which would make it impossible to grow or shrink them at run-time. (This restriction might go away if the proposal for variadic tuples goes through, but it hasn't happened yet.)

Anyway, these aren't "special cases" of the same general thing — they're fundamentally different operations.

As for tuples vs structs, there is some overlap, but not enough IMHO to justify getting rid of either. And structs can have unhelpful property names just as easily as tuples.

- Dave Sweeris.

···

On Jan 7, 2017, at 18:17, Freak Show via swift-evolution <swift-evolution@swift.org> wrote:


(Robert Widmann) #3

Some simple questions:

Say Tuples are deprecated and removed from the language:

How would you go about pattern matching on multiple values in the same switch statement?

What about pattern bindings for multiple values?

When a function that takes more than one argument in a particular parameter list is partially applied, how will I apply the needed tuple of arguments to complete the call later?

There is a lot of overlap if you view them simply as data structures, but there is also a lot the language itself does with them that has to be worked out if they're going to be replaced.

~Robert Widmann

2017/01/07 19:17、Freak Show via swift-evolution <swift-evolution@swift.org> のメッセージ:

···

FWIW, I searched the previous proposals for any kind of mention of tuple and found nothing so forgive me if this has been discussed before.

Swift currently has 5 ways to represent multi-values.

Classes, structs, arrays, dictionaries, and tuples.

Of these, classes are well established and unique in that they support inheritance and often have identity.

The others, however, are primarily used as value types in their immutable incarnations. Consider a desire to pass around a cartesian coordinate. The following representations are available:

let x = "x"
let y = "y"

struct XY { var x: Int; var y: Int }

var txy = (x: 1, y: 2) // (.0 1,.1 2)
var sxy = XY(x: 1, y: 2 ) // XY
var dxy = [ x: 1, y: 2] // ["y": 2, "x": 1]
var axy = [ 1, 2 ] // [1,2]
var taxy = ( 1, 2 ) // (.0 1,.1 2)

Their print strings are in the comment to the right - that could be a source of a whole other proposal.

A developer might choose from any one of these to represent a Point. They all represent the same information. Yet the syntax required to make use of them is annoyingly different.

Consider the tuple with named fields which is being used as a sort of anonymous ad hoc struct:

txy.x // 1
txy.0 // 1
txy[x] // error
txy[0] // error

vs the declared struct

sxy.x
sxy.0 //error
sxy[x] //error
sxy[0] //error

vs the dictionary

dxy.x // error
dxy.0 // error
dxy[x] // 1
dxy[0] // error

and then we have the tuple with unnamed fields

taxy.0 // 1
taxy[0] // error

vs the array
axy.0 // error
axy[0] // 1

It is worth observing that, to the programmer the variable sxy is indistinguishable from txy. They represent the same informtion, they are accessed in the exactly the same way. They are completely equivalent. However this fails:

sxy = txy // error

this succeeds (which may or may not be good depending on context and coincidence):

var txy = (x: 1, y: 2)
var txy2 = ( x: 2, y: 3)
txy = txy2 // OK

but this fails (which I think is a good thing)

struct XY { var x: Int; var y: Int }
struct XY2 { var x: Int; var y: Int }

var sxy2 = XY2(x: 2, y: 3)
var sxy = XY(x: 1, y: 2 )

sxy=sxy2 // error

The point of this comparison is to point out that the anonymous type generating tuple is a) superfluous and b) a source of confusion and gratuitous complexity. It assumes the role at times of immutable arrays, anonymous structs, and immutable dictionaries depending on context and we already have all of those things.

Proposal:

1) Eliminate the tuple as a first level concept.
2) Replace tuples that have named fields with structs or immutable dictionaries.
3) Replace tuples without named fields with immutable arrays.
4) Disallow tuple expressions that mix named and unnamed fields - IOW - this is an abomination:

var mixed = (z: 1, 2)

If nothing else in this proposal resonates at all - that should be adopted.

5) Unify field access syntax. Pick one or allow both on everything (similar to javascript).

FWIW, this isn't hard to do already in Objective C. It is possible to make use of default handlers to allow dict.field access on NSDictionary with a very small amount of code. Javascript also supports both dictionary oriented access (obj['field']) and dot notation (obj.field) so there is precedent for this kind of flexibility.

6) Unify iteration over all the fields of all the kinds of things that have fields. In this case, tuples are kind of like sequenced collections that have been lobotomized.

Conclusion

There is a lot of overlap among the concepts of structs, immutable dictionaries, immutable arrays and tuples. They are a source of gratuitous complexity in the language and a likely ongoing source of frustration as developers choose different, equivalent, but incompatible representations for common types of data. My primary fear is that anonymous tuples will tend to phase out named struct types and an important source of documentation of intent will be lost.

It should also be noted that I am not a compiler or VM writer and I don't give a fig how the language does things at the implementation level and this is primarily a conceptual/syntactic proposal. The goal is to make consuming data types predictable and simple and eliminate gratuitous complexity at the conceptual level through generalization of special cases.

Thanks for reading. Still reading the process document to figure out how to make this "official".

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


(Tino) #4

The point of this comparison is to point out that the anonymous type generating tuple is a) superfluous and b) a source of confusion and gratuitous complexity. It assumes the role at times of immutable arrays, anonymous structs, and immutable dictionaries depending on context and we already have all of those things.

There is another anonymous type — and it's very popular right now:
Closures*.
They are also a potential source of confusion (and even more complexity ;-), so should they be removed as well?

I guess we'll keep both, but you are definitely right that there is much complexity… but I'd rather improve that not by removing features, but by adding more regularity:
There has been a discussion about anonymous enums as well, and if we could come up with a syntax for anonymous reference types, imho everything would feel simpler.

Most likely someone already mentioned this, but tuples can be used in assignments in a way that isn't possible with any of the alternatives:

func tupleTest() -> (String, Int) {
  return ("Foo", 10)
}

let (name, count) = tupleTest()

print("There are \(count) \(name)")

- Tino

* so there is yet another (odd) variant:

var cxy = { (c: Character) -> Int in
  switch (c) {
  case "x": return 1
  case "y": return 2
  default: fatalError()
  }
}


(Rod Brown) #5

Apart from your seeming distain for Swift, this proposal seems misguided to me, and to ignore some of the recent discussion around named parameters on tuples (which are currently in flux).

Each of the types you mention each have clear, specific meanings.

The following two are basic type system representations:
1. Classes - Reference, inheritance based items.
2. Structs - Formal Value based items with storage (as opposed to enums which generally aren’t designed for storage).

The following two are collection types you’ve arbitrarily attacked for no reason. There are plenty of examples where each of these makes sense and is relevant within the language:
3. Arrays - Lists of items.
4. Dictionarys - Key value pair collections.

The final one is a separate basic type system representation:
5. Tuple. “Multiple values grouped together for a single compound value” (from Swift Programming Language Guide 3.0.1).

This type is to group related items together informally. That is, not as a wider type that is commonly used, but as an ad-hoc solution in cases where defining a type is neither required nor desirable. Say if I want to pass a return value of an item, and an associated description string for one method only, should I go and create a struct and all the boilerplate for that? This is an ad-hoc solution. That is in opposition to your example, which most definitely makes sense as a struct, and there already is one: CGPoint. Tuples themselves are actually a major part of how the language is built under the covers, and removing them for no reason is part of taking the guts out of Swift, for no reason.

I definitely agree there needs to be discussions regarding improving the situation for named parameters in tuples.

Accessing items: Each accessor is based on the one appropriate for the type. Unifying all member syntax purely for the sake of unifying it is a non-goal. We need to work out what is best for each case independently: properties vs subscripts. Rather than finding one obscure case where you used all of them and put them side by side, each should be assessed individually on their own merits.

···

On 8 Jan 2017, at 1:17 pm, Freak Show via swift-evolution <swift-evolution@swift.org> wrote:

FWIW, I searched the previous proposals for any kind of mention of tuple and found nothing so forgive me if this has been discussed before.

Swift currently has 5 ways to represent multi-values.

Classes, structs, arrays, dictionaries, and tuples.

Of these, classes are well established and unique in that they support inheritance and often have identity.

The others, however, are primarily used as value types in their immutable incarnations. Consider a desire to pass around a cartesian coordinate. The following representations are available:

let x = "x"
let y = "y"

struct XY { var x: Int; var y: Int }

var txy = (x: 1, y: 2) // (.0 1,.1 2)
var sxy = XY(x: 1, y: 2 ) // XY
var dxy = [ x: 1, y: 2] // ["y": 2, "x": 1]
var axy = [ 1, 2 ] // [1,2]
var taxy = ( 1, 2 ) // (.0 1,.1 2)

Their print strings are in the comment to the right - that could be a source of a whole other proposal.

A developer might choose from any one of these to represent a Point. They all represent the same information. Yet the syntax required to make use of them is annoyingly different.

Consider the tuple with named fields which is being used as a sort of anonymous ad hoc struct:

txy.x // 1
txy.0 // 1
txy[x] // error
txy[0] // error

vs the declared struct

sxy.x
sxy.0 //error
sxy[x] //error
sxy[0] //error

vs the dictionary

dxy.x // error
dxy.0 // error
dxy[x] // 1
dxy[0] // error

and then we have the tuple with unnamed fields

taxy.0 // 1
taxy[0] // error

vs the array
axy.0 // error
axy[0] // 1

It is worth observing that, to the programmer the variable sxy is indistinguishable from txy. They represent the same informtion, they are accessed in the exactly the same way. They are completely equivalent. However this fails:

sxy = txy // error

this succeeds (which may or may not be good depending on context and coincidence):

var txy = (x: 1, y: 2)
var txy2 = ( x: 2, y: 3)
txy = txy2 // OK

but this fails (which I think is a good thing)

struct XY { var x: Int; var y: Int }
struct XY2 { var x: Int; var y: Int }

var sxy2 = XY2(x: 2, y: 3)
var sxy = XY(x: 1, y: 2 )

sxy=sxy2 // error

The point of this comparison is to point out that the anonymous type generating tuple is a) superfluous and b) a source of confusion and gratuitous complexity. It assumes the role at times of immutable arrays, anonymous structs, and immutable dictionaries depending on context and we already have all of those things.

Proposal:

1) Eliminate the tuple as a first level concept.
2) Replace tuples that have named fields with structs or immutable dictionaries.
3) Replace tuples without named fields with immutable arrays.
4) Disallow tuple expressions that mix named and unnamed fields - IOW - this is an abomination:

var mixed = (z: 1, 2)

If nothing else in this proposal resonates at all - that should be adopted.

5) Unify field access syntax. Pick one or allow both on everything (similar to javascript).

FWIW, this isn't hard to do already in Objective C. It is possible to make use of default handlers to allow dict.field access on NSDictionary with a very small amount of code. Javascript also supports both dictionary oriented access (obj['field']) and dot notation (obj.field) so there is precedent for this kind of flexibility.

6) Unify iteration over all the fields of all the kinds of things that have fields. In this case, tuples are kind of like sequenced collections that have been lobotomized.

Conclusion

There is a lot of overlap among the concepts of structs, immutable dictionaries, immutable arrays and tuples. They are a source of gratuitous complexity in the language and a likely ongoing source of frustration as developers choose different, equivalent, but incompatible representations for common types of data. My primary fear is that anonymous tuples will tend to phase out named struct types and an important source of documentation of intent will be lost.

It should also be noted that I am not a compiler or VM writer and I don't give a fig how the language does things at the implementation level and this is primarily a conceptual/syntactic proposal. The goal is to make consuming data types predictable and simple and eliminate gratuitous complexity at the conceptual level through generalization of special cases.

Thanks for reading. Still reading the process document to figure out how to make this "official".

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


(David Sweeris) #6

A really convenient way to pass around multiple values without having to bother with a formal struct.

- Dave Sweeris

···

On Jan 7, 2017, at 19:34, Freak Show <freakshow42@mac.com> wrote:

I think you're missing the forrest for the trees here.'

Let me ask this: if you remove tuples from the language - what have you lost - really? You can still say everything you could before.


(Tyler Cloutier) #7

I’ll tell you it, though, it would be really nice to be able to add functions to tuples, just as they are done with structs.

Perhaps there is a way to formalize tuples as unnamed or anonymous Structs? It would make them going away strictly by making them more powerful.

···

On Jan 8, 2017, at 12:35 PM, Tino Heth via swift-evolution <swift-evolution@swift.org> wrote:

The point of this comparison is to point out that the anonymous type generating tuple is a) superfluous and b) a source of confusion and gratuitous complexity. It assumes the role at times of immutable arrays, anonymous structs, and immutable dictionaries depending on context and we already have all of those things.

There is another anonymous type — and it's very popular right now:
Closures*.
They are also a potential source of confusion (and even more complexity ;-), so should they be removed as well?

I guess we'll keep both, but you are definitely right that there is much complexity… but I'd rather improve that not by removing features, but by adding more regularity:
There has been a discussion about anonymous enums as well, and if we could come up with a syntax for anonymous reference types, imho everything would feel simpler.

Most likely someone already mentioned this, but tuples can be used in assignments in a way that isn't possible with any of the alternatives:

func tupleTest() -> (String, Int) {
  return ("Foo", 10)
}

let (name, count) = tupleTest()

print("There are \(count) \(name)")

- Tino

* so there is yet another (odd) variant:

var cxy = { (c: Character) -> Int in
  switch (c) {
  case "x": return 1
  case "y": return 2
  default: fatalError()
  }
}

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


(Derrick Ho) #8

Don't remove tuples.
They make it very convenient to pass multiple values around.

Tuples use .0 instead of [0] which prevents any index out of bounds issues
at compile time rather than at run time.

Tuples should not adopt a dictionary style access because dictionaries
imply nil for any key that doesn't exist in the dictionary. Tuples syntax
prevents you from using non-existent keys.

···

On Sun, Jan 8, 2017 at 1:51 AM David Sweeris via swift-evolution < swift-evolution@swift.org> wrote:

> On Jan 7, 2017, at 19:34, Freak Show <freakshow42@mac.com> wrote:
>
> I think you're missing the forrest for the trees here.'
>
> Let me ask this: if you remove tuples from the language - what have you
lost - really? You can still say everything you could before.

A really convenient way to pass around multiple values without having to
bother with a formal struct.

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


(Freak Show) #9

A really convenient way to pass around multiple values without having to bother with a formal struct.

That's actually a big part of my concern.

The people on this list are, I'm certain, among the top programmers working.

I'm more worried about what happens when average (which IME means barely competent) developers get going with this. I suspect nobody will ever declare a struct again. Type declarations are valuable - they are an opportunity to express intent. OTOH, a pair of ints is a pair of ints and if all pairs of ints are type compatible then opportunities for catching errors drop if developers start favoring anonymous tuples over former structs.

I think pattern matching is the most compelling reason to keep tuples.

If they were gone, how would we replace the following?

switch (a, b) {
case (value1, value2):
case (value3, value4):
}

I really have to ask. What do you use this for? In general iPhone application programming I have never wanted or needed to do that. I do some AudioUnits as well. Still never needed it.

Since taking on rescuing a few 2.3 projects on behalf of their highly dissatisfied clients (I do this for all languages - not meant to be a Swift ding), I have found an astonishing abundance of switch statements that should have been handled by subclassing and polymorphism.

The only time I ever find a use for a switch statement is in a parser handling wild input.

Regardless, I would extend it to formal structs I think

switch StructName(a, b) {
case (value1, value2):
case (value3, value4):
}

Apart from your seeming distain for Swift

I tried very hard to keep from expressing anything like that in my proposal. Did I fail? How?

Language designs tend to encourage some behaviors and discourage others. The goal should be to encourage good practices and discourage bad ones. I am basing quite a lot of my opinion on the code being written by other developers that I am then asked to come in and work on. Second generation code. Is it aging well? Why or why not? Anonymous types everywhere isn't really making things safer. An anonymous pair of ints is an anonymous pair of ints. You might as well return an Array (and adding fixed dimensions as part of the immutable array type would solve this just as well). eg [T][4] or some such syntax. So mostly I'm seeing arguments to keep the syntax because the syntax is the syntax because that's what the type is so that is the syntax of the type.

The following two are collection types you’ve arbitrarily attacked for no reason. There are plenty of examples where each of these makes sense and is relevant within the language:
3. Arrays - Lists of items.
4. Dictionarys - Key value pair collections.

I did not attack them. I like them. I would generally use them instead of tuples. That's how Cocoa largely works now. I used them as comparisons.

Tuples themselves are actually a major part of how the language is built under the covers, and removing them for no reason is part of taking the guts out of Swift, for no reason.

I don't care what it looks like under the covers. I am talking about what it looks like to the developer. Seems a bit "Tower of Babel-ish" in some areas. The goal was to point that out and see if the language could be simplified.

···

On Jan 7, 2017, at 22:51, David Sweeris <davesweeris@mac.com> wrote:
On Jan 7, 2017, at 23:37, Derrick Ho <wh1pch81n@gmail.com> wrote:
On Jan 8, 2017, at 05:46, Rod Brown <rodney.brown6@icloud.com> wrote:

On Jan 7, 2017, at 22:51, David Sweeris <davesweeris@mac.com> wrote:

On Jan 7, 2017, at 19:34, Freak Show <freakshow42@mac.com> wrote:

I think you're missing the forrest for the trees here.'

Let me ask this: if you remove tuples from the language - what have you lost - really? You can still say everything you could before.

A really convenient way to pass around multiple values without having to bother with a formal struct.

- Dave Sweeris


(Austin Zheng) #10

The 'completing generics' document Doug put together has a section on this: https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md#extensions-of-structural-types

Austin

···

On Jan 9, 2017, at 12:38 AM, Tyler Cloutier via swift-evolution <swift-evolution@swift.org> wrote:

I’ll tell you it, though, it would be really nice to be able to add functions to tuples, just as they are done with structs.

Perhaps there is a way to formalize tuples as unnamed or anonymous Structs? It would make them going away strictly by making them more powerful.

On Jan 8, 2017, at 12:35 PM, Tino Heth via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

The point of this comparison is to point out that the anonymous type generating tuple is a) superfluous and b) a source of confusion and gratuitous complexity. It assumes the role at times of immutable arrays, anonymous structs, and immutable dictionaries depending on context and we already have all of those things.

There is another anonymous type — and it's very popular right now:
Closures*.
They are also a potential source of confusion (and even more complexity ;-), so should they be removed as well?

I guess we'll keep both, but you are definitely right that there is much complexity… but I'd rather improve that not by removing features, but by adding more regularity:
There has been a discussion about anonymous enums as well, and if we could come up with a syntax for anonymous reference types, imho everything would feel simpler.

Most likely someone already mentioned this, but tuples can be used in assignments in a way that isn't possible with any of the alternatives:

func tupleTest() -> (String, Int) {
  return ("Foo", 10)
}

let (name, count) = tupleTest()

print("There are \(count) \(name)")

- Tino

* so there is yet another (odd) variant:

var cxy = { (c: Character) -> Int in
  switch (c) {
  case "x": return 1
  case "y": return 2
  default: fatalError()
  }
}

_______________________________________________
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


(Derrick Ho) #11

I think pattern matching is the most compelling reason to keep tuples.

If they were gone, how would we replace the following?

switch (a, b) {
case (value1, value2):
case (value3, value4):
}

···

On Sun, Jan 8, 2017 at 2:31 AM Derrick Ho <wh1pch81n@gmail.com> wrote:

Don't remove tuples.
They make it very convenient to pass multiple values around.

Tuples use .0 instead of [0] which prevents any index out of bounds issues
at compile time rather than at run time.

Tuples should not adopt a dictionary style access because dictionaries
imply nil for any key that doesn't exist in the dictionary. Tuples syntax
prevents you from using non-existent keys.

On Sun, Jan 8, 2017 at 1:51 AM David Sweeris via swift-evolution < > swift-evolution@swift.org> wrote:

> On Jan 7, 2017, at 19:34, Freak Show <freakshow42@mac.com> wrote:
>
> I think you're missing the forrest for the trees here.'
>
> Let me ask this: if you remove tuples from the language - what have you
lost - really? You can still say everything you could before.

A really convenient way to pass around multiple values without having to
bother with a formal struct.

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


(David Sweeris) #12

A really convenient way to pass around multiple values without having to bother with a formal struct.

That's actually a big part of my concern.

The people on this list are, I'm certain, among the top programmers working.

I'm more worried about what happens when average (which IME means barely competent) developers get going with this. I suspect nobody will ever declare a struct again.

Doubtful, since tuples can't have any computed properties, functions, or conform to protocols.

Type declarations are valuable - they are an opportunity to express intent. OTOH, a pair of ints is a pair of ints and if all pairs of ints are type compatible then opportunities for catching errors drop if developers start favoring anonymous tuples over former structs.

I don't think they are... "(Int, Int)" (without labels) will type-check to any pair of Ints, but IIRC "(x:Int, y:Int)" won't type-check to "(a:Int, b:Int)".

···

On Jan 8, 2017, at 09:33, Freak Show <freakshow42@mac.com> wrote:

On Jan 7, 2017, at 22:51, David Sweeris <davesweeris@mac.com> wrote:

On Jan 7, 2017, at 23:37, Derrick Ho <wh1pch81n@gmail.com> wrote:

I think pattern matching is the most compelling reason to keep tuples.

If they were gone, how would we replace the following?

switch (a, b) {
case (value1, value2):
case (value3, value4):
}

I really have to ask. What do you use this for? In general iPhone application programming I have never wanted or needed to do that. I do some AudioUnits as well. Still never needed it.

"Need" is a strong word... Yeah, I could switch over the first value and then for every single case nest another switch over the second value, but that'd be annoying and obscure the underlying logic.

- Dave Sweeris


(Tony Allevato) #13

A really convenient way to pass around multiple values without having to

bother with a formal struct.

That's actually a big part of my concern.

The people on this list are, I'm certain, among the top programmers working.

I'm more worried about what happens when average (which IME means barely
competent) developers get going with this. I suspect nobody will ever
declare a struct again.

Swift has been around for two and a half years now. Do you have any
evidence that developers have stopped declaring structs in such widespread
numbers that everyone needs to be protected from them by removing a useful
feature?

  Type declarations are valuable - they are an opportunity to express
intent. OTOH, a pair of ints is a pair of ints and if all pairs of ints
are type compatible then opportunities for catching errors drop if
developers start favoring anonymous tuples over former structs.

Anonymous types are also an opportunity to express intent. If I wrote a
division function that returns a quotient and remainder:

func divide(_ dividend: Int, by divisor: Int) -> (quotient: Int, remainder:
Int) { ... }

q, r = divide(18, by: 5)
q, _ = divide(20, by: 3)
_, r = divide(17, by: 6)

My intent is to return two values. That's all. My intent is *not* to create
a first-class type that can be constructed, passed around, or mutated.
Creating a "QuotientAndRemainder" type would complicate the design, not
improve it.

I think pattern matching is the most compelling reason to keep tuples.

If they were gone, how would we replace the following?

switch (a, b) {
case (value1, value2):
case (value3, value4):
}

I really have to ask. What do you use this for? In general iPhone
application programming I have never wanted or needed to do that. I do
some AudioUnits as well. Still never needed it.

The time I use this most frequently is enum comparison with associated
values:

enum Foo: Equatable {
  case one(Int)
  case two(String)

  static func ==(lhs: Foo, rhs: Foo) -> Bool {
    switch (lhs, rhs):
    case (.one(let lhsValue), .one(let rhsValue)): return lhsValue ==
rhsValue
    case (.two(let lhsValue), .two(let rhsValue)): return lhsValue ==
rhsValue
    default: return false
  }
}

There's no reason to formalize a struct and use a named type for that—a
tuple does exactly what is needed there, which is an anonymous ordered
grouping of values.

Since taking on rescuing a few 2.3 projects on behalf of their highly
dissatisfied clients (I do this for all languages - not meant to be a Swift
ding), I have found an astonishing abundance of switch statements that
should have been handled by subclassing and polymorphism.

Choose the right design to solve the right problem. If developers are
misusing switches when polymorphism is a better solution, then that should
be fixed in their code, not by removing a useful language feature that
serves other purposes.

The only time I ever find a use for a switch statement is in a parser
handling wild input.

Regardless, I would extend it to formal structs I think

switch StructName(a, b) {
case (value1, value2):
case (value3, value4):
}

Apart from your seeming distain for Swift

I tried very hard to keep from expressing anything like that in my
proposal. Did I fail? How?

Language designs tend to encourage some behaviors and discourage others.
The goal should be to encourage good practices and discourage bad ones. I
am basing quite a lot of my opinion on the code being written by other
developers that I am then asked to come in and work on. Second generation
code. Is it aging well? Why or why not? Anonymous types everywhere isn't
really making things safer.

Bad developers will write bad code, regardless of what language features
are available. The goal should be to educate them on the proper way to do
things, not to restrict *everyone's* ability because of those using bad
practices.

  An anonymous pair of ints is an anonymous pair of ints. You might as
well return an Array (and adding fixed dimensions as part of the immutable
array type would solve this just as well). eg [T][4] or some such syntax.
So mostly I'm seeing arguments to keep the syntax because the syntax is the
syntax because that's what the type is so that is the syntax of the type.

The following two are collection types you’ve arbitrarily attacked for no

reason. There are plenty of examples where each of these makes sense and is
relevant within the language:

3. Arrays - Lists of items.
4. Dictionarys - Key value pair collections.

I did not attack them. I like them. I would generally use them instead of
tuples. That's how Cocoa largely works now. I used them as comparisons.

Tuples themselves are actually a major part of how the language is built

under the covers, and removing them for no reason is part of taking the
guts out of Swift, for no reason.

I don't care what it looks like under the covers. I am talking about what
it looks like to the developer. Seems a bit "Tower of Babel-ish" in some
areas. The goal was to point that out and see if the language could be
simplified.

I think you're missing the forrest for the trees here.'

Let me ask this: if you remove tuples from the language - what have you

lost - really? You can still say everything you could before.

A really convenient way to pass around multiple values without having to

bother with a formal struct.

···

On Sun, Jan 8, 2017 at 9:33 AM Freak Show via swift-evolution < swift-evolution@swift.org> wrote:

On Jan 7, 2017, at 22:51, David Sweeris <davesweeris@mac.com> wrote:
On Jan 7, 2017, at 23:37, Derrick Ho <wh1pch81n@gmail.com> wrote:
On Jan 8, 2017, at 05:46, Rod Brown <rodney.brown6@icloud.com> wrote:
On Jan 7, 2017, at 22:51, David Sweeris <davesweeris@mac.com> wrote:

On Jan 7, 2017, at 19:34, Freak Show <freakshow42@mac.com> wrote:

- Dave Sweeris

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


(Freak Show) #14

That would be fine.

···

On Jan 8, 2017, at 10:31, Micah Hainline <micah.hainline@gmail.com> wrote:

Perhaps we could limit further discussion here to the idea the original poster put forth about eliminating mixed named and unnamed tuple labels.


(Micah Hainline) #15

I feel that this thread has reached the predictable consensus conclusion that we should not eliminate tuples.

Perhaps we could limit further discussion here to the idea the original poster put forth about eliminating mixed named and unnamed tuple labels.

···

On Jan 8, 2017, at 12:23 PM, Tony Allevato via swift-evolution <swift-evolution@swift.org> wrote:

On Sun, Jan 8, 2017 at 9:33 AM Freak Show via swift-evolution <swift-evolution@swift.org> wrote:
> On Jan 7, 2017, at 22:51, David Sweeris <davesweeris@mac.com> wrote:

> A really convenient way to pass around multiple values without having to bother with a formal struct.

That's actually a big part of my concern.

The people on this list are, I'm certain, among the top programmers working.

I'm more worried about what happens when average (which IME means barely competent) developers get going with this. I suspect nobody will ever declare a struct again.

Swift has been around for two and a half years now. Do you have any evidence that developers have stopped declaring structs in such widespread numbers that everyone needs to be protected from them by removing a useful feature?

  Type declarations are valuable - they are an opportunity to express intent. OTOH, a pair of ints is a pair of ints and if all pairs of ints are type compatible then opportunities for catching errors drop if developers start favoring anonymous tuples over former structs.

Anonymous types are also an opportunity to express intent. If I wrote a division function that returns a quotient and remainder:

func divide(_ dividend: Int, by divisor: Int) -> (quotient: Int, remainder: Int) { ... }

q, r = divide(18, by: 5)
q, _ = divide(20, by: 3)
_, r = divide(17, by: 6)

My intent is to return two values. That's all. My intent is *not* to create a first-class type that can be constructed, passed around, or mutated. Creating a "QuotientAndRemainder" type would complicate the design, not improve it.

> On Jan 7, 2017, at 23:37, Derrick Ho <wh1pch81n@gmail.com> wrote:
>
> I think pattern matching is the most compelling reason to keep tuples.
>
> If they were gone, how would we replace the following?
>
> switch (a, b) {
> case (value1, value2):
> case (value3, value4):
> }

I really have to ask. What do you use this for? In general iPhone application programming I have never wanted or needed to do that. I do some AudioUnits as well. Still never needed it.

The time I use this most frequently is enum comparison with associated values:

enum Foo: Equatable {
  case one(Int)
  case two(String)

  static func ==(lhs: Foo, rhs: Foo) -> Bool {
    switch (lhs, rhs):
    case (.one(let lhsValue), .one(let rhsValue)): return lhsValue == rhsValue
    case (.two(let lhsValue), .two(let rhsValue)): return lhsValue == rhsValue
    default: return false
  }
}

There's no reason to formalize a struct and use a named type for that—a tuple does exactly what is needed there, which is an anonymous ordered grouping of values.

Since taking on rescuing a few 2.3 projects on behalf of their highly dissatisfied clients (I do this for all languages - not meant to be a Swift ding), I have found an astonishing abundance of switch statements that should have been handled by subclassing and polymorphism.

Choose the right design to solve the right problem. If developers are misusing switches when polymorphism is a better solution, then that should be fixed in their code, not by removing a useful language feature that serves other purposes.

The only time I ever find a use for a switch statement is in a parser handling wild input.

Regardless, I would extend it to formal structs I think

switch StructName(a, b) {
case (value1, value2):
case (value3, value4):
}

> On Jan 8, 2017, at 05:46, Rod Brown <rodney.brown6@icloud.com> wrote:
>
> Apart from your seeming distain for Swift

I tried very hard to keep from expressing anything like that in my proposal. Did I fail? How?

Language designs tend to encourage some behaviors and discourage others. The goal should be to encourage good practices and discourage bad ones. I am basing quite a lot of my opinion on the code being written by other developers that I am then asked to come in and work on. Second generation code. Is it aging well? Why or why not? Anonymous types everywhere isn't really making things safer.

Bad developers will write bad code, regardless of what language features are available. The goal should be to educate them on the proper way to do things, not to restrict *everyone's* ability because of those using bad practices.

  An anonymous pair of ints is an anonymous pair of ints. You might as well return an Array (and adding fixed dimensions as part of the immutable array type would solve this just as well). eg [T][4] or some such syntax. So mostly I'm seeing arguments to keep the syntax because the syntax is the syntax because that's what the type is so that is the syntax of the type.

> The following two are collection types you’ve arbitrarily attacked for no reason. There are plenty of examples where each of these makes sense and is relevant within the language:
> 3. Arrays - Lists of items.
> 4. Dictionarys - Key value pair collections.

I did not attack them. I like them. I would generally use them instead of tuples. That's how Cocoa largely works now. I used them as comparisons.

> Tuples themselves are actually a major part of how the language is built under the covers, and removing them for no reason is part of taking the guts out of Swift, for no reason.

I don't care what it looks like under the covers. I am talking about what it looks like to the developer. Seems a bit "Tower of Babel-ish" in some areas. The goal was to point that out and see if the language could be simplified.

> On Jan 7, 2017, at 22:51, David Sweeris <davesweeris@mac.com> wrote:
>
>
>
>> On Jan 7, 2017, at 19:34, Freak Show <freakshow42@mac.com> wrote:
>>
>> I think you're missing the forrest for the trees here.'
>>
>> Let me ask this: if you remove tuples from the language - what have you lost - really? You can still say everything you could before.
>
> A really convenient way to pass around multiple values without having to bother with a formal struct.
>
> - Dave Sweeris

_______________________________________________
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


(Derrick Ho) #16

I think pattern matching is the most compelling reason to keep tuples.

If they were gone, how would we replace the following?

switch (a, b) {

case (value1, value2):

case (value3, value4):

}

I really have to ask. What do you use this for? In general iPhone
application programming I have never wanted or needed to do that. I do
some AudioUnits as well. Still never needed it.

The alternative would be something like this

if a == value1 && b == value2 {
} else if a == value3 && b == value4 {
}

You would end up repeating boilerplate code. Plus, the switch statements
guarantee that all cases are exhaustive while using a bunch of
if-statements would not. A switch statement would force you to provide a
default case if you did not cover all the cases, but an if-statement
doesn't stop you.

Exhaustive switch statements are popular among low-level C programming
people that work with micro-controllers like Arduino. I believe one of
swifts goals is to enter that territory.

···

On Jan 7, 2017, at 23:37, Derrick Ho <wh1pch81n@gmail.com> wrote:

On Sun, Jan 8, 2017 at 1:49 PM David Sweeris <davesweeris@mac.com> wrote:

On Jan 8, 2017, at 09:33, Freak Show <freakshow42@mac.com> wrote:

On Jan 7, 2017, at 22:51, David Sweeris <davesweeris@mac.com> wrote:

A really convenient way to pass around multiple values without having to
bother with a formal struct.

That's actually a big part of my concern.

The people on this list are, I'm certain, among the top programmers
working.

I'm more worried about what happens when average (which IME means barely
competent) developers get going with this. I suspect nobody will ever
declare a struct again.

Doubtful, since tuples can't have any computed properties, functions, or
conform to protocols.

Type declarations are valuable - they are an opportunity to express
intent. OTOH, a pair of ints is a pair of ints and if all pairs of ints
are type compatible then opportunities for catching errors drop if
developers start favoring anonymous tuples over former structs.

I don't think they are... "(Int, Int)" (without labels) will type-check to
any pair of Ints, but IIRC "(x:Int, y:Int)" won't type-check to "(a:Int,
b:Int)".

On Jan 7, 2017, at 23:37, Derrick Ho <wh1pch81n@gmail.com> wrote:

I think pattern matching is the most compelling reason to keep tuples.

If they were gone, how would we replace the following?

switch (a, b) {

case (value1, value2):

case (value3, value4):

}

I really have to ask. What do you use this for? In general iPhone
application programming I have never wanted or needed to do that. I do
some AudioUnits as well. Still never needed it.

"Need" is a strong word... Yeah, I *could* switch over the first value
and then for *every single case* nest another switch over the second
value, but that'd be annoying *and* obscure the underlying logic.

- Dave Sweeris


(Freak Show) #17

I meant to mention this: Smalltalk - Objective C's mother language - has no switch statement (or 'if' or loops either). The language is incredibly malleable because it only does one thing - send messages to objects and all the language constructs are in the library. It would take very little time to add one. Off and on someone does it as an exercise but it never sticks.

Instead, you just use a dictionary of closures. An Objective C equivalent might be:

NSDictionary* switch = @{
  @[@0,@1]: ^{ NSLog(@"zero one"); },
  @[@1,@1]: ^{ NSLog(@"one one"); }
};

NSArray* pair = @[@3, @5];

(switch at:pair ifAbsent:^{})(); //where at:ifAbsent: is added in the Smalltalk style as an extension.

The Smalltalk equivalent (much less ugly because of the lack of @'s) is

switch := {
  #(0 1) -> [ Transcript nextPutAll: 'zero one' ] .
  #(1 1) -> [ Transcript nextPutAll: 'one one' ] .
  #(1 2) -> [ Transcript nextPutAll: 'one two' ] .
} asDictionary.

(switch at: pair ifAbsent:[ [] ]) value.

So its not like this is some kind of key feature. Switch's vs dictionaries of closures - pretty much the same thing as pattern matching goes. The only thing you have to do is put an object at key that identifies itself as equal to the pattern you will throw at it.

···

On Jan 7, 2017, at 23:37, Derrick Ho <wh1pch81n@gmail.com> wrote:

I think pattern matching is the most compelling reason to keep tuples.

If they were gone, how would we replace the following?

switch (a, b) {
case (value1, value2):
case (value3, value4):
}


(David Sweeris) #18

I suspect that's a fair bit slower than a switch statement. I'm not in front of my computer, so I can't prove it, though.

- Dave Sweeris

···

On Jan 11, 2017, at 01:11, Freak Show <freakshow42@mac.com> wrote:

On Jan 7, 2017, at 23:37, Derrick Ho <wh1pch81n@gmail.com> wrote:

I think pattern matching is the most compelling reason to keep tuples.

If they were gone, how would we replace the following?

switch (a, b) {
case (value1, value2):
case (value3, value4):
}

I meant to mention this: Smalltalk - Objective C's mother language - has no switch statement (or 'if' or loops either). The language is incredibly malleable because it only does one thing - send messages to objects and all the language constructs are in the library. It would take very little time to add one. Off and on someone does it as an exercise but it never sticks.

Instead, you just use a dictionary of closures. An Objective C equivalent might be:

NSDictionary* switch = @{
  @[@0,@1]: ^{ NSLog(@"zero one"); },
  @[@1,@1]: ^{ NSLog(@"one one"); }
};

NSArray* pair = @[@3, @5];

(switch at:pair ifAbsent:^{})(); //where at:ifAbsent: is added in the Smalltalk style as an extension.

The Smalltalk equivalent (much less ugly because of the lack of @'s) is

switch := {
  #(0 1) -> [ Transcript nextPutAll: 'zero one' ] .
  #(1 1) -> [ Transcript nextPutAll: 'one one' ] .
  #(1 2) -> [ Transcript nextPutAll: 'one two' ] .
} asDictionary.

(switch at: pair ifAbsent:[ [] ]) value.

So its not like this is some kind of key feature. Switch's vs dictionaries of closures - pretty much the same thing as pattern matching goes. The only thing you have to do is put an object at key that identifies itself as equal to the pattern you will throw at it.


(André Videla) #19

NSDictionary* switch = @{
  @[@0,@1]: ^{ NSLog(@"zero one"); },
  @[@1,@1]: ^{ NSLog(@"one one"); }
};

What you're describing here is only a narrow use of switch statements and doesn't handle the most powerful features of the switch statement: pattern matching.

How would you go about
Switch (optional, superclass, genericStruct) {
    Case (let value?, let sub as SubClass, Generic<ConcreteType>: // use all this stuff
    case _: // nothing matches
}

You don't seem familiar with switches and tuple deconstruction so here is a common everyday use case: prepare segues between view controllers:

func prepareForSegue(segue: UIStoryboardSegue, sender: Any?) {
    Switch (segue.identifier, segue.destination, sender) {
        Case ("Segue"?, let vc as FirstVC, .some(.enumType(let value)) ): // prepare the vc
        ... // other segues
    }
}

So its not like this is some kind of key feature.

Dictionaries require their keys to be hashable. That's a problem with exhausitveness checking. A hashable Type has infinite cardinality and as such, can't be exhaustive. Therefore the compiler will never be able to check the completeness of your switch statement, throwing away one the languages key feature: safety.

I think we should steer this conversation back to tuple labels.

Andre Videla

···

On 11 Jan 2017, at 08:11, Freak Show via swift-evolution <swift-evolution@swift.org> wrote:

On Jan 7, 2017, at 23:37, Derrick Ho <wh1pch81n@gmail.com> wrote:

I think pattern matching is the most compelling reason to keep tuples.

If they were gone, how would we replace the following?

switch (a, b) {
case (value1, value2):
case (value3, value4):
}

I meant to mention this: Smalltalk - Objective C's mother language - has no switch statement (or 'if' or loops either). The language is incredibly malleable because it only does one thing - send messages to objects and all the language constructs are in the library. It would take very little time to add one. Off and on someone does it as an exercise but it never sticks.

Instead, you just use a dictionary of closures. An Objective C equivalent might be:

NSDictionary* switch = @{
  @[@0,@1]: ^{ NSLog(@"zero one"); },
  @[@1,@1]: ^{ NSLog(@"one one"); }
};

NSArray* pair = @[@3, @5];

(switch at:pair ifAbsent:^{})(); //where at:ifAbsent: is added in the Smalltalk style as an extension.

The Smalltalk equivalent (much less ugly because of the lack of @'s) is

switch := {
  #(0 1) -> [ Transcript nextPutAll: 'zero one' ] .
  #(1 1) -> [ Transcript nextPutAll: 'one one' ] .
  #(1 2) -> [ Transcript nextPutAll: 'one two' ] .
} asDictionary.

(switch at: pair ifAbsent:[ [] ]) value.

So its not like this is some kind of key feature. Switch's vs dictionaries of closures - pretty much the same thing as pattern matching goes. The only thing you have to do is put an object at key that identifies itself as equal to the pattern you will throw at it.

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


(David Sweeris) #20

Also, Swift's dictionary requires its keys to conform to `Hashable`, which would mean we'd have to create a formal struct *and* the "==" method for it would have to act as a pattern matched instead of checking equality. I suspect this could break a great many things that depend on "Equatable" meaning what it says.

- Dave Sweeris (again)

···

On Jan 11, 2017, at 01:29, David Sweeris via swift-evolution <swift-evolution@swift.org> wrote:

On Jan 11, 2017, at 01:11, Freak Show <freakshow42@mac.com> wrote:

On Jan 7, 2017, at 23:37, Derrick Ho <wh1pch81n@gmail.com> wrote:

I think pattern matching is the most compelling reason to keep tuples.

If they were gone, how would we replace the following?

switch (a, b) {
case (value1, value2):
case (value3, value4):
}

I meant to mention this: Smalltalk - Objective C's mother language - has no switch statement (or 'if' or loops either). The language is incredibly malleable because it only does one thing - send messages to objects and all the language constructs are in the library. It would take very little time to add one. Off and on someone does it as an exercise but it never sticks.

Instead, you just use a dictionary of closures. An Objective C equivalent might be:

NSDictionary* switch = @{
  @[@0,@1]: ^{ NSLog(@"zero one"); },
  @[@1,@1]: ^{ NSLog(@"one one"); }
};

NSArray* pair = @[@3, @5];

(switch at:pair ifAbsent:^{})(); //where at:ifAbsent: is added in the Smalltalk style as an extension.

The Smalltalk equivalent (much less ugly because of the lack of @'s) is

switch := {
  #(0 1) -> [ Transcript nextPutAll: 'zero one' ] .
  #(1 1) -> [ Transcript nextPutAll: 'one one' ] .
  #(1 2) -> [ Transcript nextPutAll: 'one two' ] .
} asDictionary.

(switch at: pair ifAbsent:[ [] ]) value.

So its not like this is some kind of key feature. Switch's vs dictionaries of closures - pretty much the same thing as pattern matching goes. The only thing you have to do is put an object at key that identifies itself as equal to the pattern you will throw at it.

I suspect that's a fair bit slower than a switch statement. I'm not in front of my computer, so I can't prove it, though.