Proposal: Remove implicit tuple splat behavior from function applications


(Chris Lattner) #1

For discussion: comments appreciated!

Remove implicit tuple splat behavior from function applications

Proposal: SE- <https://github.com/apple/swift-evolution/blob/master/proposals/NNNN-name.md>TBD
Author(s): Chris Lattner
Status: Awaiting review
Review manager: TBD
<https://github.com/apple/swift-evolution#introduction>Introduction

Function calls (which include several syntactic forms that apply an argument list to something of function type) currently have a dual nature in Swift. Given something like:

  func foo(a : Int, b : Int) {}

You can call it either with with the typical syntactic form that passes arguments to each of its parameters:

  foo(42, b : 17)

or you can take advantage of a little-known feature to pass an entire argument list as a single value (of tuple type):

  let x = (1, b: 2)
  foo(x)

This proposal recommends removing the later form, which I affectionately refer to as the “tuple splat” form. This feature is purely a sugar feature, it does not provide any expressive ability beyond passing the parameters manually.

Swift-evolution thread: TBD

<https://github.com/apple/swift-evolution#motivation>Motivation

This behavior is cute, precedented in other functional languages, and has some advantages, but it also has several major disadvantages, which are all related to its syntactic form.

* A call to foo(x) looks like a call to an overloaded version of foo, both to the compiler and to the human who maintains the code. This is extremely confusing if you don’t know the feature exists.
* There are real ambiguities in the syntax, e.g. involving Any arguments and situations where you want to pass a tuple value as a single parameter.
* The current implementation has a ton of implementation bugs - it doesn’t work reliably.
* The current implementation adds complexity to the type checker, slowing it down and adding maintenance burden.
* The current implementation doesn’t work the way we would want a tuple splat operation to work. For example, arguably, you should be able to call foo with:

  func bar() -> (Int, Int) { … }
  foo(bar())

… but this is not allowed, since tuple labels are required to line up. You have to write:

  func bar() -> (Int, b: Int) { … }
  foo(bar())

This makes this feature very difficult to use in practice, because you have to _’ize a lot of parameters (violating naming conventions), perform manual shuffling (defeating the sugar benefits of the feature), or add parameter labels to the result of functions (which leads to odd tying between callers and callees).

The root problem here is that we use exactly the same syntax for both forms of function application. If the two forms were differentiated (an option considered in “alternatives considered” below) then some of these problems would be defined away.

From a historical perspective, the tuple splat form of function application dates back to very early Swift design (probably introduced in 2010, but possibly 2011) where all function application was of a single value to a function type. For a large number of reasons (including default arguments, variadic arguments, labels, etc) we have completely abandoned this model, but we never came back to reevaluating the tuple splat behavior.

If we didn’t already have this feature, we would not add it to Swift 3 (at least in its current form).

<https://github.com/apple/swift-evolution#proposed-solution>Proposed solution

The proposed solution is simple, we should just remove this feature from the Swift 3 compiler. Ideally we would deprecate it in the Swift 2.2 compiler and remove it in Swift 3. However, if there isn’t time to get the deprecation into Swift 2.2, the author believes it would be perfectly fine to just remove it in Swift 3 (with a fixit + migration help of course).

One of the interesting aspect of this feature is that some of the people we’ve spoken to are very fond of it. However, when pressed, they admit that they are not actually using it widely in their code, or if they are using it, they are abusing naming conventions (distorting their code) in order to use it. This doesn’t seem like a positive contribution - this seems like a “clever” feature, not a practical one.

<https://github.com/apple/swift-evolution#detailed-design>Detailed design

The design is straight-forward. In the Swift 3 time frame, we continue to parse and type check these expressions as we have so far, but produce an error + fixit hint when it is the tuple splat form. The migrator would auto-apply the fixit hint as it does for other cases.

<https://github.com/apple/swift-evolution#impact-on-existing-code>Impact on existing code

Any code that uses this feature will have to move to the traditional form. In the case of the example above, this means rewriting the code from:

  foo(x)

into a form like this:

  foo(x.0, x.b)

In the case where “x” is a complex expression, a temporary variable will need to be introduced. We believe that compiler fixits can handle the simple cases directly and that this extension is not widely used.

<https://github.com/apple/swift-evolution#alternatives-considered>Alternatives considered

The major problem with this feature is that it was not well considered and implemented properly (owing to its very old age, it has just been kept limping along). The alternative then is to actually design a proper feature to support this. Since the implicitness and syntactic ambiguity with normal function application is the problem, the solution is to introduce an explicit syntactic form to represent this. For example, something like this could address the problems we have:

  foo(*x) // NOT a serious syntax proposal

However, actually designing this feature would be a non-trivial effort not core to the Swift 3 mission:

* It is a pure-sugar feature, and therefore low priority.
* We don’t have an obvious sigil to use. “prefix-star” should be kept as unused for now in case we want to use it to refer to memory-related operations in the future.
* Making the tuple splat operation great requires more than just fixing the syntactic ambiguities we have, it would require re-evaluating the semantics of the operation (e.g. in light of parameter labels, varargs and other features).

If there is serious interest in pursuing this as a concept, we should do it as a follow-on proposal to this one. If a good design emerges, we can evaluate that design based on its merits.

The final alternative is that we could leave the feature in the compiler. However, that means living with its complexity “forever” or breaking code in the Swift 4 timeframe. It would be preferable to tackle this breakage in the Swift 3 timeframe, since we know that migration will already be needed then.

-Chris


(TJ Usiyan) #2

+1

I like the feature quite a bit but avoid it as a result of the naming
concerns. If removing this feature can help improve the type checker, the
trade is worthwhile, IMO.

···

On Wed, Jan 27, 2016 at 1:23 AM, Chris Lattner via swift-evolution < swift-evolution@swift.org> wrote:

For discussion: comments appreciated!

Remove implicit tuple splat behavior from function applications

   - Proposal: SE-
   <https://github.com/apple/swift-evolution/blob/master/proposals/NNNN-name.md>
   TBD
   - Author(s): Chris Lattner
   - Status: *Awaiting review*
   - Review manager: TBD

<https://github.com/apple/swift-evolution#introduction>Introduction

Function calls (which include several syntactic forms that apply an
argument list to something of function type) currently have a dual nature
in Swift. Given something like:

func foo(a : Int, b : Int) {}

You can call it either with with the typical syntactic form that passes
arguments to each of its parameters:

foo(42, b : 17)

or you can take advantage of a little-known feature to pass an entire
argument list as a single value (of tuple type):
let x = (1, b: 2)
foo(x)

This proposal recommends removing the later form, which I affectionately
refer to as the “tuple splat” form. This feature is purely a sugar
feature, it does not provide any expressive ability beyond passing the
parameters manually.

Swift-evolution thread: TBD
<https://github.com/apple/swift-evolution#motivation>Motivation

This behavior is cute, precedented in other functional languages, and has
some advantages, but it also has several major disadvantages, which are all
related to its syntactic form.
* A call to foo(x) looks like a call to an overloaded version of foo, both
to the compiler and to the human who maintains the code. This is extremely
confusing if you don’t know the feature exists.
* There are real ambiguities in the syntax, e.g. involving Any arguments
and situations where you want to pass a tuple value as a single parameter.
* The current implementation has a ton of implementation bugs - it doesn’t
work reliably.
* The current implementation adds complexity to the type checker, slowing
it down and adding maintenance burden.
* The current implementation doesn’t work the way we would want a tuple
splat operation to work. For example, arguably, you should be able to call
foo with:

func bar() -> (Int, Int) { … }
foo(bar())

… but this is not allowed, since tuple labels are required to line up.
You have to write:

func bar() -> (Int, b: Int) { … }
foo(bar())

This makes this feature very difficult to use in practice, because you
have to _’ize a lot of parameters (violating naming conventions), perform
manual shuffling (defeating the sugar benefits of the feature), or add
parameter labels to the result of functions (which leads to odd tying
between callers and callees).

The root problem here is that we use exactly the same syntax for both
forms of function application. If the two forms were differentiated (an
option considered in “alternatives considered” below) then some of these
problems would be defined away.

From a historical perspective, the tuple splat form of function
application dates back to very early Swift design (probably introduced in
2010, but possibly 2011) where all function application was of a single
value to a function type. For a large number of reasons (including default
arguments, variadic arguments, labels, etc) we have completely abandoned
this model, but we never came back to reevaluating the tuple splat behavior.

If we didn’t already have this feature, we would not add it to Swift 3 (at
least in its current form).
<https://github.com/apple/swift-evolution#proposed-solution>Proposed
solution

The proposed solution is simple, we should just remove this feature from
the Swift 3 compiler. Ideally we would deprecate it in the Swift 2.2
compiler and remove it in Swift 3. However, if there isn’t time to get the
deprecation into Swift 2.2, the author believes it would be perfectly fine
to just remove it in Swift 3 (with a fixit + migration help of course).

One of the interesting aspect of this feature is that some of the people
we’ve spoken to are very fond of it. However, when pressed, they admit
that they are not actually using it widely in their code, or if they are
using it, they are abusing naming conventions (distorting their code) in
order to use it. This doesn’t seem like a positive contribution - this
seems like a “clever” feature, not a practical one.
<https://github.com/apple/swift-evolution#detailed-design>Detailed design

The design is straight-forward. In the Swift 3 time frame, we continue to
parse and type check these expressions as we have so far, but produce an
error + fixit hint when it is the tuple splat form. The migrator would
auto-apply the fixit hint as it does for other cases.
<https://github.com/apple/swift-evolution#impact-on-existing-code>Impact
on existing code

Any code that uses this feature will have to move to the traditional
form. In the case of the example above, this means rewriting the code from:
foo(x)

into a form like this:
foo(x.0, x.b)

In the case where “x” is a complex expression, a temporary variable will
need to be introduced. We believe that compiler fixits can handle the
simple cases directly and that this extension is not widely used.

<https://github.com/apple/swift-evolution#alternatives-considered>Alternatives
considered
The major problem with this feature is that it was not well considered and
implemented properly (owing to its very old age, it has just been kept
limping along). The alternative then is to actually design a proper
feature to support this. Since the implicitness and syntactic ambiguity
with normal function application is the problem, the solution is to
introduce an explicit syntactic form to represent this. For example,
something like this could address the problems we have:

foo(*x) // NOT a serious syntax proposal

However, actually designing this feature would be a non-trivial effort not
core to the Swift 3 mission:

* It is a pure-sugar feature, and therefore low priority.
* We don’t have an obvious sigil to use. “prefix-star” should be kept as
unused for now in case we want to use it to refer to memory-related
operations in the future.
* Making the tuple splat operation great requires more than just fixing
the syntactic ambiguities we have, it would require re-evaluating the
semantics of the operation (e.g. in light of parameter labels, varargs and
other features).

If there is serious interest in pursuing this as a concept, we should do
it as a follow-on proposal to this one. If a good design emerges, we can
evaluate that design based on its merits.

The final alternative is that we could leave the feature in the compiler.
However, that means living with its complexity “forever” or breaking code
in the Swift 4 timeframe. It would be preferable to tackle this breakage
in the Swift 3 timeframe, since we know that migration will already be
needed then.

-Chris

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


(Charles Srstka) #3

+1. I like consistency and predictability, and this would improve both of them.

Charles

···

On Jan 27, 2016, at 12:23 AM, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

For discussion: comments appreciated!

Remove implicit tuple splat behavior from function applications

Proposal: SE- <https://github.com/apple/swift-evolution/blob/master/proposals/NNNN-name.md>TBD
Author(s): Chris Lattner
Status: Awaiting review
Review manager: TBD
<https://github.com/apple/swift-evolution#introduction>Introduction

Function calls (which include several syntactic forms that apply an argument list to something of function type) currently have a dual nature in Swift. Given something like:

  func foo(a : Int, b : Int) {}

You can call it either with with the typical syntactic form that passes arguments to each of its parameters:

  foo(42, b : 17)

or you can take advantage of a little-known feature to pass an entire argument list as a single value (of tuple type):

  let x = (1, b: 2)
  foo(x)

This proposal recommends removing the later form, which I affectionately refer to as the “tuple splat” form. This feature is purely a sugar feature, it does not provide any expressive ability beyond passing the parameters manually.

Swift-evolution thread: TBD

<https://github.com/apple/swift-evolution#motivation>Motivation

This behavior is cute, precedented in other functional languages, and has some advantages, but it also has several major disadvantages, which are all related to its syntactic form.

* A call to foo(x) looks like a call to an overloaded version of foo, both to the compiler and to the human who maintains the code. This is extremely confusing if you don’t know the feature exists.
* There are real ambiguities in the syntax, e.g. involving Any arguments and situations where you want to pass a tuple value as a single parameter.
* The current implementation has a ton of implementation bugs - it doesn’t work reliably.
* The current implementation adds complexity to the type checker, slowing it down and adding maintenance burden.
* The current implementation doesn’t work the way we would want a tuple splat operation to work. For example, arguably, you should be able to call foo with:

  func bar() -> (Int, Int) { … }
  foo(bar())

… but this is not allowed, since tuple labels are required to line up. You have to write:

  func bar() -> (Int, b: Int) { … }
  foo(bar())

This makes this feature very difficult to use in practice, because you have to _’ize a lot of parameters (violating naming conventions), perform manual shuffling (defeating the sugar benefits of the feature), or add parameter labels to the result of functions (which leads to odd tying between callers and callees).

The root problem here is that we use exactly the same syntax for both forms of function application. If the two forms were differentiated (an option considered in “alternatives considered” below) then some of these problems would be defined away.

From a historical perspective, the tuple splat form of function application dates back to very early Swift design (probably introduced in 2010, but possibly 2011) where all function application was of a single value to a function type. For a large number of reasons (including default arguments, variadic arguments, labels, etc) we have completely abandoned this model, but we never came back to reevaluating the tuple splat behavior.

If we didn’t already have this feature, we would not add it to Swift 3 (at least in its current form).

<https://github.com/apple/swift-evolution#proposed-solution>Proposed solution

The proposed solution is simple, we should just remove this feature from the Swift 3 compiler. Ideally we would deprecate it in the Swift 2.2 compiler and remove it in Swift 3. However, if there isn’t time to get the deprecation into Swift 2.2, the author believes it would be perfectly fine to just remove it in Swift 3 (with a fixit + migration help of course).

One of the interesting aspect of this feature is that some of the people we’ve spoken to are very fond of it. However, when pressed, they admit that they are not actually using it widely in their code, or if they are using it, they are abusing naming conventions (distorting their code) in order to use it. This doesn’t seem like a positive contribution - this seems like a “clever” feature, not a practical one.

<https://github.com/apple/swift-evolution#detailed-design>Detailed design

The design is straight-forward. In the Swift 3 time frame, we continue to parse and type check these expressions as we have so far, but produce an error + fixit hint when it is the tuple splat form. The migrator would auto-apply the fixit hint as it does for other cases.

<https://github.com/apple/swift-evolution#impact-on-existing-code>Impact on existing code

Any code that uses this feature will have to move to the traditional form. In the case of the example above, this means rewriting the code from:

  foo(x)

into a form like this:

  foo(x.0, x.b)

In the case where “x” is a complex expression, a temporary variable will need to be introduced. We believe that compiler fixits can handle the simple cases directly and that this extension is not widely used.

<https://github.com/apple/swift-evolution#alternatives-considered>Alternatives considered

The major problem with this feature is that it was not well considered and implemented properly (owing to its very old age, it has just been kept limping along). The alternative then is to actually design a proper feature to support this. Since the implicitness and syntactic ambiguity with normal function application is the problem, the solution is to introduce an explicit syntactic form to represent this. For example, something like this could address the problems we have:

  foo(*x) // NOT a serious syntax proposal

However, actually designing this feature would be a non-trivial effort not core to the Swift 3 mission:

* It is a pure-sugar feature, and therefore low priority.
* We don’t have an obvious sigil to use. “prefix-star” should be kept as unused for now in case we want to use it to refer to memory-related operations in the future.
* Making the tuple splat operation great requires more than just fixing the syntactic ambiguities we have, it would require re-evaluating the semantics of the operation (e.g. in light of parameter labels, varargs and other features).

If there is serious interest in pursuing this as a concept, we should do it as a follow-on proposal to this one. If a good design emerges, we can evaluate that design based on its merits.

The final alternative is that we could leave the feature in the compiler. However, that means living with its complexity “forever” or breaking code in the Swift 4 timeframe. It would be preferable to tackle this breakage in the Swift 3 timeframe, since we know that migration will already be needed then.

-Chris

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


(David Waite) #4

I used this just last week! But only as an illustration

+1

One comment though, a splat operator need not be purely syntactic sugar - it could be the way arrays are applied to variadic functions.

-DW


(David Owens II) #5

+1. I'm pretty sure this feature is the source of some of the more annoying bugs I've run into, especially with generics and mislabeled parameters. In any regards, this should not be implicit.

-David

···

On Jan 26, 2016, at 10:23 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

For discussion: comments appreciated!

Remove implicit tuple splat behavior from function applications

Proposal: SE- <https://github.com/apple/swift-evolution/blob/master/proposals/NNNN-name.md>TBD
Author(s): Chris Lattner
Status: Awaiting review
Review manager: TBD
<https://github.com/apple/swift-evolution#introduction>Introduction

Function calls (which include several syntactic forms that apply an argument list to something of function type) currently have a dual nature in Swift. Given something like:

  func foo(a : Int, b : Int) {}

You can call it either with with the typical syntactic form that passes arguments to each of its parameters:

  foo(42, b : 17)

or you can take advantage of a little-known feature to pass an entire argument list as a single value (of tuple type):

  let x = (1, b: 2)
  foo(x)

This proposal recommends removing the later form, which I affectionately refer to as the “tuple splat” form. This feature is purely a sugar feature, it does not provide any expressive ability beyond passing the parameters manually.

Swift-evolution thread: TBD

<https://github.com/apple/swift-evolution#motivation>Motivation

This behavior is cute, precedented in other functional languages, and has some advantages, but it also has several major disadvantages, which are all related to its syntactic form.

* A call to foo(x) looks like a call to an overloaded version of foo, both to the compiler and to the human who maintains the code. This is extremely confusing if you don’t know the feature exists.
* There are real ambiguities in the syntax, e.g. involving Any arguments and situations where you want to pass a tuple value as a single parameter.
* The current implementation has a ton of implementation bugs - it doesn’t work reliably.
* The current implementation adds complexity to the type checker, slowing it down and adding maintenance burden.
* The current implementation doesn’t work the way we would want a tuple splat operation to work. For example, arguably, you should be able to call foo with:

  func bar() -> (Int, Int) { … }
  foo(bar())

… but this is not allowed, since tuple labels are required to line up. You have to write:

  func bar() -> (Int, b: Int) { … }
  foo(bar())

This makes this feature very difficult to use in practice, because you have to _’ize a lot of parameters (violating naming conventions), perform manual shuffling (defeating the sugar benefits of the feature), or add parameter labels to the result of functions (which leads to odd tying between callers and callees).

The root problem here is that we use exactly the same syntax for both forms of function application. If the two forms were differentiated (an option considered in “alternatives considered” below) then some of these problems would be defined away.

From a historical perspective, the tuple splat form of function application dates back to very early Swift design (probably introduced in 2010, but possibly 2011) where all function application was of a single value to a function type. For a large number of reasons (including default arguments, variadic arguments, labels, etc) we have completely abandoned this model, but we never came back to reevaluating the tuple splat behavior.

If we didn’t already have this feature, we would not add it to Swift 3 (at least in its current form).

<https://github.com/apple/swift-evolution#proposed-solution>Proposed solution

The proposed solution is simple, we should just remove this feature from the Swift 3 compiler. Ideally we would deprecate it in the Swift 2.2 compiler and remove it in Swift 3. However, if there isn’t time to get the deprecation into Swift 2.2, the author believes it would be perfectly fine to just remove it in Swift 3 (with a fixit + migration help of course).

One of the interesting aspect of this feature is that some of the people we’ve spoken to are very fond of it. However, when pressed, they admit that they are not actually using it widely in their code, or if they are using it, they are abusing naming conventions (distorting their code) in order to use it. This doesn’t seem like a positive contribution - this seems like a “clever” feature, not a practical one.

<https://github.com/apple/swift-evolution#detailed-design>Detailed design

The design is straight-forward. In the Swift 3 time frame, we continue to parse and type check these expressions as we have so far, but produce an error + fixit hint when it is the tuple splat form. The migrator would auto-apply the fixit hint as it does for other cases.

<https://github.com/apple/swift-evolution#impact-on-existing-code>Impact on existing code

Any code that uses this feature will have to move to the traditional form. In the case of the example above, this means rewriting the code from:

  foo(x)

into a form like this:

  foo(x.0, x.b)

In the case where “x” is a complex expression, a temporary variable will need to be introduced. We believe that compiler fixits can handle the simple cases directly and that this extension is not widely used.

<https://github.com/apple/swift-evolution#alternatives-considered>Alternatives considered

The major problem with this feature is that it was not well considered and implemented properly (owing to its very old age, it has just been kept limping along). The alternative then is to actually design a proper feature to support this. Since the implicitness and syntactic ambiguity with normal function application is the problem, the solution is to introduce an explicit syntactic form to represent this. For example, something like this could address the problems we have:

  foo(*x) // NOT a serious syntax proposal

However, actually designing this feature would be a non-trivial effort not core to the Swift 3 mission:

* It is a pure-sugar feature, and therefore low priority.
* We don’t have an obvious sigil to use. “prefix-star” should be kept as unused for now in case we want to use it to refer to memory-related operations in the future.
* Making the tuple splat operation great requires more than just fixing the syntactic ambiguities we have, it would require re-evaluating the semantics of the operation (e.g. in light of parameter labels, varargs and other features).

If there is serious interest in pursuing this as a concept, we should do it as a follow-on proposal to this one. If a good design emerges, we can evaluate that design based on its merits.

The final alternative is that we could leave the feature in the compiler. However, that means living with its complexity “forever” or breaking code in the Swift 4 timeframe. It would be preferable to tackle this breakage in the Swift 3 timeframe, since we know that migration will already be needed then.

-Chris

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


(Paul Cantrell) #6

I do think there’s eventual value in this (modulo syntax refinement):

  foo(*x) // NOT a serious syntax proposal

I have hit just a few proxy-flavored situations where argument forwarding was useful. However, in those situations, it’s very little manual effort to just rebuild the tuple: f(tuple.a, b: tuple.b). I thus don’t consider that feature urgent, and would happily wait until Swift 4+ for it. I’d just like to keep a toe in the door for it.

With that minor caveat, I support this proposal. I’ve been bitten by the Any-related ambiguities Chris mentions — far more often than I’ve actually had use for this feature.

I’ll also reiterate my wish to remove the overloaded meaning of $0 as the unsplatted args of a closure. It’s equally esoteric and super confusing. Hmm, I should bump that thread….

Cheers, P

···

On Jan 27, 2016, at 12:23 AM, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

For discussion: comments appreciated!

Remove implicit tuple splat behavior from function applications

Proposal: SE- <https://github.com/apple/swift-evolution/blob/master/proposals/NNNN-name.md>TBD
Author(s): Chris Lattner
Status: Awaiting review
Review manager: TBD
<https://github.com/apple/swift-evolution#introduction>Introduction

Function calls (which include several syntactic forms that apply an argument list to something of function type) currently have a dual nature in Swift. Given something like:

  func foo(a : Int, b : Int) {}

You can call it either with with the typical syntactic form that passes arguments to each of its parameters:

  foo(42, b : 17)

or you can take advantage of a little-known feature to pass an entire argument list as a single value (of tuple type):

  let x = (1, b: 2)
  foo(x)

This proposal recommends removing the later form, which I affectionately refer to as the “tuple splat” form. This feature is purely a sugar feature, it does not provide any expressive ability beyond passing the parameters manually.

Swift-evolution thread: TBD

<https://github.com/apple/swift-evolution#motivation>Motivation

This behavior is cute, precedented in other functional languages, and has some advantages, but it also has several major disadvantages, which are all related to its syntactic form.

* A call to foo(x) looks like a call to an overloaded version of foo, both to the compiler and to the human who maintains the code. This is extremely confusing if you don’t know the feature exists.
* There are real ambiguities in the syntax, e.g. involving Any arguments and situations where you want to pass a tuple value as a single parameter.
* The current implementation has a ton of implementation bugs - it doesn’t work reliably.
* The current implementation adds complexity to the type checker, slowing it down and adding maintenance burden.
* The current implementation doesn’t work the way we would want a tuple splat operation to work. For example, arguably, you should be able to call foo with:

  func bar() -> (Int, Int) { … }
  foo(bar())

… but this is not allowed, since tuple labels are required to line up. You have to write:

  func bar() -> (Int, b: Int) { … }
  foo(bar())

This makes this feature very difficult to use in practice, because you have to _’ize a lot of parameters (violating naming conventions), perform manual shuffling (defeating the sugar benefits of the feature), or add parameter labels to the result of functions (which leads to odd tying between callers and callees).

The root problem here is that we use exactly the same syntax for both forms of function application. If the two forms were differentiated (an option considered in “alternatives considered” below) then some of these problems would be defined away.

From a historical perspective, the tuple splat form of function application dates back to very early Swift design (probably introduced in 2010, but possibly 2011) where all function application was of a single value to a function type. For a large number of reasons (including default arguments, variadic arguments, labels, etc) we have completely abandoned this model, but we never came back to reevaluating the tuple splat behavior.

If we didn’t already have this feature, we would not add it to Swift 3 (at least in its current form).

<https://github.com/apple/swift-evolution#proposed-solution>Proposed solution

The proposed solution is simple, we should just remove this feature from the Swift 3 compiler. Ideally we would deprecate it in the Swift 2.2 compiler and remove it in Swift 3. However, if there isn’t time to get the deprecation into Swift 2.2, the author believes it would be perfectly fine to just remove it in Swift 3 (with a fixit + migration help of course).

One of the interesting aspect of this feature is that some of the people we’ve spoken to are very fond of it. However, when pressed, they admit that they are not actually using it widely in their code, or if they are using it, they are abusing naming conventions (distorting their code) in order to use it. This doesn’t seem like a positive contribution - this seems like a “clever” feature, not a practical one.

<https://github.com/apple/swift-evolution#detailed-design>Detailed design

The design is straight-forward. In the Swift 3 time frame, we continue to parse and type check these expressions as we have so far, but produce an error + fixit hint when it is the tuple splat form. The migrator would auto-apply the fixit hint as it does for other cases.

<https://github.com/apple/swift-evolution#impact-on-existing-code>Impact on existing code

Any code that uses this feature will have to move to the traditional form. In the case of the example above, this means rewriting the code from:

  foo(x)

into a form like this:

  foo(x.0, x.b)

In the case where “x” is a complex expression, a temporary variable will need to be introduced. We believe that compiler fixits can handle the simple cases directly and that this extension is not widely used.

<https://github.com/apple/swift-evolution#alternatives-considered>Alternatives considered

The major problem with this feature is that it was not well considered and implemented properly (owing to its very old age, it has just been kept limping along). The alternative then is to actually design a proper feature to support this. Since the implicitness and syntactic ambiguity with normal function application is the problem, the solution is to introduce an explicit syntactic form to represent this. For example, something like this could address the problems we have:

  foo(*x) // NOT a serious syntax proposal

However, actually designing this feature would be a non-trivial effort not core to the Swift 3 mission:

* It is a pure-sugar feature, and therefore low priority.
* We don’t have an obvious sigil to use. “prefix-star” should be kept as unused for now in case we want to use it to refer to memory-related operations in the future.
* Making the tuple splat operation great requires more than just fixing the syntactic ambiguities we have, it would require re-evaluating the semantics of the operation (e.g. in light of parameter labels, varargs and other features).

If there is serious interest in pursuing this as a concept, we should do it as a follow-on proposal to this one. If a good design emerges, we can evaluate that design based on its merits.

The final alternative is that we could leave the feature in the compiler. However, that means living with its complexity “forever” or breaking code in the Swift 4 timeframe. It would be preferable to tackle this breakage in the Swift 3 timeframe, since we know that migration will already be needed then.

-Chris

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


(Howard Lovatt) #7

+1. Never use it.

-- Howard.

···

On 27 Jan 2016, at 5:23 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

For discussion: comments appreciated!

Remove implicit tuple splat behavior from function applications
Proposal: SE-TBD
Author(s): Chris Lattner
Status: Awaiting review
Review manager: TBD
Introduction

Function calls (which include several syntactic forms that apply an argument list to something of function type) currently have a dual nature in Swift. Given something like:

  func foo(a : Int, b : Int) {}

You can call it either with with the typical syntactic form that passes arguments to each of its parameters:

  foo(42, b : 17)

or you can take advantage of a little-known feature to pass an entire argument list as a single value (of tuple type):

  let x = (1, b: 2)
  foo(x)

This proposal recommends removing the later form, which I affectionately refer to as the “tuple splat” form. This feature is purely a sugar feature, it does not provide any expressive ability beyond passing the parameters manually.

Swift-evolution thread: TBD

Motivation

This behavior is cute, precedented in other functional languages, and has some advantages, but it also has several major disadvantages, which are all related to its syntactic form.

* A call to foo(x) looks like a call to an overloaded version of foo, both to the compiler and to the human who maintains the code. This is extremely confusing if you don’t know the feature exists.
* There are real ambiguities in the syntax, e.g. involving Any arguments and situations where you want to pass a tuple value as a single parameter.
* The current implementation has a ton of implementation bugs - it doesn’t work reliably.
* The current implementation adds complexity to the type checker, slowing it down and adding maintenance burden.
* The current implementation doesn’t work the way we would want a tuple splat operation to work. For example, arguably, you should be able to call foo with:

  func bar() -> (Int, Int) { … }
  foo(bar())

… but this is not allowed, since tuple labels are required to line up. You have to write:

  func bar() -> (Int, b: Int) { … }
  foo(bar())

This makes this feature very difficult to use in practice, because you have to _’ize a lot of parameters (violating naming conventions), perform manual shuffling (defeating the sugar benefits of the feature), or add parameter labels to the result of functions (which leads to odd tying between callers and callees).

The root problem here is that we use exactly the same syntax for both forms of function application. If the two forms were differentiated (an option considered in “alternatives considered” below) then some of these problems would be defined away.

From a historical perspective, the tuple splat form of function application dates back to very early Swift design (probably introduced in 2010, but possibly 2011) where all function application was of a single value to a function type. For a large number of reasons (including default arguments, variadic arguments, labels, etc) we have completely abandoned this model, but we never came back to reevaluating the tuple splat behavior.

If we didn’t already have this feature, we would not add it to Swift 3 (at least in its current form).

Proposed solution

The proposed solution is simple, we should just remove this feature from the Swift 3 compiler. Ideally we would deprecate it in the Swift 2.2 compiler and remove it in Swift 3. However, if there isn’t time to get the deprecation into Swift 2.2, the author believes it would be perfectly fine to just remove it in Swift 3 (with a fixit + migration help of course).

One of the interesting aspect of this feature is that some of the people we’ve spoken to are very fond of it. However, when pressed, they admit that they are not actually using it widely in their code, or if they are using it, they are abusing naming conventions (distorting their code) in order to use it. This doesn’t seem like a positive contribution - this seems like a “clever” feature, not a practical one.

Detailed design

The design is straight-forward. In the Swift 3 time frame, we continue to parse and type check these expressions as we have so far, but produce an error + fixit hint when it is the tuple splat form. The migrator would auto-apply the fixit hint as it does for other cases.

Impact on existing code

Any code that uses this feature will have to move to the traditional form. In the case of the example above, this means rewriting the code from:

  foo(x)

into a form like this:

  foo(x.0, x.b)

In the case where “x” is a complex expression, a temporary variable will need to be introduced. We believe that compiler fixits can handle the simple cases directly and that this extension is not widely used.

Alternatives considered

The major problem with this feature is that it was not well considered and implemented properly (owing to its very old age, it has just been kept limping along). The alternative then is to actually design a proper feature to support this. Since the implicitness and syntactic ambiguity with normal function application is the problem, the solution is to introduce an explicit syntactic form to represent this. For example, something like this could address the problems we have:

  foo(*x) // NOT a serious syntax proposal

However, actually designing this feature would be a non-trivial effort not core to the Swift 3 mission:

* It is a pure-sugar feature, and therefore low priority.
* We don’t have an obvious sigil to use. “prefix-star” should be kept as unused for now in case we want to use it to refer to memory-related operations in the future.
* Making the tuple splat operation great requires more than just fixing the syntactic ambiguities we have, it would require re-evaluating the semantics of the operation (e.g. in light of parameter labels, varargs and other features).

If there is serious interest in pursuing this as a concept, we should do it as a follow-on proposal to this one. If a good design emerges, we can evaluate that design based on its merits.

The final alternative is that we could leave the feature in the compiler. However, that means living with its complexity “forever” or breaking code in the Swift 4 timeframe. It would be preferable to tackle this breakage in the Swift 3 timeframe, since we know that migration will already be needed then.

-Chris

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


(Brent Royal-Gordon) #8

Remove implicit tuple splat behavior from function applications

Please do not remove this without providing an alternative.

I use this pretty frequently, usually in combination with higher-order functions like `map` and `filter` when I'm linking together steps in a pipeline. Here's one example where I'm converting structs into CloudKit objects. I have to perform a series of steps: extract the type and value from the instance (and possibly from its child instances), retrieve or allocate CKRecords for each one, and then write the data into the CKRecords. The top-level flow for this is simply:

    func addInstance(instance: CloudRepresentable, recursively: Bool) throws {
        try typeNameAndValueForInstance(instance, recursively: recursively).lazy.map(recordAllocatorWithVersionMemory(versionMemory)).forEach(writeAttributesAndReferencesToRecord)
    }

Each step in this process is implemented with a private method whose signature matches the methods before and after. This allows me to name each step and encapsulate the details of how they're done. Yes, the signatures are slightly contorted, but they're private methods—implementation details—so it doesn't matter.

    private func typeNameAndValueForInstance(instance: CloudRepresentable, recursively: Bool) -> [(typeName: String, value: CloudValue)]
    private func recordAllocatorWithVersionMemory(versionMemory: CloudDatabaseVersionMemoryType?)(typeName: String, _ cloudValue: CloudValue) throws -> (CKRecord, CloudValue)
    private func writeAttributesAndReferencesToRecord(record: CKRecord, cloudValue: CloudValue)

I agree that the implicit splat causes problems sometimes, but I don't think it should be removed without an alternative. For my uses, simply being able to say `myFunc.apply` for any function (including the ones provided by SomeType.init) would be sufficient. This method could perhaps be overloaded to support all possible combinations of default argument omissions.

···

--
Brent Royal-Gordon
Architechies


(Nikita Leonov) #9

+1. Based on experience of splatting use in other languages it has limited applications. I assume feature not well known by Swift community just because of small demand. For those who needs splatting, it can be replicated by existing tooling manually or generalized by something similar to following code:
func splat<T, U, R>(f: (T, U) -> R, _ tuple: (T, U)) -> R {
    return f(tuple.0, tuple.1)
}

Nikita

···

On Jan 26, 2016, at 10:23 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

For discussion: comments appreciated!

Remove implicit tuple splat behavior from function applications

Proposal: SE- <https://github.com/apple/swift-evolution/blob/master/proposals/NNNN-name.md>TBD
Author(s): Chris Lattner
Status: Awaiting review
Review manager: TBD
<https://github.com/apple/swift-evolution#introduction>Introduction

Function calls (which include several syntactic forms that apply an argument list to something of function type) currently have a dual nature in Swift. Given something like:

  func foo(a : Int, b : Int) {}

You can call it either with with the typical syntactic form that passes arguments to each of its parameters:

  foo(42, b : 17)

or you can take advantage of a little-known feature to pass an entire argument list as a single value (of tuple type):

  let x = (1, b: 2)
  foo(x)

This proposal recommends removing the later form, which I affectionately refer to as the “tuple splat” form. This feature is purely a sugar feature, it does not provide any expressive ability beyond passing the parameters manually.

Swift-evolution thread: TBD

<https://github.com/apple/swift-evolution#motivation>Motivation

This behavior is cute, precedented in other functional languages, and has some advantages, but it also has several major disadvantages, which are all related to its syntactic form.

* A call to foo(x) looks like a call to an overloaded version of foo, both to the compiler and to the human who maintains the code. This is extremely confusing if you don’t know the feature exists.
* There are real ambiguities in the syntax, e.g. involving Any arguments and situations where you want to pass a tuple value as a single parameter.
* The current implementation has a ton of implementation bugs - it doesn’t work reliably.
* The current implementation adds complexity to the type checker, slowing it down and adding maintenance burden.
* The current implementation doesn’t work the way we would want a tuple splat operation to work. For example, arguably, you should be able to call foo with:

  func bar() -> (Int, Int) { … }
  foo(bar())

… but this is not allowed, since tuple labels are required to line up. You have to write:

  func bar() -> (Int, b: Int) { … }
  foo(bar())

This makes this feature very difficult to use in practice, because you have to _’ize a lot of parameters (violating naming conventions), perform manual shuffling (defeating the sugar benefits of the feature), or add parameter labels to the result of functions (which leads to odd tying between callers and callees).

The root problem here is that we use exactly the same syntax for both forms of function application. If the two forms were differentiated (an option considered in “alternatives considered” below) then some of these problems would be defined away.

From a historical perspective, the tuple splat form of function application dates back to very early Swift design (probably introduced in 2010, but possibly 2011) where all function application was of a single value to a function type. For a large number of reasons (including default arguments, variadic arguments, labels, etc) we have completely abandoned this model, but we never came back to reevaluating the tuple splat behavior.

If we didn’t already have this feature, we would not add it to Swift 3 (at least in its current form).

<https://github.com/apple/swift-evolution#proposed-solution>Proposed solution

The proposed solution is simple, we should just remove this feature from the Swift 3 compiler. Ideally we would deprecate it in the Swift 2.2 compiler and remove it in Swift 3. However, if there isn’t time to get the deprecation into Swift 2.2, the author believes it would be perfectly fine to just remove it in Swift 3 (with a fixit + migration help of course).

One of the interesting aspect of this feature is that some of the people we’ve spoken to are very fond of it. However, when pressed, they admit that they are not actually using it widely in their code, or if they are using it, they are abusing naming conventions (distorting their code) in order to use it. This doesn’t seem like a positive contribution - this seems like a “clever” feature, not a practical one.

<https://github.com/apple/swift-evolution#detailed-design>Detailed design

The design is straight-forward. In the Swift 3 time frame, we continue to parse and type check these expressions as we have so far, but produce an error + fixit hint when it is the tuple splat form. The migrator would auto-apply the fixit hint as it does for other cases.

<https://github.com/apple/swift-evolution#impact-on-existing-code>Impact on existing code

Any code that uses this feature will have to move to the traditional form. In the case of the example above, this means rewriting the code from:

  foo(x)

into a form like this:

  foo(x.0, x.b)

In the case where “x” is a complex expression, a temporary variable will need to be introduced. We believe that compiler fixits can handle the simple cases directly and that this extension is not widely used.

<https://github.com/apple/swift-evolution#alternatives-considered>Alternatives considered

The major problem with this feature is that it was not well considered and implemented properly (owing to its very old age, it has just been kept limping along). The alternative then is to actually design a proper feature to support this. Since the implicitness and syntactic ambiguity with normal function application is the problem, the solution is to introduce an explicit syntactic form to represent this. For example, something like this could address the problems we have:

  foo(*x) // NOT a serious syntax proposal

However, actually designing this feature would be a non-trivial effort not core to the Swift 3 mission:

* It is a pure-sugar feature, and therefore low priority.
* We don’t have an obvious sigil to use. “prefix-star” should be kept as unused for now in case we want to use it to refer to memory-related operations in the future.
* Making the tuple splat operation great requires more than just fixing the syntactic ambiguities we have, it would require re-evaluating the semantics of the operation (e.g. in light of parameter labels, varargs and other features).

If there is serious interest in pursuing this as a concept, we should do it as a follow-on proposal to this one. If a good design emerges, we can evaluate that design based on its merits.

The final alternative is that we could leave the feature in the compiler. However, that means living with its complexity “forever” or breaking code in the Swift 4 timeframe. It would be preferable to tackle this breakage in the Swift 3 timeframe, since we know that migration will already be needed then.

-Chris

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


(Janosch Hildebrand) #10

+1 to the existing proposal.

On a related note, would this also affect the implicit "argument-list to tuple" conversion?
To give an example:

var array: [(Int, Int)] = []
var int_var = 1
let int_let = 1

// These obviously work
array.append((int_let, 1))
array.append((1, 1))
array.append((int_var, 1))
array.append((int_var, int_var))

// The following have worked to varying degrees over Swift's (public) lifetime
// Currently only the first example compiles
array.append(int_let, int_let)
array.append(int_let, 1)
array.append(1, 1)
array.append(int_var, 1)
array.append(int_var, int_var)

This is in a way the reverse of splatting but IMO equally troublesome at least in it's current form.
And it also presumes some form of equivalence between function argument lists and tuples.

So my question is does the proposal already cover removing this behaviour (in which case +1)?
Or else, could this be added to the proposal or would this need a separate proposal entirely?

- Janosch


(Jacob Bandes-Storch) #11

+1 from me as well. I don't think I've ever used this feature on purpose.

I look forward to future discussions about splatting, especially as they
relate to variadic functions.

···

On Tue, Jan 26, 2016 at 10:36 PM, T.J. Usiyan via swift-evolution < swift-evolution@swift.org> wrote:

+1

I like the feature quite a bit but avoid it as a result of the naming
concerns. If removing this feature can help improve the type checker, the
trade is worthwhile, IMO.

On Wed, Jan 27, 2016 at 1:23 AM, Chris Lattner via swift-evolution < > swift-evolution@swift.org> wrote:

For discussion: comments appreciated!

Remove implicit tuple splat behavior from function applications

   - Proposal: SE-
   <https://github.com/apple/swift-evolution/blob/master/proposals/NNNN-name.md>
   TBD
   - Author(s): Chris Lattner
   - Status: *Awaiting review*
   - Review manager: TBD

<https://github.com/apple/swift-evolution#introduction>Introduction

Function calls (which include several syntactic forms that apply an
argument list to something of function type) currently have a dual nature
in Swift. Given something like:

func foo(a : Int, b : Int) {}

You can call it either with with the typical syntactic form that passes
arguments to each of its parameters:

foo(42, b : 17)

or you can take advantage of a little-known feature to pass an entire
argument list as a single value (of tuple type):
let x = (1, b: 2)
foo(x)

This proposal recommends removing the later form, which I affectionately
refer to as the “tuple splat” form. This feature is purely a sugar
feature, it does not provide any expressive ability beyond passing the
parameters manually.

Swift-evolution thread: TBD
<https://github.com/apple/swift-evolution#motivation>Motivation

This behavior is cute, precedented in other functional languages, and has
some advantages, but it also has several major disadvantages, which are all
related to its syntactic form.
* A call to foo(x) looks like a call to an overloaded version of foo,
both to the compiler and to the human who maintains the code. This is
extremely confusing if you don’t know the feature exists.
* There are real ambiguities in the syntax, e.g. involving Any arguments
and situations where you want to pass a tuple value as a single parameter.
* The current implementation has a ton of implementation bugs - it
doesn’t work reliably.
* The current implementation adds complexity to the type checker, slowing
it down and adding maintenance burden.
* The current implementation doesn’t work the way we would want a tuple
splat operation to work. For example, arguably, you should be able to call
foo with:

func bar() -> (Int, Int) { … }
foo(bar())

… but this is not allowed, since tuple labels are required to line up.
You have to write:

func bar() -> (Int, b: Int) { … }
foo(bar())

This makes this feature very difficult to use in practice, because you
have to _’ize a lot of parameters (violating naming conventions), perform
manual shuffling (defeating the sugar benefits of the feature), or add
parameter labels to the result of functions (which leads to odd tying
between callers and callees).

The root problem here is that we use exactly the same syntax for both
forms of function application. If the two forms were differentiated (an
option considered in “alternatives considered” below) then some of these
problems would be defined away.

From a historical perspective, the tuple splat form of function
application dates back to very early Swift design (probably introduced in
2010, but possibly 2011) where all function application was of a single
value to a function type. For a large number of reasons (including default
arguments, variadic arguments, labels, etc) we have completely abandoned
this model, but we never came back to reevaluating the tuple splat behavior.

If we didn’t already have this feature, we would not add it to Swift 3
(at least in its current form).
<https://github.com/apple/swift-evolution#proposed-solution>Proposed
solution

The proposed solution is simple, we should just remove this feature from
the Swift 3 compiler. Ideally we would deprecate it in the Swift 2.2
compiler and remove it in Swift 3. However, if there isn’t time to get the
deprecation into Swift 2.2, the author believes it would be perfectly fine
to just remove it in Swift 3 (with a fixit + migration help of course).

One of the interesting aspect of this feature is that some of the people
we’ve spoken to are very fond of it. However, when pressed, they admit
that they are not actually using it widely in their code, or if they are
using it, they are abusing naming conventions (distorting their code) in
order to use it. This doesn’t seem like a positive contribution - this
seems like a “clever” feature, not a practical one.
<https://github.com/apple/swift-evolution#detailed-design>Detailed design

The design is straight-forward. In the Swift 3 time frame, we continue
to parse and type check these expressions as we have so far, but produce an
error + fixit hint when it is the tuple splat form. The migrator would
auto-apply the fixit hint as it does for other cases.
<https://github.com/apple/swift-evolution#impact-on-existing-code>Impact
on existing code

Any code that uses this feature will have to move to the traditional
form. In the case of the example above, this means rewriting the code from:
foo(x)

into a form like this:
foo(x.0, x.b)

In the case where “x” is a complex expression, a temporary variable will
need to be introduced. We believe that compiler fixits can handle the
simple cases directly and that this extension is not widely used.

<https://github.com/apple/swift-evolution#alternatives-considered>Alternatives
considered
The major problem with this feature is that it was not well considered
and implemented properly (owing to its very old age, it has just been kept
limping along). The alternative then is to actually design a proper
feature to support this. Since the implicitness and syntactic ambiguity
with normal function application is the problem, the solution is to
introduce an explicit syntactic form to represent this. For example,
something like this could address the problems we have:

foo(*x) // NOT a serious syntax proposal

However, actually designing this feature would be a non-trivial effort
not core to the Swift 3 mission:

* It is a pure-sugar feature, and therefore low priority.
* We don’t have an obvious sigil to use. “prefix-star” should be kept as
unused for now in case we want to use it to refer to memory-related
operations in the future.
* Making the tuple splat operation great requires more than just fixing
the syntactic ambiguities we have, it would require re-evaluating the
semantics of the operation (e.g. in light of parameter labels, varargs and
other features).

If there is serious interest in pursuing this as a concept, we should do
it as a follow-on proposal to this one. If a good design emerges, we can
evaluate that design based on its merits.

The final alternative is that we could leave the feature in the
compiler. However, that means living with its complexity “forever” or
breaking code in the Swift 4 timeframe. It would be preferable to tackle
this breakage in the Swift 3 timeframe, since we know that migration will
already be needed then.

-Chris

_______________________________________________
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


(James Campbell) #12

I think libraries like argo use it.

In these cases it's for initlizing a struct with data (from Json).

You could argue that these libraries could implement this better but I would like to put forward a few questions:

- if we kept this we could instead add an apply function to closures which could take a turple, vardaric array or maybe even allowed you to call it by specifying the names arguments (in any order you liked)

That way you could do:

Object.foo //returns a normal closure
Object.foo.apply(turple) //compiler generated sugar function

A lot of libraries including Lenses I think would be simplified by this as they would no longer rely on currying or turple splats as heavily. It also allows you to apply the value of something easily.

Or we could define a new syntax to call labelled arguments in any order

This would work well with immutable objects as in certain cases I wish to mutate a struc like so:

Struct Person
{
Let name: String

Init(name: String)
{
self.name = name)
}
}

Let person = Person(name:"James")
person.mutate({
.name = "Bob"
}) // this special syntax says to the compiler to take this special object and to return a new copy of the original struct with the values changed.

Inside of the {} you can directly access the properties you wish to change.

I use the closure syntax but perhaps it could use another one .

This syntax could be used for apply so you can define the parameters you care about:

dependency({
name:"networking",
git:""
})

Sent froml my iPhone

···

On 27 Jan 2016, at 06:38, Jacob Bandes-Storch via swift-evolution <swift-evolution@swift.org> wrote:

+1 from me as well. I don't think I've ever used this feature on purpose.

I look forward to future discussions about splatting, especially as they relate to variadic functions.

On Tue, Jan 26, 2016 at 10:36 PM, T.J. Usiyan via swift-evolution <swift-evolution@swift.org> wrote:
+1

I like the feature quite a bit but avoid it as a result of the naming concerns. If removing this feature can help improve the type checker, the trade is worthwhile, IMO.

On Wed, Jan 27, 2016 at 1:23 AM, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:
For discussion: comments appreciated!

Remove implicit tuple splat behavior from function applications
Proposal: SE-TBD
Author(s): Chris Lattner
Status: Awaiting review
Review manager: TBD
Introduction

Function calls (which include several syntactic forms that apply an argument list to something of function type) currently have a dual nature in Swift. Given something like:

  func foo(a : Int, b : Int) {}

You can call it either with with the typical syntactic form that passes arguments to each of its parameters:

  foo(42, b : 17)

or you can take advantage of a little-known feature to pass an entire argument list as a single value (of tuple type):

  let x = (1, b: 2)
  foo(x)

This proposal recommends removing the later form, which I affectionately refer to as the “tuple splat” form. This feature is purely a sugar feature, it does not provide any expressive ability beyond passing the parameters manually.

Swift-evolution thread: TBD

Motivation

This behavior is cute, precedented in other functional languages, and has some advantages, but it also has several major disadvantages, which are all related to its syntactic form.

* A call to foo(x) looks like a call to an overloaded version of foo, both to the compiler and to the human who maintains the code. This is extremely confusing if you don’t know the feature exists.
* There are real ambiguities in the syntax, e.g. involving Any arguments and situations where you want to pass a tuple value as a single parameter.
* The current implementation has a ton of implementation bugs - it doesn’t work reliably.
* The current implementation adds complexity to the type checker, slowing it down and adding maintenance burden.
* The current implementation doesn’t work the way we would want a tuple splat operation to work. For example, arguably, you should be able to call foo with:

  func bar() -> (Int, Int) { … }
  foo(bar())

… but this is not allowed, since tuple labels are required to line up. You have to write:

  func bar() -> (Int, b: Int) { … }
  foo(bar())

This makes this feature very difficult to use in practice, because you have to _’ize a lot of parameters (violating naming conventions), perform manual shuffling (defeating the sugar benefits of the feature), or add parameter labels to the result of functions (which leads to odd tying between callers and callees).

The root problem here is that we use exactly the same syntax for both forms of function application. If the two forms were differentiated (an option considered in “alternatives considered” below) then some of these problems would be defined away.

From a historical perspective, the tuple splat form of function application dates back to very early Swift design (probably introduced in 2010, but possibly 2011) where all function application was of a single value to a function type. For a large number of reasons (including default arguments, variadic arguments, labels, etc) we have completely abandoned this model, but we never came back to reevaluating the tuple splat behavior.

If we didn’t already have this feature, we would not add it to Swift 3 (at least in its current form).

Proposed solution

The proposed solution is simple, we should just remove this feature from the Swift 3 compiler. Ideally we would deprecate it in the Swift 2.2 compiler and remove it in Swift 3. However, if there isn’t time to get the deprecation into Swift 2.2, the author believes it would be perfectly fine to just remove it in Swift 3 (with a fixit + migration help of course).

One of the interesting aspect of this feature is that some of the people we’ve spoken to are very fond of it. However, when pressed, they admit that they are not actually using it widely in their code, or if they are using it, they are abusing naming conventions (distorting their code) in order to use it. This doesn’t seem like a positive contribution - this seems like a “clever” feature, not a practical one.

Detailed design

The design is straight-forward. In the Swift 3 time frame, we continue to parse and type check these expressions as we have so far, but produce an error + fixit hint when it is the tuple splat form. The migrator would auto-apply the fixit hint as it does for other cases.

Impact on existing code

Any code that uses this feature will have to move to the traditional form. In the case of the example above, this means rewriting the code from:

  foo(x)

into a form like this:

  foo(x.0, x.b)

In the case where “x” is a complex expression, a temporary variable will need to be introduced. We believe that compiler fixits can handle the simple cases directly and that this extension is not widely used.

Alternatives considered

The major problem with this feature is that it was not well considered and implemented properly (owing to its very old age, it has just been kept limping along). The alternative then is to actually design a proper feature to support this. Since the implicitness and syntactic ambiguity with normal function application is the problem, the solution is to introduce an explicit syntactic form to represent this. For example, something like this could address the problems we have:

  foo(*x) // NOT a serious syntax proposal

However, actually designing this feature would be a non-trivial effort not core to the Swift 3 mission:

* It is a pure-sugar feature, and therefore low priority.
* We don’t have an obvious sigil to use. “prefix-star” should be kept as unused for now in case we want to use it to refer to memory-related operations in the future.
* Making the tuple splat operation great requires more than just fixing the syntactic ambiguities we have, it would require re-evaluating the semantics of the operation (e.g. in light of parameter labels, varargs and other features).

If there is serious interest in pursuing this as a concept, we should do it as a follow-on proposal to this one. If a good design emerges, we can evaluate that design based on its merits.

The final alternative is that we could leave the feature in the compiler. However, that means living with its complexity “forever” or breaking code in the Swift 4 timeframe. It would be preferable to tackle this breakage in the Swift 3 timeframe, since we know that migration will already be needed then.

-Chris

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

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

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


(Greg Titus) #13

+1.

Removing implicit tuple splat will also fix most of the confusing argument issues with anonymous closures.

You wrote a couple weeks back:

···

----
    func foo(val: Int) { }

    func bar(closure: (Int,Int) -> Void) {
        closure(0, 1)
    }

    bar { foo($0) } // compiler error
    bar { foo($1) } // just dandy
    bar { foo($0 + $1) } // also works
—-

The reason why the compiler error happens is because it is currently legal for $0 to be a tuple of type (Int,Int) and to call foo() with that tuple because of the implicit splatting. Once that is illegal, having the anonymous closure take a single (Int,Int) tuple isn’t a legal possibility, and so you will probably end up with a more understandable error about bar() being called with an argument of (_) -> Void instead of the expected (Int, Int) -> Void. (I.e. the compiler thinks that there’s just one argument to the closure still, but at least it’s narrowed down the type problem to the correct location.)

And now that the problem will be narrowed to the type signature of the anonymous closure, it becomes possible to fix it (in a separate proposal) by making type inference on anonymous closures implicitly allow additional ignored arguments.

  - Greg

On Jan 27, 2016, at 8:06 AM, Paul Cantrell via swift-evolution <swift-evolution@swift.org> wrote:

I do think there’s eventual value in this (modulo syntax refinement):

  foo(*x) // NOT a serious syntax proposal

I have hit just a few proxy-flavored situations where argument forwarding was useful. However, in those situations, it’s very little manual effort to just rebuild the tuple: f(tuple.a, b: tuple.b). I thus don’t consider that feature urgent, and would happily wait until Swift 4+ for it. I’d just like to keep a toe in the door for it.

With that minor caveat, I support this proposal. I’ve been bitten by the Any-related ambiguities Chris mentions — far more often than I’ve actually had use for this feature.

I’ll also reiterate my wish to remove the overloaded meaning of $0 as the unsplatted args of a closure. It’s equally esoteric and super confusing. Hmm, I should bump that thread….

Cheers, P

On Jan 27, 2016, at 12:23 AM, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

For discussion: comments appreciated!

Remove implicit tuple splat behavior from function applications

  • Proposal: SE-TBD
  • Author(s): Chris Lattner
  • Status: Awaiting review
  • Review manager: TBD
Introduction

Function calls (which include several syntactic forms that apply an argument list to something of function type) currently have a dual nature in Swift. Given something like:

  func foo(a : Int, b : Int) {}

You can call it either with with the typical syntactic form that passes arguments to each of its parameters:

  foo(42, b : 17)

or you can take advantage of a little-known feature to pass an entire argument list as a single value (of tuple type):

  let x = (1, b: 2)
  foo(x)

This proposal recommends removing the later form, which I affectionately refer to as the “tuple splat” form. This feature is purely a sugar feature, it does not provide any expressive ability beyond passing the parameters manually.

Swift-evolution thread: TBD

Motivation

This behavior is cute, precedented in other functional languages, and has some advantages, but it also has several major disadvantages, which are all related to its syntactic form.

* A call to foo(x) looks like a call to an overloaded version of foo, both to the compiler and to the human who maintains the code. This is extremely confusing if you don’t know the feature exists.
* There are real ambiguities in the syntax, e.g. involving Any arguments and situations where you want to pass a tuple value as a single parameter.
* The current implementation has a ton of implementation bugs - it doesn’t work reliably.
* The current implementation adds complexity to the type checker, slowing it down and adding maintenance burden.
* The current implementation doesn’t work the way we would want a tuple splat operation to work. For example, arguably, you should be able to call foo with:

  func bar() -> (Int, Int) { … }
  foo(bar())

… but this is not allowed, since tuple labels are required to line up. You have to write:

  func bar() -> (Int, b: Int) { … }
  foo(bar())

This makes this feature very difficult to use in practice, because you have to _’ize a lot of parameters (violating naming conventions), perform manual shuffling (defeating the sugar benefits of the feature), or add parameter labels to the result of functions (which leads to odd tying between callers and callees).

The root problem here is that we use exactly the same syntax for both forms of function application. If the two forms were differentiated (an option considered in “alternatives considered” below) then some of these problems would be defined away.

From a historical perspective, the tuple splat form of function application dates back to very early Swift design (probably introduced in 2010, but possibly 2011) where all function application was of a single value to a function type. For a large number of reasons (including default arguments, variadic arguments, labels, etc) we have completely abandoned this model, but we never came back to reevaluating the tuple splat behavior.

If we didn’t already have this feature, we would not add it to Swift 3 (at least in its current form).

Proposed solution

The proposed solution is simple, we should just remove this feature from the Swift 3 compiler. Ideally we would deprecate it in the Swift 2.2 compiler and remove it in Swift 3. However, if there isn’t time to get the deprecation into Swift 2.2, the author believes it would be perfectly fine to just remove it in Swift 3 (with a fixit + migration help of course).

One of the interesting aspect of this feature is that some of the people we’ve spoken to are very fond of it. However, when pressed, they admit that they are not actually using it widely in their code, or if they are using it, they are abusing naming conventions (distorting their code) in order to use it. This doesn’t seem like a positive contribution - this seems like a “clever” feature, not a practical one.

Detailed design

The design is straight-forward. In the Swift 3 time frame, we continue to parse and type check these expressions as we have so far, but produce an error + fixit hint when it is the tuple splat form. The migrator would auto-apply the fixit hint as it does for other cases.

Impact on existing code

Any code that uses this feature will have to move to the traditional form. In the case of the example above, this means rewriting the code from:

  foo(x)

into a form like this:

  foo(x.0, x.b)

In the case where “x” is a complex expression, a temporary variable will need to be introduced. We believe that compiler fixits can handle the simple cases directly and that this extension is not widely used.

Alternatives considered

The major problem with this feature is that it was not well considered and implemented properly (owing to its very old age, it has just been kept limping along). The alternative then is to actually design a proper feature to support this. Since the implicitness and syntactic ambiguity with normal function application is the problem, the solution is to introduce an explicit syntactic form to represent this. For example, something like this could address the problems we have:

  foo(*x) // NOT a serious syntax proposal

However, actually designing this feature would be a non-trivial effort not core to the Swift 3 mission:

* It is a pure-sugar feature, and therefore low priority.
* We don’t have an obvious sigil to use. “prefix-star” should be kept as unused for now in case we want to use it to refer to memory-related operations in the future.
* Making the tuple splat operation great requires more than just fixing the syntactic ambiguities we have, it would require re-evaluating the semantics of the operation (e.g. in light of parameter labels, varargs and other features).

If there is serious interest in pursuing this as a concept, we should do it as a follow-on proposal to this one. If a good design emerges, we can evaluate that design based on its merits.

The final alternative is that we could leave the feature in the compiler. However, that means living with its complexity “forever” or breaking code in the Swift 4 timeframe. It would be preferable to tackle this breakage in the Swift 3 timeframe, since we know that migration will already be needed then.

-Chris

_______________________________________________
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


(Jens Persson) #14

+-0. I saw, in my dreams, many "different parts" of the language, like
pattern matching, function argument- & parameter lists, and tuples, all
just being one and the same simple yet powerful unifying concept ...
:´ /

···

On Wed, Jan 27, 2016 at 7:38 AM, Jacob Bandes-Storch via swift-evolution < swift-evolution@swift.org> wrote:

+1 from me as well. I don't think I've ever used this feature on purpose.

I look forward to future discussions about splatting, especially as they
relate to variadic functions.

On Tue, Jan 26, 2016 at 10:36 PM, T.J. Usiyan via swift-evolution < > swift-evolution@swift.org> wrote:

+1

I like the feature quite a bit but avoid it as a result of the naming
concerns. If removing this feature can help improve the type checker, the
trade is worthwhile, IMO.

On Wed, Jan 27, 2016 at 1:23 AM, Chris Lattner via swift-evolution < >> swift-evolution@swift.org> wrote:

For discussion: comments appreciated!

Remove implicit tuple splat behavior from function applications

   - Proposal: SE-
   <https://github.com/apple/swift-evolution/blob/master/proposals/NNNN-name.md>
   TBD
   - Author(s): Chris Lattner
   - Status: *Awaiting review*
   - Review manager: TBD

<https://github.com/apple/swift-evolution#introduction>Introduction

Function calls (which include several syntactic forms that apply an
argument list to something of function type) currently have a dual nature
in Swift. Given something like:

func foo(a : Int, b : Int) {}

You can call it either with with the typical syntactic form that passes
arguments to each of its parameters:

foo(42, b : 17)

or you can take advantage of a little-known feature to pass an entire
argument list as a single value (of tuple type):
let x = (1, b: 2)
foo(x)

This proposal recommends removing the later form, which I affectionately
refer to as the “tuple splat” form. This feature is purely a sugar
feature, it does not provide any expressive ability beyond passing the
parameters manually.

Swift-evolution thread: TBD
<https://github.com/apple/swift-evolution#motivation>Motivation

This behavior is cute, precedented in other functional languages, and
has some advantages, but it also has several major disadvantages, which are
all related to its syntactic form.
* A call to foo(x) looks like a call to an overloaded version of foo,
both to the compiler and to the human who maintains the code. This is
extremely confusing if you don’t know the feature exists.
* There are real ambiguities in the syntax, e.g. involving Any arguments
and situations where you want to pass a tuple value as a single parameter.
* The current implementation has a ton of implementation bugs - it
doesn’t work reliably.
* The current implementation adds complexity to the type checker,
slowing it down and adding maintenance burden.
* The current implementation doesn’t work the way we would want a tuple
splat operation to work. For example, arguably, you should be able to call
foo with:

func bar() -> (Int, Int) { … }
foo(bar())

… but this is not allowed, since tuple labels are required to line up.
You have to write:

func bar() -> (Int, b: Int) { … }
foo(bar())

This makes this feature very difficult to use in practice, because you
have to _’ize a lot of parameters (violating naming conventions), perform
manual shuffling (defeating the sugar benefits of the feature), or add
parameter labels to the result of functions (which leads to odd tying
between callers and callees).

The root problem here is that we use exactly the same syntax for both
forms of function application. If the two forms were differentiated (an
option considered in “alternatives considered” below) then some of these
problems would be defined away.

From a historical perspective, the tuple splat form of function
application dates back to very early Swift design (probably introduced in
2010, but possibly 2011) where all function application was of a single
value to a function type. For a large number of reasons (including default
arguments, variadic arguments, labels, etc) we have completely abandoned
this model, but we never came back to reevaluating the tuple splat behavior.

If we didn’t already have this feature, we would not add it to Swift 3
(at least in its current form).
<https://github.com/apple/swift-evolution#proposed-solution>Proposed
solution

The proposed solution is simple, we should just remove this feature from
the Swift 3 compiler. Ideally we would deprecate it in the Swift 2.2
compiler and remove it in Swift 3. However, if there isn’t time to get the
deprecation into Swift 2.2, the author believes it would be perfectly fine
to just remove it in Swift 3 (with a fixit + migration help of course).

One of the interesting aspect of this feature is that some of the people
we’ve spoken to are very fond of it. However, when pressed, they admit
that they are not actually using it widely in their code, or if they
are using it, they are abusing naming conventions (distorting their code)
in order to use it. This doesn’t seem like a positive contribution - this
seems like a “clever” feature, not a practical one.
<https://github.com/apple/swift-evolution#detailed-design>Detailed
design

The design is straight-forward. In the Swift 3 time frame, we continue
to parse and type check these expressions as we have so far, but produce an
error + fixit hint when it is the tuple splat form. The migrator would
auto-apply the fixit hint as it does for other cases.
<https://github.com/apple/swift-evolution#impact-on-existing-code>Impact
on existing code

Any code that uses this feature will have to move to the traditional
form. In the case of the example above, this means rewriting the code from:
foo(x)

into a form like this:
foo(x.0, x.b)

In the case where “x” is a complex expression, a temporary variable will
need to be introduced. We believe that compiler fixits can handle the
simple cases directly and that this extension is not widely used.

<https://github.com/apple/swift-evolution#alternatives-considered>Alternatives
considered
The major problem with this feature is that it was not well considered
and implemented properly (owing to its very old age, it has just been kept
limping along). The alternative then is to actually design a proper
feature to support this. Since the implicitness and syntactic ambiguity
with normal function application is the problem, the solution is to
introduce an explicit syntactic form to represent this. For example,
something like this could address the problems we have:

foo(*x) // NOT a serious syntax proposal

However, actually designing this feature would be a non-trivial effort
not core to the Swift 3 mission:

* It is a pure-sugar feature, and therefore low priority.
* We don’t have an obvious sigil to use. “prefix-star” should be kept
as unused for now in case we want to use it to refer to memory-related
operations in the future.
* Making the tuple splat operation great requires more than just fixing
the syntactic ambiguities we have, it would require re-evaluating the
semantics of the operation (e.g. in light of parameter labels, varargs and
other features).

If there is serious interest in pursuing this as a concept, we should do
it as a follow-on proposal to this one. If a good design emerges, we can
evaluate that design based on its merits.

The final alternative is that we could leave the feature in the
compiler. However, that means living with its complexity “forever” or
breaking code in the Swift 4 timeframe. It would be preferable to tackle
this breakage in the Swift 3 timeframe, since we know that migration will
already be needed then.

-Chris

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

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

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

--
bitCycle AB | Smedjegatan 12 | 742 32 Östhammar | Sweden
http://www.bitcycle.com/
Phone: +46-73-753 24 62
E-mail: jens@bitcycle.com


(Haravikk) #15

+1 from me as well; if someone needs this they can just use a closure to perform the conversion. This is probably a better way to present it anyway since it makes it clearer since this is presumably what is happening behind the scenes anyway.


(Nisse Bergman) #16

-1 I use this in both my mocking API and in JSON deserialising/serializing.
I think this is a great thing to have

···

On 27 Jan 2016, at 11:07, David Waite via swift-evolution <swift-evolution@swift.org> wrote:

I used this just last week! But only as an illustration

+1

One comment though, a splat operator need not be purely syntactic sugar - it could be the way arrays are applied to variadic functions.

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


(Radek Pietruszewski) #17

+1 with the same caveat. I used this feature a few times and liked having it, but I agree that it would be much better as an explicit operation, and I can wait for that. It's not high priority.

···

Sent from my iPad

On 27 Jan 2016, at 17:06, Paul Cantrell via swift-evolution <swift-evolution@swift.org> wrote:

I do think there’s eventual value in this (modulo syntax refinement):

  foo(*x) // NOT a serious syntax proposal

I have hit just a few proxy-flavored situations where argument forwarding was useful. However, in those situations, it’s very little manual effort to just rebuild the tuple: f(tuple.a, b: tuple.b). I thus don’t consider that feature urgent, and would happily wait until Swift 4+ for it. I’d just like to keep a toe in the door for it.

With that minor caveat, I support this proposal. I’ve been bitten by the Any-related ambiguities Chris mentions — far more often than I’ve actually had use for this feature.

I’ll also reiterate my wish to remove the overloaded meaning of $0 as the unsplatted args of a closure. It’s equally esoteric and super confusing. Hmm, I should bump that thread….

Cheers, P

On Jan 27, 2016, at 12:23 AM, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

For discussion: comments appreciated!

Remove implicit tuple splat behavior from function applications
Proposal: SE-TBD
Author(s): Chris Lattner
Status: Awaiting review
Review manager: TBD
Introduction

Function calls (which include several syntactic forms that apply an argument list to something of function type) currently have a dual nature in Swift. Given something like:

  func foo(a : Int, b : Int) {}

You can call it either with with the typical syntactic form that passes arguments to each of its parameters:

  foo(42, b : 17)

or you can take advantage of a little-known feature to pass an entire argument list as a single value (of tuple type):

  let x = (1, b: 2)
  foo(x)

This proposal recommends removing the later form, which I affectionately refer to as the “tuple splat” form. This feature is purely a sugar feature, it does not provide any expressive ability beyond passing the parameters manually.

Swift-evolution thread: TBD

Motivation

This behavior is cute, precedented in other functional languages, and has some advantages, but it also has several major disadvantages, which are all related to its syntactic form.

* A call to foo(x) looks like a call to an overloaded version of foo, both to the compiler and to the human who maintains the code. This is extremely confusing if you don’t know the feature exists.
* There are real ambiguities in the syntax, e.g. involving Any arguments and situations where you want to pass a tuple value as a single parameter.
* The current implementation has a ton of implementation bugs - it doesn’t work reliably.
* The current implementation adds complexity to the type checker, slowing it down and adding maintenance burden.
* The current implementation doesn’t work the way we would want a tuple splat operation to work. For example, arguably, you should be able to call foo with:

  func bar() -> (Int, Int) { … }
  foo(bar())

… but this is not allowed, since tuple labels are required to line up. You have to write:

  func bar() -> (Int, b: Int) { … }
  foo(bar())

This makes this feature very difficult to use in practice, because you have to _’ize a lot of parameters (violating naming conventions), perform manual shuffling (defeating the sugar benefits of the feature), or add parameter labels to the result of functions (which leads to odd tying between callers and callees).

The root problem here is that we use exactly the same syntax for both forms of function application. If the two forms were differentiated (an option considered in “alternatives considered” below) then some of these problems would be defined away.

From a historical perspective, the tuple splat form of function application dates back to very early Swift design (probably introduced in 2010, but possibly 2011) where all function application was of a single value to a function type. For a large number of reasons (including default arguments, variadic arguments, labels, etc) we have completely abandoned this model, but we never came back to reevaluating the tuple splat behavior.

If we didn’t already have this feature, we would not add it to Swift 3 (at least in its current form).

Proposed solution

The proposed solution is simple, we should just remove this feature from the Swift 3 compiler. Ideally we would deprecate it in the Swift 2.2 compiler and remove it in Swift 3. However, if there isn’t time to get the deprecation into Swift 2.2, the author believes it would be perfectly fine to just remove it in Swift 3 (with a fixit + migration help of course).

One of the interesting aspect of this feature is that some of the people we’ve spoken to are very fond of it. However, when pressed, they admit that they are not actually using it widely in their code, or if they are using it, they are abusing naming conventions (distorting their code) in order to use it. This doesn’t seem like a positive contribution - this seems like a “clever” feature, not a practical one.

Detailed design

The design is straight-forward. In the Swift 3 time frame, we continue to parse and type check these expressions as we have so far, but produce an error + fixit hint when it is the tuple splat form. The migrator would auto-apply the fixit hint as it does for other cases.

Impact on existing code

Any code that uses this feature will have to move to the traditional form. In the case of the example above, this means rewriting the code from:

  foo(x)

into a form like this:

  foo(x.0, x.b)

In the case where “x” is a complex expression, a temporary variable will need to be introduced. We believe that compiler fixits can handle the simple cases directly and that this extension is not widely used.

Alternatives considered

The major problem with this feature is that it was not well considered and implemented properly (owing to its very old age, it has just been kept limping along). The alternative then is to actually design a proper feature to support this. Since the implicitness and syntactic ambiguity with normal function application is the problem, the solution is to introduce an explicit syntactic form to represent this. For example, something like this could address the problems we have:

  foo(*x) // NOT a serious syntax proposal

However, actually designing this feature would be a non-trivial effort not core to the Swift 3 mission:

* It is a pure-sugar feature, and therefore low priority.
* We don’t have an obvious sigil to use. “prefix-star” should be kept as unused for now in case we want to use it to refer to memory-related operations in the future.
* Making the tuple splat operation great requires more than just fixing the syntactic ambiguities we have, it would require re-evaluating the semantics of the operation (e.g. in light of parameter labels, varargs and other features).

If there is serious interest in pursuing this as a concept, we should do it as a follow-on proposal to this one. If a good design emerges, we can evaluate that design based on its merits.

The final alternative is that we could leave the feature in the compiler. However, that means living with its complexity “forever” or breaking code in the Swift 4 timeframe. It would be preferable to tackle this breakage in the Swift 3 timeframe, since we know that migration will already be needed then.

-Chris

_______________________________________________
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


(Paul Cantrell) #18

Brent — Not sure how it all plays out, but would it work for you to use tuples to make all the functions in your chain take a single arg? e.g.:

   private func recordAllocatorWithVersionMemory(args: (versionMemory: CloudDatabaseVersionMemoryType?, typeName: String, _ cloudValue: CloudValue)) throws -> (CKRecord, CloudValue)

   private func writeAttributesAndReferencesToRecord(args: (record: CKRecord, cloudValue: CloudValue))

P

···

On Jan 28, 2016, at 12:30 AM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

Remove implicit tuple splat behavior from function applications

Please do not remove this without providing an alternative.

I use this pretty frequently, usually in combination with higher-order functions like `map` and `filter` when I'm linking together steps in a pipeline. Here's one example where I'm converting structs into CloudKit objects. I have to perform a series of steps: extract the type and value from the instance (and possibly from its child instances), retrieve or allocate CKRecords for each one, and then write the data into the CKRecords. The top-level flow for this is simply:

   func addInstance(instance: CloudRepresentable, recursively: Bool) throws {
       try typeNameAndValueForInstance(instance, recursively: recursively).lazy.map(recordAllocatorWithVersionMemory(versionMemory)).forEach(writeAttributesAndReferencesToRecord)
   }

Each step in this process is implemented with a private method whose signature matches the methods before and after. This allows me to name each step and encapsulate the details of how they're done. Yes, the signatures are slightly contorted, but they're private methods—implementation details—so it doesn't matter.

   private func typeNameAndValueForInstance(instance: CloudRepresentable, recursively: Bool) -> [(typeName: String, value: CloudValue)]
   private func recordAllocatorWithVersionMemory(versionMemory: CloudDatabaseVersionMemoryType?)(typeName: String, _ cloudValue: CloudValue) throws -> (CKRecord, CloudValue)
   private func writeAttributesAndReferencesToRecord(record: CKRecord, cloudValue: CloudValue)

I agree that the implicit splat causes problems sometimes, but I don't think it should be removed without an alternative.


(Chris Lattner) #19

Remove implicit tuple splat behavior from function applications

Please do not remove this without providing an alternative.

I understand the desires, but I don’t think that is the right way to go in this case for three reasons:

1. Proposals should be incremental. Removing this (very rarely used) feature is a prerequisite to adding its replacement, particularly given that the replacement will have different semantics.

2. The proposed addition of the new feature will have to be self-justified based on the merits of that proposal. Those merits will depend on the exact design.

3. Shipping Swift 3 with this feature, then changing the feature in Swift 4 would break code. While Swift 4 will probably break *some* code, we really want to reduce cases that happens. We *know* this feature is wrong as it is, so it is most conservative to remove it now (accepting the breakage in Swift 3 timeframe) and add it back if/when there is a good proposal.

-Chris

···

On Jan 27, 2016, at 10:30 PM, Brent Royal-Gordon <brent@architechies.com> wrote:

I use this pretty frequently, usually in combination with higher-order functions like `map` and `filter` when I'm linking together steps in a pipeline. Here's one example where I'm converting structs into CloudKit objects. I have to perform a series of steps: extract the type and value from the instance (and possibly from its child instances), retrieve or allocate CKRecords for each one, and then write the data into the CKRecords. The top-level flow for this is simply:

   func addInstance(instance: CloudRepresentable, recursively: Bool) throws {
       try typeNameAndValueForInstance(instance, recursively: recursively).lazy.map(recordAllocatorWithVersionMemory(versionMemory)).forEach(writeAttributesAndReferencesToRecord)
   }

Each step in this process is implemented with a private method whose signature matches the methods before and after. This allows me to name each step and encapsulate the details of how they're done. Yes, the signatures are slightly contorted, but they're private methods—implementation details—so it doesn't matter.

   private func typeNameAndValueForInstance(instance: CloudRepresentable, recursively: Bool) -> [(typeName: String, value: CloudValue)]
   private func recordAllocatorWithVersionMemory(versionMemory: CloudDatabaseVersionMemoryType?)(typeName: String, _ cloudValue: CloudValue) throws -> (CKRecord, CloudValue)
   private func writeAttributesAndReferencesToRecord(record: CKRecord, cloudValue: CloudValue)

I agree that the implicit splat causes problems sometimes, but I don't think it should be removed without an alternative. For my uses, simply being able to say `myFunc.apply` for any function (including the ones provided by SomeType.init) would be sufficient. This method could perhaps be overloaded to support all possible combinations of default argument omissions.

--
Brent Royal-Gordon
Architechies


(Tino) #20

+1. Based on experience of splatting use in other languages it has limited applications. I assume feature not well known by Swift community just because of small demand. For those who needs splatting, it can be replicated by existing tooling manually or generalized by something similar to following code:
func splat<T, U, R>(f: (T, U) -> R, _ tuple: (T, U)) -> R {
    return f(tuple.0, tuple.1)
}

If a set of splat functions would be available in stdlib, they could act as a temporary stand-in, but it is a poor replacement:
The beauty of splat is that it works for functions with an arbitrary number of parameters out of the box, and it is most useful when you have many parameters (something that is very uncommon in Cocoa — but I expect that this won't be true for new libraries that make use of default parameter values).
Swift has many convenience features and overlapping functionality, but tuple splat is singular and can't be replaced with other constructs.