[swift-evolution] Proposal: Always flatten the single element tuple

David Hart davidhart at fastmail.com
Thu Jun 8 15:39:59 CDT 2017


Side note: when converting a project to Swift 4, I’ve encountered quite a few Quality of Life regressions that could also be related:

Regression 1

When using this library https://github.com/socketio/socket.io-client-swift:

public typealias NormalCallback = ([Any], SocketAckEmitter) -> Void

class Socket {
    func on(_ event: String, callback: @escaping NormalCallback) -> UUID { … }
}

My code had to be modified from:

socket.on(“message”) { _ in
    print(“got message")
}

to:

socket.on(“message”) { (_, _) in
    print(“got message")
}

Regression 2

When using RxSwift: https://github.com/ReactiveX/RxSwift <https://github.com/ReactiveX/RxSwift> (simplified example):

public class Driver<E> {
    func drive(onNext: ((E) -> Void)) -> Disposable {}
}

I had to change the following code:

let driver: Driver<Void> = …
driver.drive(onNext: {
    print(“onNext")
})

to:

let driver: Driver<Void> = …
driver.drive(onNext { _ in
    print(“onNext")
})

Regression 3

Also with RxSwift (simplified example):

public class Driver<E> {
    func onNext(_ element: E) { … }
}

I had to change the following code:

let driver: Driver<Void> = …
driver.onNext()

to:

let driver: Driver<Void> = …
driver.onNext(())

Conclusion

Basically, these regressions add synctactic pollution to quite a few cases turning around tuples and Void.

David.

> On 8 Jun 2017, at 12:08, David Hart via swift-evolution <swift-evolution at swift.org> wrote:
> 
>> 
>> On 8 Jun 2017, at 11:17, Gwendal Roué via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>> 
>> 
>>> Le 8 juin 2017 à 19:40, Brent Royal-Gordon via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> a écrit :
>>> 
>>>> On Jun 7, 2017, at 3:03 AM, Adrian Zubarev via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>>>> 
>>>> Well please no:
>>>> 
>>>> 
>>>>  let fn2: ((Int, Int)) -> Void = { lhs, rhs in } 
>>>> 
>>>> Instead use destructuring sugar pitched by Chris Lattner on the other thread:
>>>> 
>>>> let fn2: ((Int, Int)) -> Void = { ((lhs, rhs)) in }
>>>> 
>>> 
>>> I think this suggestion is better than the status quo. I'm wondering, though, if we should just drop the outer set of parentheses entirely, unless you're also putting types on the parameters. That is, a closure of type `(Int, Int) -> T` can look like this:
>>> 
>>> 	{ (x: Int, y: Int) in … }
>>> 
>>> Or it can look like this:
>>> 
>>> 	{ x, y in … }
>>> 
>>> But it *cannot* look like this:
>>> 
>>> 	{ (x, y) in … }
>>> 
>>> The `(x, y)` form can instead be a closure of a type like `((Int, Int)) -> T`, which immediately destructures the tuple parameter into separate constants.
>>> 
>>> -- 
>>> Brent Royal-Gordon
>>> Architechies
>> 
>> Hello,
>> 
>> There's a difference, in the mind of people here that try to show how bad were the recent changes, between:
>> 
>> 1: closures defined independently
>> 2: closures given as a parameter to a function.
>> 
>> I think that we all agree that the type of a closure that is defined independently should be well defined:
>> 
>>     // Choose between (Int, Int) -> () or ((x: Int, y: Int)) -> ()
>>     let a = { (x: Int, y: Int) -> Int in ... }
>>     let b = { ((x: Int, y: Int)) -> Int in ... }
>> 
>> However, when a closure is given as an argument of a function that expects a closure, we ask for the maximum possible flexibility, as Swift 3 did:
>> 
>>     func wantsTwoArguments(_ closure: (Int, Int) -> Int) { closure(1, 2) }
>>     wantsTwoArguments { a, b in a + b }
>>     wantsTwoArguments { (a, b) in a + b }
>>     wantsTwoArguments { t in t.0 + t.1 } // OK, maybe not
>> 
>>     func wantsATupleArgument(_ closure: ((Int, Int)) -> Int) { closure((1, 2)) }
>>     wantsATupleArgument { a, b in a + b }
>>     wantsATupleArgument { (a, b) in a + b }
>>     wantsATupleArgument { t in t.0 + t.1 }
>>     
>>     func wantsANamedTupleArgument(_ closure: ((lhs: Int, rhs: Int)) -> Int) { closure((lhs: 1, rhs: 2)) }
>>     wantsANamedTupleArgument { a, b in a + b }
>>     wantsANamedTupleArgument { (a, b) in a + b }
>>     wantsANamedTupleArgument { t in t.lhs + t.rhs }
>> 
>> This gives us the ability to deal with unfitted function signatures. For example, most Dictionary methods. Yes, they are usually unfitted:
>> 
>>     extension Dictionary {
>>         func forEach(_ body: ((key: Key, value: Value)) throws -> Void) rethrows
>>     }
>> 
>> Who cares about this named (key:value:) tuple? Absolutely nobody, as exemplified by this remarquable Swift 3 snippet below, where no tuple, no `key`, and no `value` is in sight:
>> 
>>     let scores: [String: Int] = ... // [playerName: score]
>>     scores.forEach { name, score in
>>         print("\(name): \(score)")
>>     }
>> 
>> Do you see?
> 
> This post is the one that makes most sense to me! Be strict on definition but flexible on use.
> 
>> Gwendal
>> 
>> 
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
>> https://lists.swift.org/mailman/listinfo/swift-evolution <https://lists.swift.org/mailman/listinfo/swift-evolution>
> 
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
> https://lists.swift.org/mailman/listinfo/swift-evolution <https://lists.swift.org/mailman/listinfo/swift-evolution>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20170608/c7669525/attachment.html>


More information about the swift-evolution mailing list