another issue with tuples


(Aaron Bohannon) #1

Yesterday, it was pointed out that a variable name referring to a tuple
cannot be used as a pattern. I have noticed another sort of inconsistency
in how tuples are treated when they are referenced by name:

func f() -> Int { return 5 }

let t = ("a", f)

let _: (String, () throws -> Int) = t // type error
let _: (String, () throws -> Int) = (t.0, t.1) // OK

This situation leads to a different sort of type error; however, the error
seems equally unwarranted. I can't see any good reason for a well-typed
program to become ill-typed when `(t.0, t.1)` is replaced with `t`
(assuming `t` is a pair).

Should I file a separate bug for the specific example above?

- Aaron


(Shane S) #2

Aaron this works for me in both Swift 2.2 and Swift 3 provided that you remove the ‘throws’ keyword.

What seems odd to me is not the first assignment, but rather the second that _allows_ the use of ‘throws’ when t.1 (i.e. f) does not throw - is your concern the same?

Shane

···

On Jul 5, 2016, at 10:48 PM, Aaron Bohannon via swift-users <swift-users@swift.org<mailto:swift-users@swift.org>> wrote:

Yesterday, it was pointed out that a variable name referring to a tuple cannot be used as a pattern. I have noticed another sort of inconsistency in how tuples are treated when they are referenced by name:

func f() -> Int { return 5 }

let t = ("a", f)

let _: (String, () throws -> Int) = t // type error
let _: (String, () throws -> Int) = (t.0, t.1) // OK

This situation leads to a different sort of type error; however, the error seems equally unwarranted. I can't see any good reason for a well-typed program to become ill-typed when `(t.0, t.1)` is replaced with `t` (assuming `t` is a pair).

Should I file a separate bug for the specific example above?

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


(Zhao Xin) #3

Tuple is not designed to be used as commonly as classes and structs.

NOTE

Tuples are useful for temporary groups of related values. They are not
suited to the creation of complex data structures. If your data structure
is likely to persist beyond a temporary scope, model it as a class or
structure, rather than as a tuple. For more information, see Classes and
Structures.

quoting from:

https://developer.apple.com/library/prerelease/content/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html

If you looking up in the documents, there is no struct nor class called
Tuple. The normal using on tuple in my option is to returning multiple
values in a function, or switch-case matching.

So you are right, tuple is not well-formed. But it is designed to be used
only temporarily. Please feel free to file another bug.

Zhaoxin

···

On Wed, Jul 6, 2016 at 1:48 PM, Aaron Bohannon via swift-users < swift-users@swift.org> wrote:

Yesterday, it was pointed out that a variable name referring to a tuple
cannot be used as a pattern. I have noticed another sort of inconsistency
in how tuples are treated when they are referenced by name:

func f() -> Int { return 5 }

let t = ("a", f)

let _: (String, () throws -> Int) = t // type error
let _: (String, () throws -> Int) = (t.0, t.1) // OK

This situation leads to a different sort of type error; however, the error
seems equally unwarranted. I can't see any good reason for a well-typed
program to become ill-typed when `(t.0, t.1)` is replaced with `t`
(assuming `t` is a pair).

Should I file a separate bug for the specific example above?

- Aaron

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


(Neil Faiman) #4

The language reference says that “Assignment is performed from each part of the value to the corresponding part of the expression. A String value can be assigned to a String, and a () -> Int value can be assigned to a () throws -> Int, so one would reasonably expect that both tuple assignments would work.

My hunch would be that the compiler has two different code paths for an assignment whose RHS is an identifier and an assignment whose RHS is a parenthesized-expression; that in the first case starts about by asking whether the RHS type is the same or a subtype of the LHS type, and reporting an error when it isn’t, but that in the second case, it starts with an element-by-element type comparison, which succeeds.

Regards,

  Neil

···

On Jul 6, 2016, at 9:55 AM, Shane S via swift-users <swift-users@swift.org> wrote:

Aaron this works for me in both Swift 2.2 and Swift 3 provided that you remove the ‘throws’ keyword.

What seems odd to me is not the first assignment, but rather the second that _allows_ the use of ‘throws’ when t.1 (i.e. f) does not throw - is your concern the same?

Shane

On Jul 5, 2016, at 10:48 PM, Aaron Bohannon via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:

Yesterday, it was pointed out that a variable name referring to a tuple cannot be used as a pattern. I have noticed another sort of inconsistency in how tuples are treated when they are referenced by name:

func f() -> Int { return 5 }

let t = ("a", f)

let _: (String, () throws -> Int) = t // type error
let _: (String, () throws -> Int) = (t.0, t.1) // OK

This situation leads to a different sort of type error; however, the error seems equally unwarranted. I can't see any good reason for a well-typed program to become ill-typed when `(t.0, t.1)` is replaced with `t` (assuming `t` is a pair).

Should I file a separate bug for the specific example above?

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

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


(Shane S) #5

The section you are referencing is regarding ‘assignment’; however, in the example below we aren’t talking about assignment, this is 'constant-declaration', which is different than a ‘parenthesized expression’.

But overall I get it, and I think I’m trending toward the same conclusion you reached re: checking the full type of the identifier -vs- comparing the types of the constituent elements - interesting stuff!

Shane

···

On Jul 6, 2016, at 7:34 AM, Neil Faiman <neil.swift@faiman.org<mailto:neil.swift@faiman.org>> wrote:

The language reference says that “Assignment is performed from each part of the value to the corresponding part of the expression. A String value can be assigned to a String, and a () -> Int value can be assigned to a () throws -> Int, so one would reasonably expect that both tuple assignments would work.

My hunch would be that the compiler has two different code paths for an assignment whose RHS is an identifier and an assignment whose RHS is a parenthesized-expression; that in the first case starts about by asking whether the RHS type is the same or a subtype of the LHS type, and reporting an error when it isn’t, but that in the second case, it starts with an element-by-element type comparison, which succeeds.

Regards,

Neil

On Jul 6, 2016, at 9:55 AM, Shane S via swift-users <swift-users@swift.org<mailto:swift-users@swift.org>> wrote:

Aaron this works for me in both Swift 2.2 and Swift 3 provided that you remove the ‘throws’ keyword.

What seems odd to me is not the first assignment, but rather the second that _allows_ the use of ‘throws’ when t.1 (i.e. f) does not throw - is your concern the same?

Shane

On Jul 5, 2016, at 10:48 PM, Aaron Bohannon via swift-users <swift-users@swift.org<mailto:swift-users@swift.org>> wrote:

Yesterday, it was pointed out that a variable name referring to a tuple cannot be used as a pattern. I have noticed another sort of inconsistency in how tuples are treated when they are referenced by name:

func f() -> Int { return 5 }

let t = ("a", f)

let _: (String, () throws -> Int) = t // type error
let _: (String, () throws -> Int) = (t.0, t.1) // OK

This situation leads to a different sort of type error; however, the error seems equally unwarranted. I can't see any good reason for a well-typed program to become ill-typed when `(t.0, t.1)` is replaced with `t` (assuming `t` is a pair).

Should I file a separate bug for the specific example above?

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

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


(Aaron Bohannon) #6

This inconsistency is not limited to situations in which a tuple is a
non-temporary value:

class C {}

let _ = (C(), 3 as Int) as (Any, Int?) // WORKS FINE
let _ = (C(), 3) as (C, Int) as (Any, Int?) // TYPE ERROR
let _ = ((C(), 3), true).0 as (Any, Int?) // TYPE ERROR

It can even cause the compiler to crash during type-checking on expressions
like the one below (which ought to be considered well-typed):

let _ = "a".isEmpty ? (C(), 3) as (C, Int) : (C(), 3) as (Any, Int?)

And it leads to some rather amusing output if you compile and run this code:

if (C(), 3 as Int) is (Any, Int?) {
  print("test is true")
} else {
  print("test is false")
}

The outcome will look something like this:

*test.swift:88:20: warning: 'is' test is always trueif (C(), 3 as Int) is
(Any, Int?) { ^test is false*

My bug report contains versions of all of those examples:

https://bugs.swift.org/browse/SR-2041

I used type casts above in order to make the origin of the problem as clear
as possible; however, it's also worth adding one more example to illustrate
that this problem is not limited to code that is contrived or uses tuples
in ways that are stylistically discouraged:

func returnPair() -> (C, Int) { return (C(), 3) }
var x: Int? = nil

x = returnPair().1 // WORKS FINE

(_, x) = returnPair() // TYPE ERROR

- Aaron

···

On Wed, Jul 6, 2016 at 8:57 AM, Shane S <electro_alchemy@hotmail.com> wrote:

The section you are referencing is regarding ‘assignment’; however, in the
example below we aren’t talking about assignment, this is
'constant-declaration', which is different than a ‘parenthesized
expression’.

But overall I get it, and I think I’m trending toward the same conclusion
you reached re: checking the full type of the identifier -vs- comparing the
types of the constituent elements - interesting stuff!

Shane

On Jul 6, 2016, at 7:34 AM, Neil Faiman <neil.swift@faiman.org> wrote:

The language reference says that “Assignment is performed from each part
of the *value* to the corresponding part of the *expression*. A *String* value
can be assigned to a *String*, and a *() -> Int* value can be assigned to
a *() throws -> Int*, so one would reasonably expect that both tuple
assignments would work.

My hunch would be that the compiler has two different code paths for an
assignment whose RHS is an *identifier* and an assignment whose RHS is a
*parenthesized-expression*; that in the first case starts about by asking
whether the RHS type is the same or a subtype of the LHS type, and
reporting an error when it isn’t, but that in the second case, it starts
with an element-by-element type comparison, which succeeds.

Regards,

Neil

On Jul 6, 2016, at 9:55 AM, Shane S via swift-users <swift-users@swift.org> > wrote:

Aaron this works for me in both Swift 2.2 and Swift 3 provided that you
remove the ‘throws’ keyword.

What seems odd to me is not the first assignment, but rather the second
that _allows_ the use of ‘throws’ when t.1 (i.e. f) does not throw - is
your concern the same?

Shane

On Jul 5, 2016, at 10:48 PM, Aaron Bohannon via swift-users < > swift-users@swift.org> wrote:

Yesterday, it was pointed out that a variable name referring to a tuple
cannot be used as a pattern. I have noticed another sort of inconsistency
in how tuples are treated when they are referenced by name:

func f() -> Int { return 5 }

let t = ("a", f)

let _: (String, () throws -> Int) = t // type error
let _: (String, () throws -> Int) = (t.0, t.1) // OK

This situation leads to a different sort of type error; however, the error
seems equally unwarranted. I can't see any good reason for a well-typed
program to become ill-typed when `(t.0, t.1)` is replaced with `t`
(assuming `t` is a pair).

Should I file a separate bug for the specific example above?

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

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