Proposal: Remove implicit tuple splat behavior from function applications

+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))

Yes, these should all work.

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

it is a bug that this first one compiles, it is an example of the flakiness caused by this. This is the inverse of splat :-)

array.append(int_let, 1)
array.append(1, 1)
array.append(int_var, 1)
array.append(int_var, int_var)

These should all be uniformly rejected, since append takes one parameter.

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)?

Yes, even with the current design, this is a bug. As I mentioned, the tuple “splat” feature has a number of bugs.

-Chris

···

On Jan 31, 2016, at 10:35 AM, Janosch Hildebrand <jnosh@jnosh.com> wrote:

Fair enough. Can we get an explicit, well-designed version of this before we get rid of the implicit, poorly-designed version?

I can’t recall off the top my head where, but I know I’ve done something like this:
struct FunctionApplicator <T, U> {
    var args: T
    let function: T -> U
    init(args: T, function: T -> U) {
        self.args = args
        self.function = function
    }
    func apply() -> U {
        return function(args)
    }
}

Without tuple splatting, you’d need a FunctionApplicator1<T,U>, FunctionApplicator2<T,U,V>, FunctionApplicator3<T,U,V,W>, etc. They can’t even have the same name because Swift doesn’t support overloading type names for types with a different number of generic parameters.

- Dave Sweeris

···

On Jan 27, 2016, at 08:48, Matthew Johnson via swift-evolution <swift-evolution@swift.org> wrote:

On Jan 27, 2016, at 4:18 AM, Nisse Bergman via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

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

It would be great to have an explicit, well-designed version of this. Nobody is arguing against that. But there are quite a few problems with its current form.

Hi, Nisse. Since most responses on the thread have been +1s, would you mind going into a bit more detail here? It's important to see real-world use cases for a feature, and it may help convince others.

Thanks,
Jordan

···

On Jan 27, 2016, at 2:18 , Nisse Bergman via swift-evolution <swift-evolution@swift.org> wrote:

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

I think libraries like argo use it.

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

Ok, I’m definitely interested in knowing more. Please include a code sample, showing both the declaration being splatted into and the call sites. Otherwise, I can’t tell how much value this feature is adding, and what the pain would be if it were removed. Thanks!

-Chris

···

On Jan 26, 2016, at 11:41 PM, James Campbell via swift-evolution <swift-evolution@swift.org> wrote:

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 <mailto: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 <mailto: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 <mailto: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&gt;TBD
Author(s): Chris Lattner
Status: Awaiting review
Review manager: TBD
<GitHub - apple/swift-evolution: This maintains proposals for changes and user-visible enhancements to the Swift Programming Language.

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

<GitHub - apple/swift-evolution: This maintains proposals for changes and user-visible enhancements to the Swift Programming Language.

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).

<GitHub - apple/swift-evolution: This maintains proposals for changes and user-visible enhancements to the Swift Programming Language. 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.

<GitHub - apple/swift-evolution: This maintains proposals for changes and user-visible enhancements to the Swift Programming Language. 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.

<GitHub - apple/swift-evolution: This maintains proposals for changes and user-visible enhancements to the Swift Programming Language. 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.

<GitHub - apple/swift-evolution: This maintains proposals for changes and user-visible enhancements to the Swift Programming Language. 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 <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

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

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

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

I had that dream too, very early on in Swift development, but it isn’t practical for a very large number of reasons…

-Chris

···

On Jan 26, 2016, at 11:44 PM, Jens Persson via swift-evolution <swift-evolution@swift.org> wrote:

+-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 ...
:´ /

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

Ok, I’m definitely interested in knowing more. Please include a code sample, showing both the declaration being splatted into and the call sites. Otherwise, I can’t tell how much value this feature is adding, and what the pain would be if it were removed. Thanks!

-Chris

···

On Jan 27, 2016, at 2:18 AM, Nisse Bergman <nisse@potmo.com> wrote:

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

Fair enough. Can we get an explicit, well-designed version of this before we get rid of the implicit, poorly-designed version?

I’m certainly not opposed to someone exploring this, but I don’t think it should block removing the bad thing.

I can’t recall off the top my head where, but I know I’ve done something like this:
struct FunctionApplicator <T, U> {
    var args: T
    let function: T -> U
    init(args: T, function: T -> U) {
        self.args = args
        self.function = function
    }
    func apply() -> U {
        return function(args)
    }
}

Without tuple splatting, you’d need a FunctionApplicator1<T,U>, FunctionApplicator2<T,U,V>, FunctionApplicator3<T,U,V,W>, etc. They can’t even have the same name because Swift doesn’t support overloading type names for types with a different number of generic parameters.

Perhaps I’m misunderstanding, but that is certainly not the case. Even without the feature in question you can definitely pass a tuple around as a single argument, e.g.:

func f(a : Int, _ b : Int) {…}

let x = FunctionApplicator((42, b: 19), f)

This functionality won’t be affected by removal of this feature.

-Chris

···

On Jan 27, 2016, at 9:27 AM, Dave via swift-evolution <swift-evolution@swift.org> wrote:

<scratches head> Then I’ve misunderstood what splatting was. Is the difference between splatting and what my example does the arguments’ labels?
func f(a : Int, _ b : Int) {…}
let x = FunctionApplicator((42, b: 19), f) //Would stay legal, because of the "b:"
let y = FunctionApplicator((42, 19), f) //Would become illegal, because there’s no “b:”

And we would have to use the internal argument labels? If so, how would that work with functions in 3rd-party libraries, where we might not have access to that information?

- Dave Sweeris

···

On Jan 27, 2016, at 10:21, Chris Lattner <clattner@apple.com> wrote:

On Jan 27, 2016, at 9:27 AM, Dave via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Fair enough. Can we get an explicit, well-designed version of this before we get rid of the implicit, poorly-designed version?

I’m certainly not opposed to someone exploring this, but I don’t think it should block removing the bad thing.

I can’t recall off the top my head where, but I know I’ve done something like this:
struct FunctionApplicator <T, U> {
    var args: T
    let function: T -> U
    init(args: T, function: T -> U) {
        self.args = args
        self.function = function
    }
    func apply() -> U {
        return function(args)
    }
}

Without tuple splatting, you’d need a FunctionApplicator1<T,U>, FunctionApplicator2<T,U,V>, FunctionApplicator3<T,U,V,W>, etc. They can’t even have the same name because Swift doesn’t support overloading type names for types with a different number of generic parameters.

Perhaps I’m misunderstanding, but that is certainly not the case. Even without the feature in question you can definitely pass a tuple around as a single argument, e.g.:

func f(a : Int, _ b : Int) {…}

let x = FunctionApplicator((42, b: 19), f)

This functionality won’t be affected by removal of this feature.

-Chris

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

It would be great to have an explicit, well-designed version of this. Nobody is arguing against that. But there are quite a few problems with its current form.

···

On Jan 27, 2016, at 4:18 AM, Nisse Bergman via swift-evolution <swift-evolution@swift.org> wrote:

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

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

There is no change to either of these. The later example succeed because of an implicit conversion from "(T,U) -> (T, b: U)”, which is an entirely separate thing.

The tuple splat behavior I’m talking about can *only* affect call sites that take a single argument. Both of these examples take two.

-Chris

···

On Jan 27, 2016, at 10:46 AM, davesweeris@mac.com wrote:

<scratches head> Then I’ve misunderstood what splatting was. Is the difference between splatting and what my example does the arguments’ labels?
func f(a : Int, _ b : Int) {…}
let x = FunctionApplicator((42, b: 19), f) //Would stay legal, because of the "b:"
let y = FunctionApplicator((42, 19), f) //Would become illegal, because there’s no “b:”

I'm +1 if this will eventually be replaced with a better-designed version, but I do use it and would be sad to see it go. I've found it very helpful for testing:

protocol APIType {
    func getChatMessages(channel channel: ChatChannel, limit: Int, success: (chatMessages: [ChatMessage]) -> Void, failure: (error: NSError) -> Void)
}

class FakeAPI: APIType {
    var getChatMessagesEndpoint = FakeAPIEndpoint(
        calls: [(channel: ChatChannel, limit: Int)](),
        successStubs: [[ChatMessage]](),
        failureStubs: [NSError]()
    )
    
    func getChatMessages(channel channel: ChatChannel, limit: Int, success: (chatMessages: [ChatMessage]) -> Void, failure: (error: NSError) -> Void) {
        getChatMessagesEndpoint.handleCall(success, failure, callParams: (channel: channel, limit: limit))
    }
}

struct FakeAPIEndpoint<CallParams, SuccessParams, FailureParams> {
    private(set) var calls: [CallParams]
    private(set) var successStubs: [SuccessParams]
    private(set) var failureStubs: [FailureParams]
    
    mutating func stubSuccess(params: SuccessParams) {
        successStubs.append(params)
    }
    
    mutating func stubFailure(params: FailureParams) {
        failureStubs.append(params)
    }
    
    private mutating func handleCall(success: (SuccessParams) -> Void, _ failure: (FailureParams) -> Void, call: CallParams) {
        calls.append(call)
        
        if successStubs.count > 0 {
            success(successStubs.removeFirst())
        }
        else if failureStubs.count > 0 {
            failure(failureStubs.removeFirst())
        }
    }
}

It's really nice to have that handleCall method that can call the success or failure callback automatically rather than reimplementing that functionality in every fake API call.

Jarod

···

On Jan 27, 2016, at 10:16, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

On Jan 27, 2016, at 2:18 AM, Nisse Bergman <nisse@potmo.com> wrote:

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

Ok, I’m definitely interested in knowing more. Please include a code sample, showing both the declaration being splatted into and the call sites. Otherwise, I can’t tell how much value this feature is adding, and what the pain would be if it were removed. Thanks!

-Chris

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

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

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

It would be great to have an explicit, well-designed version of this. Nobody is arguing against that. But there are quite a few problems with its current form.

+1, exactly this.

I also have tried to use the implicit form for similar purposes but it’s IMHO too wonky to rely on; I’d rather see it removed, and a well-thought-through explicit form (eventually) introduced (perhaps as part of broader compiler support for tuple-manipulation).

···

On Jan 27, 2016, at 10:48 AM, Matthew Johnson via swift-evolution <swift-evolution@swift.org> wrote:

On Jan 27, 2016, at 4:18 AM, Nisse Bergman via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

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

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

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

I'm +1 if this will eventually be replaced with a better-designed version, but I do use it and would be sad to see it go. I've found it very helpful for testing:

Hi Jarod, where are you using this feature?

protocol APIType {
    func getChatMessages(channel channel: ChatChannel, limit: Int, success: (chatMessages: [ChatMessage]) -> Void, failure: (error: NSError) -> Void)
}

class FakeAPI: APIType {
    var getChatMessagesEndpoint = FakeAPIEndpoint(
        calls: [(channel: ChatChannel, limit: Int)](),
        successStubs: [[ChatMessage]](),
        failureStubs: [NSError]()
    )
    
    func getChatMessages(channel channel: ChatChannel, limit: Int, success: (chatMessages: [ChatMessage]) -> Void, failure: (error: NSError) -> Void) {
        getChatMessagesEndpoint.handleCall(success, failure, callParams: (channel: channel, limit: limit))

If you are referring to this line, then there is no change. The only affected case is in call sites that take a single parameter without a label. Passing tuples as values will not be change.

-Chris

···

On Jan 27, 2016, at 11:03 AM, swift@lng.la wrote:

    }
}

struct FakeAPIEndpoint<CallParams, SuccessParams, FailureParams> {
    private(set) var calls: [CallParams]
    private(set) var successStubs: [SuccessParams]
    private(set) var failureStubs: [FailureParams]
    
    mutating func stubSuccess(params: SuccessParams) {
        successStubs.append(params)
    }
    
    mutating func stubFailure(params: FailureParams) {
        failureStubs.append(params)
    }
    
    private mutating func handleCall(success: (SuccessParams) -> Void, _ failure: (FailureParams) -> Void, call: CallParams) {
        calls.append(call)
        
        if successStubs.count > 0 {
            success(successStubs.removeFirst())
        }
        else if failureStubs.count > 0 {
            failure(failureStubs.removeFirst())
        }
    }
}

It's really nice to have that handleCall method that can call the success or failure callback automatically rather than reimplementing that functionality in every fake API call.

Jarod

On Jan 27, 2016, at 10:16, Chris Lattner via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Jan 27, 2016, at 2:18 AM, Nisse Bergman <nisse@potmo.com <mailto:nisse@potmo.com>> wrote:

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

Ok, I’m definitely interested in knowing more. Please include a code sample, showing both the declaration being splatted into and the call sites. Otherwise, I can’t tell how much value this feature is adding, and what the pain would be if it were removed. Thanks!

-Chris

On 27 Jan 2016, at 11:07, David Waite via swift-evolution <swift-evolution@swift.org <mailto: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 <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

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

Apologies -- I just realized I chose a bad example that isn't actually using the feature, but the usage would be here:

if successStubs.count > 0 {
    success(successStubs.removeFirst())
}
else if failureStubs.count > 0 {
    failure(failureStubs.removeFirst())
}

Here's a quick modification of the example that would necessitate using the feature:

var getChatMessagesEndpoint = FakeAPIEndpoint(
    calls: [(channel: ChatChannel, limit: Int)](),
    successStubs: [(messages: [ChatMessage], someOtherReturnValue: Int)](),
    failureStubs: [(error: NSError, status: Int)]()
)

Unless I've overlooked something, when the success and failure callbacks take multiple parameters, calling them in a generic way requires the use of tuple splatting.

Jarod

···

On Jan 27, 2016, at 11:10, Chris Lattner <clattner@apple.com> wrote:

On Jan 27, 2016, at 11:03 AM, swift@lng.la <mailto:swift@lng.la> wrote:

I'm +1 if this will eventually be replaced with a better-designed version, but I do use it and would be sad to see it go. I've found it very helpful for testing:

Hi Jarod, where are you using this feature?

protocol APIType {
    func getChatMessages(channel channel: ChatChannel, limit: Int, success: (chatMessages: [ChatMessage]) -> Void, failure: (error: NSError) -> Void)
}

class FakeAPI: APIType {
    var getChatMessagesEndpoint = FakeAPIEndpoint(
        calls: [(channel: ChatChannel, limit: Int)](),
        successStubs: [[ChatMessage]](),
        failureStubs: [NSError]()
    )
    
    func getChatMessages(channel channel: ChatChannel, limit: Int, success: (chatMessages: [ChatMessage]) -> Void, failure: (error: NSError) -> Void) {
        getChatMessagesEndpoint.handleCall(success, failure, callParams: (channel: channel, limit: limit))

If you are referring to this line, then there is no change. The only affected case is in call sites that take a single parameter without a label. Passing tuples as values will not be change.

-Chris

    }
}

struct FakeAPIEndpoint<CallParams, SuccessParams, FailureParams> {
    private(set) var calls: [CallParams]
    private(set) var successStubs: [SuccessParams]
    private(set) var failureStubs: [FailureParams]
    
    mutating func stubSuccess(params: SuccessParams) {
        successStubs.append(params)
    }
    
    mutating func stubFailure(params: FailureParams) {
        failureStubs.append(params)
    }
    
    private mutating func handleCall(success: (SuccessParams) -> Void, _ failure: (FailureParams) -> Void, call: CallParams) {
        calls.append(call)
        
        if successStubs.count > 0 {
            success(successStubs.removeFirst())
        }
        else if failureStubs.count > 0 {
            failure(failureStubs.removeFirst())
        }
    }
}

It's really nice to have that handleCall method that can call the success or failure callback automatically rather than reimplementing that functionality in every fake API call.

Jarod

On Jan 27, 2016, at 10:16, Chris Lattner via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Jan 27, 2016, at 2:18 AM, Nisse Bergman <nisse@potmo.com <mailto:nisse@potmo.com>> wrote:

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

Ok, I’m definitely interested in knowing more. Please include a code sample, showing both the declaration being splatted into and the call sites. Otherwise, I can’t tell how much value this feature is adding, and what the pain would be if it were removed. Thanks!

-Chris

On 27 Jan 2016, at 11:07, David Waite via swift-evolution <swift-evolution@swift.org <mailto: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 <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

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

Chris, tell me if this code example clarifies what you are proposing.

Given:
func moveTo(x x:Double, y:Double} {/*…*/ }
moveTo(x:0, y:0)

This is the behavior that is being proposed to be removed:
var point = (x:1.0, y:1.0)
moveTo(point)

instead, I’d have to do either:
moveTo(x:point.x, y:point.y)

or:

func moveTo(point:(x:Double, y:Double)) {
  moveTo(x:point.x, y:point.y)
}

today, I’m not entirely sure the behavior if I defined that second function signature. I’m guessing that is one of the motivators to remove this behavior.

-DW

···

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

On Jan 27, 2016, at 11:03 AM, swift@lng.la <mailto:swift@lng.la> wrote:

I'm +1 if this will eventually be replaced with a better-designed version, but I do use it and would be sad to see it go. I've found it very helpful for testing:

Hi Jarod, where are you using this feature?

protocol APIType {
    func getChatMessages(channel channel: ChatChannel, limit: Int, success: (chatMessages: [ChatMessage]) -> Void, failure: (error: NSError) -> Void)
}

class FakeAPI: APIType {
    var getChatMessagesEndpoint = FakeAPIEndpoint(
        calls: [(channel: ChatChannel, limit: Int)](),
        successStubs: [[ChatMessage]](),
        failureStubs: [NSError]()
    )
    
    func getChatMessages(channel channel: ChatChannel, limit: Int, success: (chatMessages: [ChatMessage]) -> Void, failure: (error: NSError) -> Void) {
        getChatMessagesEndpoint.handleCall(success, failure, callParams: (channel: channel, limit: limit))

If you are referring to this line, then there is no change. The only affected case is in call sites that take a single parameter without a label. Passing tuples as values will not be change.

-Chris

    }
}

struct FakeAPIEndpoint<CallParams, SuccessParams, FailureParams> {
    private(set) var calls: [CallParams]
    private(set) var successStubs: [SuccessParams]
    private(set) var failureStubs: [FailureParams]
    
    mutating func stubSuccess(params: SuccessParams) {
        successStubs.append(params)
    }
    
    mutating func stubFailure(params: FailureParams) {
        failureStubs.append(params)
    }
    
    private mutating func handleCall(success: (SuccessParams) -> Void, _ failure: (FailureParams) -> Void, call: CallParams) {
        calls.append(call)
        
        if successStubs.count > 0 {
            success(successStubs.removeFirst())
        }
        else if failureStubs.count > 0 {
            failure(failureStubs.removeFirst())
        }
    }
}

It's really nice to have that handleCall method that can call the success or failure callback automatically rather than reimplementing that functionality in every fake API call.

Jarod

On Jan 27, 2016, at 10:16, Chris Lattner via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Jan 27, 2016, at 2:18 AM, Nisse Bergman <nisse@potmo.com <mailto:nisse@potmo.com>> wrote:

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

Ok, I’m definitely interested in knowing more. Please include a code sample, showing both the declaration being splatted into and the call sites. Otherwise, I can’t tell how much value this feature is adding, and what the pain would be if it were removed. Thanks!

-Chris

On 27 Jan 2016, at 11:07, David Waite via swift-evolution <swift-evolution@swift.org <mailto: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 <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

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

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

I thought Dave had a good point with his first generics example before, but don’t understand what the point was with labeling that he makes above. However, there is a call site that takes a single argument in his example:
func apply() -> U {
    return function(args)
}

Where args could be a tuple of type T.

I just want to be clear on this. I’ll put the question a bit more succinctly. Wouldn’t this functionality be eliminated with the removal of tuple splatting?

func execute<T,U>(f: T->U, args: T) -> U {
    return f(args)
}
execute({$0+1}, args: 5) // Single arg
//6
execute(*, args: (5,5)) // Tuple arg
//25

In this generic function, args can be either a single argument *or* a tuple of arguments that gets applied to the function.

Having said that, I’m not sure what the real use cases or practical value of this is. Perhaps like a lot of people, I thought Swift’s features like function currying and tuple splatting were *cool* but in reality never used them. Although I think the generics context is something to consider, I still give the proposal +1. Tuple splatting looks to be a feature that can’t pull its own weight and has downsides because of syntactic ambiguity.

-Chris Whidden

···

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

On Jan 27, 2016, at 10:46 AM, davesweeris@mac.com <mailto:davesweeris@mac.com> wrote:

<scratches head> Then I’ve misunderstood what splatting was. Is the difference between splatting and what my example does the arguments’ labels?
func f(a : Int, _ b : Int) {…}
let x = FunctionApplicator((42, b: 19), f) //Would stay legal, because of the "b:"
let y = FunctionApplicator((42, 19), f) //Would become illegal, because there’s no “b:”

There is no change to either of these. The later example succeed because of an implicit conversion from "(T,U) -> (T, b: U)”, which is an entirely separate thing.

The tuple splat behavior I’m talking about can *only* affect call sites that take a single argument. Both of these examples take two.

-Chris

I agree. I'm +1 for the proposal, but it should be possible for a T to
resolve to an arguments-list type tuple. As I don't often think like a
compiler, I don't know if that would be made harder or easier as a
result of this proposal.

Zachary Waldowski zach@waldowski.me

···

On Wed, Jan 27, 2016, at 12:27 PM, Dave via swift-evolution wrote:

Fair enough. Can we get an explicit, well-designed version of this
before we get rid of the implicit, poorly-designed version?

I can’t recall off the top my head where, but I *know* I’ve done
something like this: struct FunctionApplicator <T, U> { var args: T
let function: T -> U init(args: T, function: T -> U) { self.args =
args self.function = function } func apply() -> U {
returnfunction(args) } }

Without tuple splatting, you’d need a FunctionApplicator1<T,U>,
FunctionApplicator2<T,U,V>, FunctionApplicator3<T,U,V,W>, etc. They
can’t even have the same name because Swift doesn’t support
overloading type names for types with a different number of generic
parameters.

- Dave Sweeris

On Jan 27, 2016, at 08:48, Matthew Johnson via swift-evolution <swift- >> evolution@swift.org> wrote:

On Jan 27, 2016, at 4:18 AM, Nisse Bergman via swift-evolution <swift- >>> evolution@swift.org> wrote:

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

It would be great to have an explicit, well-designed version of this.
Nobody is arguing against that. But there are quite a few problems
with its current form.

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

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))

Yes, but it would be less elegant. As it is, these are normal functions written to operate on a single model instance, and I just happen to use them with `map` and `forEach` to apply them to many instances. Rewriting them to explicitly take tuples makes the code more awkward. Obviously this ends up boiling down to code style, but I would be very sorry to see the capability to essentially prepare a bunch of argument lists and apply them all to a function go.

···

--
Brent Royal-Gordon
Architechies

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.

"Look, boy: First you have to endure a fresh haircut, afterwards we can debate about the merits of visiting the ice cream shop" ;-)

I guess all critics could be silenced easily with a realistic possibility that the feature will be re-added in a later version, so its just a natural reaction to highlight the value of tuple splat now and convince many people that it is a nice concept.
If this fails, I predict that reactions on a proposal to add tuple splat will include statements like "didn't Swift have this in an old version, but removed it because it was bad?".

A process with small steps is easier to handle and more honest (no one can be sure what will happen until Swift 4, so it's good to be careful with promises), but it is easier to thrill people with a big picture of the future (it is even more easy to disappoint them with a failed vision — and we already have enough thrill now ;-).

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.

Considering a common reaction on the proposal ("I never used this, get rid of it"), it is hard to foresee how many people would like to add an exotic feature that they haven't seen in action.
But I really hope that decisions in general are not only based on the needs of the majority:
Many C++ developers never write templates, and some even hate them — but imho their removal would cripple the language.

Tino

This isn’t using the feature either. You are passing a single value (returned by removeFirst) as a single argument. No spat is happening.

-Chris

···

On Jan 27, 2016, at 11:24 AM, swift@lng.la wrote:

Apologies -- I just realized I chose a bad example that isn't actually using the feature, but the usage would be here:

if successStubs.count > 0 {
    success(successStubs.removeFirst())
}
else if failureStubs.count > 0 {
    failure(failureStubs.removeFirst())
}