[swift-evolution] History and future of Swift's parentheses
Robert Bennett
rltbennett at icloud.com
Fri Jun 9 17:02:38 CDT 2017
I see. My ideal solution to this would be an explicit tuple packing and unpacking API, as opposed to implicitly flexible functions. Something akin to (borrowing Python’s asterisk syntax):
func compose<T, U, V>(_ g: (*U)->*V, _ f: (*T)->*U) -> (*T)->*V {...}
The asterisks indicate that the arguments to the function will be packed into a tuple (or not, in the case of a single-argument function) whose type is denoted by T, U, V, or conversely that the functions take/return argument lists that look like *T, *U, *V where T, U, V are tuple types. Then the function definition would look like
{
return { x: *T in g(f(x)) }
}
This would in essence create a new non nominal type: function argument lists. These would be distinct from tuples, although the two may freely be converted into one another using the asterisk. This type would be structurally distinct from tuples by virtue of the fact that single-element argument lists exist while single-element tuples do not. (An asterisk will convert a single-element argument list into a single (non-tuple) element and vice versa.)
To users, argument lists would be accessed solely through tuples with the asterisk syntax. The identity **T == T would hold whenever T is a tuple or argument list. Their usefulness would lie in the ability to call f(*x) when f takes multiple arguments and x is a tuple.
I acknowledge that this is probably not going to happen for Swift 4 but I would sure like it if it did.
> On Jun 9, 2017, at 5:10 PM, Jens Persson via swift-evolution <swift-evolution at swift.org> wrote:
>
> The point of exercise 1 is to show that it is impossible (in Swift 4) to write a generic function composition operator (or function) which works as expected for any reasonable functions.
> This was possible in Swift 3, but in Swift 4 it will only work for functions with exactly one parameter. You'd have to special-case it for every combination of parameter counts of f and g that it should be able to handle.
>
> The following program demonstrates how it can be done in Swift 3.1 and 3.2:
>
> func compose<T, U, V>(_ g: @escaping (U) -> V, _ f: @escaping (T) -> U) -> (T) -> V {
> return { x in g(f(x)) }
> }
> func sum(_ a: Int, _ b: Int) -> Int { return a + b }
> func square(_ a: Int) -> Int { return a * a }
> let squaredSum = compose(square, sum)
> let result = squaredSum((3, 4)) // A bit unexepected with a tuple here but ok ...
> print(result) // 49
> // Well, it worked, not flawlessly but we did manage to write
> // a function composition function and we composed sum
> // and square, and we could call it and get a correct result.
>
>
> And this program demonstrates what happens if you try it in Swift 4:
>
> func compose<T, U, V>(_ g: @escaping (U) -> V, _ f: @escaping (T) -> U) -> (T) -> V {
> return { x in g(f(x)) }
> }
> func sum(_ a: Int, _ b: Int) -> Int { return a + b }
> func square(_ a: Int) -> Int { return a * a }
> // let squaredSum = compose(square, sum) // Error! (without the compose-variant below)
>
> // The error message is:
> // Cannot convert value of type `(Int, Int) -> Int` to
> // expected argument type `(_) -> _`
>
> // That's it, it is simply not possible!
>
> // You'd have to write special variants of the compose func for every combination
> // of parameter counts! For example, in order to get this sum and square
> // example working, this specific variant must be written:
> func compose<T, U, V, W>(_ g: @escaping (V) -> W, _ f: @escaping (T, U) -> V) -> (T, U) -> W {
> return { (x, y) in g(f(x, y)) }
> }
> // Now it will work:
> let squaredSum = compose(square, sum)
> // But only thanks to that awfully specific compose func variant ...
> // We would have to write a lot more variants for it to be practically usable on pretty much any common function.
>
> I'm sure some will say:
> "no regular developers use function composition anyway so why ..."
> or
> "It's not very swifty to use free functions and higher order functions like that."
>
> My answer is that this is just a simple but telling example. The issue (as I see it) exists in all situations involving generics and function types.
>
> I'm a regular programmer and I like to be able to write basic, useful abstractions.
> It's no fun when the language forces you to write lots of specific variants of your generic code.
>
> I would feel less worried about the parentheses situation if the language was going in a direction where you could see how this simple exercise would be a no brainer.
>
> Can Swift's parentheses-situation be sorted out before ABI stability?
> Otherwise it would be a bit like if Swift had kept the special rule for the first parameter, only much worse.
>
> /Jens
>
>
>
>
>> On Fri, Jun 9, 2017 at 7:17 PM, Gor Gyolchanyan <gor at gyolchanyan.com> wrote:
>> Yes, except why would you need to define `((A, B)) -> C`?, If you need to pass a 2-element tuple into a function that takes two parameters - you can! If you want to pass two values into a function that *looks* like it takes a single 2-element tuple - you can! Seems to me that the difference between `((A, B)) -> C` and `(A, B) -> C` is virtually non-existent. But keep in mind that this only works for bare tuples (the ones that can't have labels). Non-closure functions DO have labels, which is part of their signature, so this is a different story.
>>
>>>> On Jun 9, 2017, at 6:18 PM, Gwendal Roué <gwendal.roue at gmail.com> wrote:
>>>>
>>>>
>>>>> Le 9 juin 2017 à 17:12, Gor Gyolchanyan via swift-evolution <swift-evolution at swift.org> a écrit :
>>>>>
>>>>>
>>>>> So I wonder if any of you have had any thoughts about what Swift's parentheses-related future (or evolutionary baggage) will be?
>>>>>
>>>>
>>>> I really wish swift used the concept of tuples **exclusively** for all purposes that involve parentheses, as well as dividing tuples into two categories:
>>>> - Bare tuples, which do not have labels.
>>>> - Rich tuples, which do.
>>>> As a consequence, here's a list of statements that would become true:
>>>> - All functions take exactly one parameter, which is a tuple.
>>>> - All closures (a.k.a. function pointers) take exactly one parameter, which is a bare tuple.
>>>> - All functions return exactly one parameter, which is a tuple.
>>>> - Pattern matching is done on a single bare tuple using a single bare tuple pattern.
>>>>
>>>> The currently ongoing proposal to make a single-element tuple auto-flatten would work extremely well with this idea, by making all these changes completely backward-compatible.
>>>
>>> If I have well understood, Swift has evolved away from this.
>>>
>>> If what you describe were true, added to the fact that there is no such thing as a one-element tuple in the language, then (A,B) -> C and ((A, B)) -> C could not be distinguished, for the simple reason that ((A, B)) -> C could not be defined.
>>>
>>> For ((A, B)) -> C to be defined, we'd need a function that takes exactly one parameter, which is a tuple (your idea), whose single element is a tuple (oops, there is no single-valued tuples).
>>>
>>> No opinion here, just they way I have understood recent Swift history.
>>> Gwendal
>>>
>>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20170609/0aee6b72/attachment.html>
More information about the swift-evolution
mailing list