[Review] SE-0111: Remove type system significance of function argument labels

Language history, a.k.a. story time!

We started out with the “perfect” model of a function type being a map from a tuple to a tuple. Different argument labels were just overloads. It really was quite a simple model, other than not having 1-tuples. Well, and variadics and default values and trailing closures didn’t make sense anywhere but in functions, but still. Very simple.

(And inout. And autoclosure. And maybe a few more.)

Then we hit a snag: naming guidelines. We wanted argument labels to be something people felt comfortable using, something that would be encouraged over a sea of unlabeled arguments. But even before the Swift 3 naming conventions were hammered out, the natural names for argument labels didn’t seem to match the names you’d want to use in the function. So we split the names of parameters off from the names of tuple elements.

(This was precipitated by wanting to import Objective-C methods, but I think it would have come up regardless.)

As seen earlier in the thread, argument labels don’t make for good tuple element labels. Especially with the Swift 3 guidelines, argument labels usually don’t make sense without the context provided by the base name, and two methods that happen to share argument labels might not actually be very similar, while two methods that are duals of each other might have different argument labels due to, well, English (e.g. 'add(to:)' vs. 'remove(from:)’).

The real blow, however, came with that very first idea: that we could treat methods with different argument labels as simple overloads in type. This led to poor diagnostics where the compiler couldn’t decide whether to believe the types or the argument labels, and might tell you you have the wrong argument labels rather than a type mismatch. For pretty much every Apple API, this was the wrong decision. On top of all that, it was really hard to refer to a method when you didn’t want to call it. (Most methods with the same base name still have unique labels, so you don’t need the types to disambiguate.)

So we introduced the notion of “full names”, which are the things you see written as ‘move(from:to:)` (and which are represented by DeclName in the compiler). Almost immediately diagnostics got better, testing optional protocol requirements got shorter, and a lot of compiler implementation got simpler.

And then we kind of got stuck here. We have full names used throughout the compiler, but tuple labels still appear in types. They’re still used in mangling. We got rid of the “tuple splat” feature, but still model out-of-order arguments as “tuple shuffles”. And we allow a number of conversions that look like they should be invalid, but aren’t.

(And it’s important that we continue allowing them, or at least some of them, because we want to be able to pass existing functions to things like map and reduce without worrying about conflicting labels.)

So we’ve given up the perfect ideal of tuple-to-tuple. But we did it because we value other things more than that ideal: variadics, default values, trailing closures, inout, autoclosure, distinct argument labels and parameter names, referencing a function by full name, and diagnostics that better match the user’s likely intent (particularly given the naming guidelines and existing libraries). I think that’s a worthwhile trade.

Jordan

P.S. Anyone is allowed to think this is not a worthwhile trade! But part of the purpose of this story is to show that we’re already 90% of the way towards making tuples and function arguments completely separate, even if they have similar syntax. This proposal gets us to maybe 95%.

···

On Jun 30, 2016, at 13:36, Taras Zakharko via swift-evolution <swift-evolution@swift.org> wrote:

On 30 Jun 2016, at 22:11, Austin Zheng <austinzheng@gmail.com <mailto:austinzheng@gmail.com>> wrote:

As for the label semantics, Swift's current behavior is actively misleading, please see the example in the prior email. There are no meaningful semantics in the label, because implicit conversion between differently-labeled function types means that there is no way to usefully enforce these invariants to begin with.

That is a good point. I admit to not knowing this (I strongly expected that labels would be semantically meaningful). But what exactly is the status of the argument labels than in Swift? Just a documentation device for the programmer and a hint for the compiler to do function dispatch? But if the compiler indeed does dispatch on argument labels, then they are not completely void of semantics, are they? As I mentioned before, I think the problem here is much deeper.

The state of affairs I would prefer is something along these lines:

1. Labels are semantically meaningful
2. There is an explicit casting system for function signatures
3. This casting system should be in close correspondence to tuples. The "function argument lists look sort of like tuples“ is a very compelling reason actually, because of the principle of the least surprise. If I have two things in the language that look very similar, then its very confusing if they exhibit very different behaviour. Again, I am not proposing that one goes back to model functions in terms of tuples. But as long as there is a surface resemblance (and an obvious morphisms between the two), at least some aspects of their design should be kept in sync.

But again, this touches on some deep design decisions for the language, so I — as an amateur — don’t feel in my plate discussing this here. I believe that there currently might be some inconsistencies in the language design that should be sealed with (but maybe they are no inconsistencies at all and I simply have false expectations).

3 Likes

You might like the alternative I added (https://github.com/apple/swift-evolution/blob/master/proposals/0111-remove-arg-label-type-significance.md\). It basically goes the other way: keep the semantic significance of labels, and make them mean something by prohibiting converting between different function types with the same types but different labels.

Yes, thats what I would vote for! I would also add the possibility of explicitly converting between different labels, as long as the types match, e.g.:

battingAveragePredicate = sinkBattleship as ((ofHits: Int, forRuns: Int) -> Bool)

or

battingAveragePredicate = sinkBattleship as typeof(battlingAveragePredicate)

(whatever the correct syntax for this would be).

Best,

t.

···

On 30 Jun 2016, at 22:38, Austin Zheng <austinzheng@gmail.com> wrote:

On Thu, Jun 30, 2016 at 1:36 PM, Taras Zakharko <taras.zakharko@uzh.ch <mailto:taras.zakharko@uzh.ch>> wrote:

On 30 Jun 2016, at 22:11, Austin Zheng <austinzheng@gmail.com <mailto:austinzheng@gmail.com>> wrote:

As for the label semantics, Swift's current behavior is actively misleading, please see the example in the prior email. There are no meaningful semantics in the label, because implicit conversion between differently-labeled function types means that there is no way to usefully enforce these invariants to begin with.

That is a good point. I admit to not knowing this (I strongly expected that labels would be semantically meaningful). But what exactly is the status of the argument labels than in Swift? Just a documentation device for the programmer and a hint for the compiler to do function dispatch? But if the compiler indeed does dispatch on argument labels, then they are not completely void of semantics, are they? As I mentioned before, I think the problem here is much deeper.

The state of affairs I would prefer is something along these lines:

1. Labels are semantically meaningful
2. There is an explicit casting system for function signatures
3. This casting system should be in close correspondence to tuples. The "function argument lists look sort of like tuples“ is a very compelling reason actually, because of the principle of the least surprise. If I have two things in the language that look very similar, then its very confusing if they exhibit very different behaviour. Again, I am not proposing that one goes back to model functions in terms of tuples. But as long as there is a surface resemblance (and an obvious morphisms between the two), at least some aspects of their design should be kept in sync.

But again, this touches on some deep design decisions for the language, so I — as an amateur — don’t feel in my plate discussing this here. I believe that there currently might be some inconsistencies in the language design that should be sealed with (but maybe they are no inconsistencies at all and I simply have false expectations).

Best,

Taras

On Thu, Jun 30, 2016 at 12:42 PM, Taras Zakharko via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On 30 Jun 2016, at 21:20, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:

On Thu, Jun 30, 2016 at 2:14 PM, Taras Zakharko via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

> On 30 Jun 2016, at 20:26, Chris Lattner via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>
> Hello Swift community,
>
> The review of "SE-0111: Remove type system significance of function argument labels" begins now and runs through July 4. The proposal is available here:
>
> swift-evolution/0111-remove-arg-label-type-significance.md at master · apple/swift-evolution · GitHub
>
> * What is your evaluation of the proposal?

-1

> * Is the problem being addressed significant enough to warrant a change to Swift?

Yes, but I do not think that the proposed solution is the correct one. Rather, one should aim for designing a proper tuple type. Right now, tuples seem more like an afterthought even though they play a significant role in the language. Proper tuple casting/extensions/tuple algebra will solve the issues pointed out in this proposal, among other useful applications.

Taras, I don't believe this proposal touches tuples in any way. IIUC, argument lists are no longer tuples and have not been for a long time, and there is no intention on the part of the core team to return to that state of affairs.

Still, there is a clear correspondence between tuples and argument lists. I though that the model of functions as maps from tuples to tuples was very elegant, but I understand why this model was dropped. However tuples seem to be somehow misplaced right now, and this fact resonates through the entire language system (function types, enums, pattern matching etc.). I think one should stop and reconsider tuple status first to get a sense of a ‚grand strategy‘ for Swift. I am worried that small changes like this proposal attempt to deal with the ripples cast by a much deeper problem instead of the problem itself. As far as I am considered, there are two basic options. Either say, well, tuples were a nice idea, but it doesn’t really work out — and then consistently apply this to the entire language. Or, reinstate that tuples are a modelling construct on which a lot of language concepts are based and try to fix the underlaying limitations. Function arguments don’t need to be tuples formally. However, why not have a casting system that allows one to transform between function signatures and tuple types? That would certainly solve the deficients Swift is experiencing now as well as allow greater flexibility in the future.

And orthogonally to the tuple topic, I think that argument labels carry meaningful semantics and are much more than just cosmetic devices. Accepting this proposal would make the language weird. The deeper connection between the semantics of the argument list and the exposed type would be disturbed.

Hope any of this has made any sense :)

> * Does this proposal fit well with the feel and direction of Swift?

I do not believe so

> * If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?
> * How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

A glance

_______________________________________________
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

They already *are* type compatible. This works right now:

var a : (ofHits: Int, forRuns: Int) -> Bool = meetsBattingAverage a =
sinkBattleship // ??? a(ofHits: 1, forRuns: 2)

Your proposal does not make it clear that this works (which is
surprising to me).

I would argue the proposal the other way: that there should be an error
on line 2, and this should not be permitted implicitly.

I'm not sure if this is a definitely bad thing. Let's assume I have a number of funcs with similar but different labels and I want to define a function variable with another labels and assign one of these funcs:

func foo(one: Int, two: Int) {..}
func bar(first: Int, second: Int) {..}

var a : (alfa: Int, betta: Int) -> () = foo
a(alfa: 1, betta: 2)
a = bar
a(alfa: 1, betta: 2)

But the type of `a` is still (Int, Int) -> () the same as for `foo` and `bar`

···

On 30.06.2016 21:56, Scott James Remnant via swift-evolution wrote:

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

Great story! What gets us from 95% to 100%? :)

···

On 1 Jul 2016, at 05:33, Jordan Rose via swift-evolution <swift-evolution@swift.org> wrote:

On Jun 30, 2016, at 13:36, Taras Zakharko via swift-evolution <swift-evolution@swift.org> wrote:

On 30 Jun 2016, at 22:11, Austin Zheng <austinzheng@gmail.com> wrote:

As for the label semantics, Swift's current behavior is actively misleading, please see the example in the prior email. There are no meaningful semantics in the label, because implicit conversion between differently-labeled function types means that there is no way to usefully enforce these invariants to begin with.

That is a good point. I admit to not knowing this (I strongly expected that labels would be semantically meaningful). But what exactly is the status of the argument labels than in Swift? Just a documentation device for the programmer and a hint for the compiler to do function dispatch? But if the compiler indeed does dispatch on argument labels, then they are not completely void of semantics, are they? As I mentioned before, I think the problem here is much deeper.

The state of affairs I would prefer is something along these lines:

1. Labels are semantically meaningful
2. There is an explicit casting system for function signatures
3. This casting system should be in close correspondence to tuples. The "function argument lists look sort of like tuples“ is a very compelling reason actually, because of the principle of the least surprise. If I have two things in the language that look very similar, then its very confusing if they exhibit very different behaviour. Again, I am not proposing that one goes back to model functions in terms of tuples. But as long as there is a surface resemblance (and an obvious morphisms between the two), at least some aspects of their design should be kept in sync.

But again, this touches on some deep design decisions for the language, so I — as an amateur — don’t feel in my plate discussing this here. I believe that there currently might be some inconsistencies in the language design that should be sealed with (but maybe they are no inconsistencies at all and I simply have false expectations).

Language history, a.k.a. story time!

We started out with the “perfect” model of a function type being a map from a tuple to a tuple. Different argument labels were just overloads. It really was quite a simple model, other than not having 1-tuples. Well, and variadics and default values and trailing closures didn’t make sense anywhere but in functions, but still. Very simple.

(And inout. And autoclosure. And maybe a few more.)

Then we hit a snag: naming guidelines. We wanted argument labels to be something people felt comfortable using, something that would be encouraged over a sea of unlabeled arguments. But even before the Swift 3 naming conventions were hammered out, the natural names for argument labels didn’t seem to match the names you’d want to use in the function. So we split the names of parameters off from the names of tuple elements.

(This was precipitated by wanting to import Objective-C methods, but I think it would have come up regardless.)

As seen earlier in the thread, argument labels don’t make for good tuple element labels. Especially with the Swift 3 guidelines, argument labels usually don’t make sense without the context provided by the base name, and two methods that happen to share argument labels might not actually be very similar, while two methods that are duals of each other might have different argument labels due to, well, English (e.g. 'add(to:)' vs. 'remove(from:)’).

The real blow, however, came with that very first idea: that we could treat methods with different argument labels as simple overloads in type. This led to poor diagnostics where the compiler couldn’t decide whether to believe the types or the argument labels, and might tell you you have the wrong argument labels rather than a type mismatch. For pretty much every Apple API, this was the wrong decision. On top of all that, it was really hard to refer to a method when you didn’t want to call it. (Most methods with the same base name still have unique labels, so you don’t need the types to disambiguate.)

So we introduced the notion of “full names”, which are the things you see written as ‘move(from:to:)` (and which are represented by DeclName in the compiler). Almost immediately diagnostics got better, testing optional protocol requirements got shorter, and a lot of compiler implementation got simpler.

And then we kind of got stuck here. We have full names used throughout the compiler, but tuple labels still appear in types. They’re still used in mangling. We got rid of the “tuple splat” feature, but still model out-of-order arguments as “tuple shuffles”. And we allow a number of conversions that look like they should be invalid, but aren’t.

(And it’s important that we continue allowing them, or at least some of them, because we want to be able to pass existing functions to things like map and reduce without worrying about conflicting labels.)

So we’ve given up the perfect ideal of tuple-to-tuple. But we did it because we value other things more than that ideal: variadics, default values, trailing closures, inout, autoclosure, distinct argument labels and parameter names, referencing a function by full name, and diagnostics that better match the user’s likely intent (particularly given the naming guidelines and existing libraries). I think that’s a worthwhile trade.

Jordan

P.S. Anyone is allowed to think this is not a worthwhile trade! But part of the purpose of this story is to show that we’re already 90% of the way towards making tuples and function arguments completely separate, even if they have similar syntax. This proposal gets us to maybe 95%.

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

Jordan, Thanks for the very insightful explanation! It all makes a lot of sense in perspective.

Apparently I was thinking about this issue a bit while I was sleeping, and now it seems to me that part of the problem is because one strives for the function signature to be linguistically meaningful (as in natural language meaningful). It was mentioned that argument labels often don’t make much sense when detached from the function name, and that some function seem to have semantically compatible signatures and some don’t, e.g. the example of drawLineTo(x: Float, y: Float) and drawLineWith(angle: Float, distance: Float)

So this is actually a linguistic problem. More precisely, its a problem of predicate frames/argument semantics. Consider verbs like ‚hit' and ‚kill'. In term of precise semantics, we can model them as something like

kill(killer:victim:)
hit(hitter:victim:)

However, the actual status of the arguments here is compatible. Both verbs describe asymmetric actions, with one argument playing an active role in (negatively) changing the state of the other , passive one. So both are compatible with a broad metatype (what we often call predicate type in linguistics)

P(agent:undergoer:)

However, there are plenty of verbs that are quite different. Like go(person:to). Also two arguments, but the semantic status of them is very different.

If that is really the case, then there is indeed no general solution which keeps the argument labels semantically meaningful. But there is still a potential conflict with the tuple/function signature labels.
The only reasonable choice that comes to my mind is to completely drop argument labels in closure variables and just look at the argument type. Basically, by assigning function variables, we drop any semantics and just look at the overall structure (cardinality/argument types)

In more detail:

1. Maintain that argument labels are not part of the type, but merely hints for the function dispatch/programmer convenience
2. Disallow argument labels in closure variables. I.e.:

    var fun = drawLineTo(x:y:)

   has type (Float, Float) -> (), which makes the variable explicitly compatible with any function of cardinality 2 that takes Float arguments. At the sae time, any declaration like

    var fun : (x: Float, y: Float) -> ()

   should be illegal. This further makes clear that tuples and functions are not the same thing.

3. In the long term, maybe consider removing labels from tuples. At the same time, there should be a new mechanism in place that allows one to elevate tuples to structs, with properties taking the role of the current tuple labels (like Python’s namedtuple)

An alternative, in order to keep the cake and eat it too, would be something along the lines of making predicate types explicit. E.g. creating some sort of function ‚semantic metatype‘ and declaring function conformance to this metatype. But I can’t imagine that it is a good idea. Natural languages are really not a good source of inspiration in this regard.

Best,

Taras

···

On 01 Jul 2016, at 05:33, Jordan Rose <jordan_rose@apple.com> wrote:

On Jun 30, 2016, at 13:36, Taras Zakharko via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On 30 Jun 2016, at 22:11, Austin Zheng <austinzheng@gmail.com <mailto:austinzheng@gmail.com>> wrote:

As for the label semantics, Swift's current behavior is actively misleading, please see the example in the prior email. There are no meaningful semantics in the label, because implicit conversion between differently-labeled function types means that there is no way to usefully enforce these invariants to begin with.

That is a good point. I admit to not knowing this (I strongly expected that labels would be semantically meaningful). But what exactly is the status of the argument labels than in Swift? Just a documentation device for the programmer and a hint for the compiler to do function dispatch? But if the compiler indeed does dispatch on argument labels, then they are not completely void of semantics, are they? As I mentioned before, I think the problem here is much deeper.

The state of affairs I would prefer is something along these lines:

1. Labels are semantically meaningful
2. There is an explicit casting system for function signatures
3. This casting system should be in close correspondence to tuples. The "function argument lists look sort of like tuples“ is a very compelling reason actually, because of the principle of the least surprise. If I have two things in the language that look very similar, then its very confusing if they exhibit very different behaviour. Again, I am not proposing that one goes back to model functions in terms of tuples. But as long as there is a surface resemblance (and an obvious morphisms between the two), at least some aspects of their design should be kept in sync.

But again, this touches on some deep design decisions for the language, so I — as an amateur — don’t feel in my plate discussing this here. I believe that there currently might be some inconsistencies in the language design that should be sealed with (but maybe they are no inconsistencies at all and I simply have false expectations).

Language history, a.k.a. story time!

We started out with the “perfect” model of a function type being a map from a tuple to a tuple. Different argument labels were just overloads. It really was quite a simple model, other than not having 1-tuples. Well, and variadics and default values and trailing closures didn’t make sense anywhere but in functions, but still. Very simple.

(And inout. And autoclosure. And maybe a few more.)

Then we hit a snag: naming guidelines. We wanted argument labels to be something people felt comfortable using, something that would be encouraged over a sea of unlabeled arguments. But even before the Swift 3 naming conventions were hammered out, the natural names for argument labels didn’t seem to match the names you’d want to use in the function. So we split the names of parameters off from the names of tuple elements.

(This was precipitated by wanting to import Objective-C methods, but I think it would have come up regardless.)

As seen earlier in the thread, argument labels don’t make for good tuple element labels. Especially with the Swift 3 guidelines, argument labels usually don’t make sense without the context provided by the base name, and two methods that happen to share argument labels might not actually be very similar, while two methods that are duals of each other might have different argument labels due to, well, English (e.g. 'add(to:)' vs. 'remove(from:)’).

The real blow, however, came with that very first idea: that we could treat methods with different argument labels as simple overloads in type. This led to poor diagnostics where the compiler couldn’t decide whether to believe the types or the argument labels, and might tell you you have the wrong argument labels rather than a type mismatch. For pretty much every Apple API, this was the wrong decision. On top of all that, it was really hard to refer to a method when you didn’t want to call it. (Most methods with the same base name still have unique labels, so you don’t need the types to disambiguate.)

So we introduced the notion of “full names”, which are the things you see written as ‘move(from:to:)` (and which are represented by DeclName in the compiler). Almost immediately diagnostics got better, testing optional protocol requirements got shorter, and a lot of compiler implementation got simpler.

And then we kind of got stuck here. We have full names used throughout the compiler, but tuple labels still appear in types. They’re still used in mangling. We got rid of the “tuple splat” feature, but still model out-of-order arguments as “tuple shuffles”. And we allow a number of conversions that look like they should be invalid, but aren’t.

(And it’s important that we continue allowing them, or at least some of them, because we want to be able to pass existing functions to things like map and reduce without worrying about conflicting labels.)

So we’ve given up the perfect ideal of tuple-to-tuple. But we did it because we value other things more than that ideal: variadics, default values, trailing closures, inout, autoclosure, distinct argument labels and parameter names, referencing a function by full name, and diagnostics that better match the user’s likely intent (particularly given the naming guidelines and existing libraries). I think that’s a worthwhile trade.

Jordan

P.S. Anyone is allowed to think this is not a worthwhile trade! But part of the purpose of this story is to show that we’re already 90% of the way towards making tuples and function arguments completely separate, even if they have similar syntax. This proposal gets us to maybe 95%.

As with the original proposal, your examples here don't obey the Naming Guidelines.

If we're going to pretend that Swift is C, we wouldn't have a distinction between argument labels and parameter names in the first place!

Is there an example of two functions, obeying the naming guidelines, which have different argument labels (not parameter names) for parameters of the same type, where it makes sense to cross-call them?

Scott

···

On Jun 30, 2016, at 1:11 PM, Vladimir.S <svabox@gmail.com> wrote:

On 30.06.2016 21:56, Scott James Remnant via swift-evolution wrote:

They already *are* type compatible. This works right now:

var a : (ofHits: Int, forRuns: Int) -> Bool = meetsBattingAverage a =
sinkBattleship // ??? a(ofHits: 1, forRuns: 2)

Your proposal does not make it clear that this works (which is
surprising to me).

I would argue the proposal the other way: that there should be an error
on line 2, and this should not be permitted implicitly.

I'm not sure if this is a definitely bad thing. Let's assume I have a number of funcs with similar but different labels and I want to define a function variable with another labels and assign one of these funcs:

func foo(one: Int, two: Int) {..}
func bar(first: Int, second: Int) {..}

var a : (alfa: Int, betta: Int) -> () = foo
a(alfa: 1, betta: 2)
a = bar
a(alfa: 1, betta: 2)

But the type of `a` is still (Int, Int) -> () the same as for `foo` and `bar`

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

As with the original proposal, your examples here don't obey the Naming
Guidelines.

If we're going to pretend that Swift is C, we wouldn't have a distinction
between argument labels and parameter names in the first place!

Is there an example of two functions, obeying the naming guidelines, which
have different argument labels (not parameter names) for parameters of the
same type, where it makes sense to cross-call them?

Sure there are. For example, `adding(_:)` and `multiplied(by:)` in the new
Integer protocol.

···

On Thu, Jun 30, 2016 at 3:30 PM, Scott James Remnant via swift-evolution < swift-evolution@swift.org> wrote:

Scott

> On Jun 30, 2016, at 1:11 PM, Vladimir.S <svabox@gmail.com> wrote:
>
> On 30.06.2016 21:56, Scott James Remnant via swift-evolution wrote:
>>> They already *are* type compatible. This works right now:
>>>
>>> var a : (ofHits: Int, forRuns: Int) -> Bool = meetsBattingAverage a =
>>> sinkBattleship // ??? a(ofHits: 1, forRuns: 2)
>>
>> Your proposal does not make it clear that this works (which is
>> surprising to me).
>>
>> I would argue the proposal the other way: that there should be an error
>> on line 2, and this should not be permitted implicitly.
>
> I'm not sure if this is a definitely bad thing. Let's assume I have a
number of funcs with similar but different labels and I want to define a
function variable with another labels and assign one of these funcs:
>
> func foo(one: Int, two: Int) {..}
> func bar(first: Int, second: Int) {..}
>
> var a : (alfa: Int, betta: Int) -> () = foo
> a(alfa: 1, betta: 2)
> a = bar
> a(alfa: 1, betta: 2)
>
> But the type of `a` is still (Int, Int) -> () the same as for `foo` and
`bar`
>
>>
>> Scott _______________________________________________ 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

As an addendum to this:

If it’s important that the variable be invoked with argument labels, that can be provided by having argument labels as part of the variable’s name:

func foo(a: A, b: B) -> C
func bar(from: A, using: B) -> C
// ...
let myA : A = …
let myB : B = ...
var higherOrderABToC(with:until:) : (A, B) -> C = foo(a:b:)
var myC = higherOrderABToC(with: myA, until: myB)
higherOrderABToC = bar(from:using:)
myC = higherOrderABToC(with: myA, until: myB)

···

On Jun 30, 2016, at 4:16 PM, Michael Ilseman via swift-evolution <swift-evolution@swift.org> wrote:

I didn’t see the pre-proposal, and I apologize if this has come up elsewhere on this list, but did you consider the alternative where you must use the compound name to refer to a function? That is:

func foo(a: A, b: B) -> C
func bar(from: A, using: B) -> C
let higherOrderABToC: (A, B) -> C = foo(a:b:)
higherOrderABToC = bar(from:using:)

That is, “foo” by itself is an incomplete name, and you must provide the full compound name. This will penalize convenience of references to functions, though you will simplify the overloading logic of functions: two functions are not overloaded if their full compound names differ. This can be thought of as taking SE0021 and running wild, enforcing that all naming of functions use the compound name.

The notion of providing the name of a function with a full compound name is increasingly common in Swift, with SE0021 generalized naming, improvements to swift_name, SE0044 import-as-member, etc.

To modify the example from later in the proposal:

func doSomething(x: Int, y: Int) -> Bool {
    return x == y
}

func somethingElse(a: Int, b: Int) -> Bool {
    return a > b
}

// fn2's type is (Int, Int) -> Bool
var fn2 = doSomething(x:y:)

// Okay
fn2(1, 2)

// Okay
fn2 = somethingElse(a:b:)

The notion here, is that we’re not just dropping argument labels from the type system, but instead we’re hoisting such concerns into the syntax by making them an intrinsic part of the name. Then, if you bind to it with a value, that value of course wouldn’t be called with labels because labels are not part of its name.

If this is unwieldy, then it’s worth stating how this behaves in the presence of functions overloaded based on argument label alone, as Erica mentions.

On Jun 30, 2016, at 3:43 PM, Austin Zheng <austinzheng@gmail.com <mailto:austinzheng@gmail.com>> wrote:

I would think that the naming guidelines are an argument for reducing the role of argument labels, if any.

This labeled tuple makes sense, because 'x' and 'y' describe the semantic meaning of each element (Int which represents an x-coordinate):

let myTuple: (x: Int, y: Int)

This also makes sense, because 'hits' and 'runs' describe the semantic meaning of each argument (Int which represents number of hits):

let a : (hits: Int, runs: Int) -> Bool

This makes absolutely no sense to me:

let b: (ofHits: Int, forRuns: Int) -> Bool

In fact, 'b' is a better example than the average, because of the naming guideline point to spell out the semantics of weakly typed arguments in the argument label.

func getWidget(for provider: Provider, with manifest: ShippingManifest) -> Widget { /* ... */ }

let widgetGetter : (for: Provider, with: ShippingManifest) = getWidget

At this point, the labels are completely superfluous. They make no sense except in the context of a method name, because they are prepositional phrases. Knowing that the Provider is "for" something and something does something "with" the ShippingManifest is absolutely useless to anyone reading the code where the method name those labels are part of isn't immediately obvious.

Austin

On Thu, Jun 30, 2016 at 3:33 PM, Michael Ilseman via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Jun 30, 2016, at 11:43 AM, Scott James Remnant via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

-1

This proposal doesn’t even use Swift naming style to make its point, as soon as you do, the reason why Swift considers argument labels to be part of the type signature becomes apparent.

The author of the proposal uses the following example:

func doSomething(x: Int, y: Int) -> Bool

This is just not Swift-y, a much better example would be:

func sinkBattleship(atX x: Int, y: Int) -> Bool

<pedanticism>
If you want to talk about pedantic following of API naming guidelines for example code, then I believe that your example also runs afoul. It would be:

func sinkBattleshipAt(x: Int, y: Int) -> Bool

Due to a special case where the preposition covers multiple arguments. This arrises mostly from flatten-ed structs as parameters, e.g. from old C APIs predating struct literal syntax. See:

An exception arises when the first two arguments represent parts of a single abstraction.

a.move(toX: b, y: c)
a.fade(fromRed: b, green: c, blue: d)
In such cases, begin the argument label after the preposition, to keep the abstraction clear.

a.moveTo(x: b, y: c)
a.fadeFrom(red: b, green: c, blue: d)

</pedanticism>

the proposal states that the argument labels be then stripped from the type, which would make this method type-compatible with:

func meetsBattingAverage(ofHits hits: Int, forRuns runs: Int) -> Bool

I don’t think it’s desirable for this to work at all… Argument labels are not parameter names, they are a first class part of Swift’s type system, and always meaningful when employed properly.

Scott
_______________________________________________
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

Is there an example of two functions, obeying the naming guidelines, which have different argument labels (not parameter names) for parameters of the same type, where it makes sense to cross-call them?

Sure there are. For example, `adding(_:)` and `multiplied(by:)` in the new Integer protocol.

These have quite different results!

I’m pretty convinced that Swift shouldn’t implicitly allow you to assign multiplied(by:) to the same member as you could assign adding(_:) - explicit is fine, with some syntax e.g. through a closure, but not implicit!

To correct my goofy pre-coffee not-quite-following-the-naming-guidelines suggestion, that would be equivalent of allowing these two to be interchangeable:

  func drawLineTo(x: Float, y: Float)
  func drawLineWith(angle: Float, distance: Float)

Which is clearly not something you would expect to be allowed implicitly.

I’m definitely for the concept of the proposal that this is inconsistent, but I’m also definitely for making this explicitly disallowed everywhere, not allowed everywhere.

I think Erica makes a good point about syntax too, we’ve been assuming:

  // compatible types
  var x = drawLineTo
  x = drawLineWith

but really this isn’t Swifty syntax, the full argument labels provide more clarity here; and I’d even be in favor of disallowing the above shortening.

Scott

Is there an example of two functions, obeying the naming guidelines, which

have different argument labels (not parameter names) for parameters of the
same type, where it makes sense to cross-call them?

Sure there are. For example, `adding(_:)` and `multiplied(by:)` in the new
Integer protocol.

These have quite different results!

Of course they have different results. What's the point of two different
functions that yield the same results? I don't see your point here.
Anywhere I can supply `adding(_:)` as a predicate, I expect to be able to
supply `multiplied(by:)`. Both of these methods take one number and return
another; there is nothing about the label `by` inherent to what the latter
method does; it's entirely an accident of the English language.

I’m pretty convinced that Swift shouldn’t implicitly allow you to assign
multiplied(by:) to the same member as you could assign adding(_:) -
explicit is fine, with some syntax e.g. through a closure, but not implicit!

But adding(_:) and subtracting(_:) are OK? The point is, with Swift
guidelines, argument labels can differ due to the vagaries of the English
language; it is not a proxy for whether two functions are "not very
different," "somewhat different," or "very different."

To correct my goofy pre-coffee not-quite-following-the-naming-guidelines
suggestion, that would be equivalent of allowing these two to be
interchangeable:

  func drawLineTo(x: Float, y: Float)
  func drawLineWith(angle: Float, distance: Float)

Which is clearly not something you would expect to be allowed implicitly.

Why not? It is not clear to me.

···

On Thu, Jun 30, 2016 at 7:02 PM, Scott James Remnant <scott@netsplit.com> wrote:

I’m definitely for the concept of the proposal that this is inconsistent,
but I’m also definitely for making this explicitly disallowed everywhere,
not allowed everywhere.

I think Erica makes a good point about syntax too, we’ve been assuming:

  // compatible types
  var x = drawLineTo
  x = drawLineWith

but really this isn’t Swifty syntax, the full argument labels provide more
clarity here; and I’d even be in favor of disallowing the above shortening.

Scott

Mostly I was just hedging my bets, but the main one I can think of is that enum cases still use tuples, which means they don’t have distinct argument labels and preserve the labels in switches. There’s also a bunch of implementation improvements we can make (like fixing mangling and printing of function types), but that’s not something swift-evolution cares about so much.

There are also some related things about how function arguments work that are probably better done the further we get here:
- Forwarding variadic parameters
- Generalized argument forwarding? That works with inout?
- Variadic generics

Someday!
Jordan

···

On Jun 30, 2016, at 23:18, David Hart <david@hartbit.com> wrote:

Great story! What gets us from 95% to 100%? :)

On 1 Jul 2016, at 05:33, Jordan Rose via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Jun 30, 2016, at 13:36, Taras Zakharko via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On 30 Jun 2016, at 22:11, Austin Zheng <austinzheng@gmail.com <mailto:austinzheng@gmail.com>> wrote:

As for the label semantics, Swift's current behavior is actively misleading, please see the example in the prior email. There are no meaningful semantics in the label, because implicit conversion between differently-labeled function types means that there is no way to usefully enforce these invariants to begin with.

That is a good point. I admit to not knowing this (I strongly expected that labels would be semantically meaningful). But what exactly is the status of the argument labels than in Swift? Just a documentation device for the programmer and a hint for the compiler to do function dispatch? But if the compiler indeed does dispatch on argument labels, then they are not completely void of semantics, are they? As I mentioned before, I think the problem here is much deeper.

The state of affairs I would prefer is something along these lines:

1. Labels are semantically meaningful
2. There is an explicit casting system for function signatures
3. This casting system should be in close correspondence to tuples. The "function argument lists look sort of like tuples“ is a very compelling reason actually, because of the principle of the least surprise. If I have two things in the language that look very similar, then its very confusing if they exhibit very different behaviour. Again, I am not proposing that one goes back to model functions in terms of tuples. But as long as there is a surface resemblance (and an obvious morphisms between the two), at least some aspects of their design should be kept in sync.

But again, this touches on some deep design decisions for the language, so I — as an amateur — don’t feel in my plate discussing this here. I believe that there currently might be some inconsistencies in the language design that should be sealed with (but maybe they are no inconsistencies at all and I simply have false expectations).

Language history, a.k.a. story time!

We started out with the “perfect” model of a function type being a map from a tuple to a tuple. Different argument labels were just overloads. It really was quite a simple model, other than not having 1-tuples. Well, and variadics and default values and trailing closures didn’t make sense anywhere but in functions, but still. Very simple.

(And inout. And autoclosure. And maybe a few more.)

Then we hit a snag: naming guidelines. We wanted argument labels to be something people felt comfortable using, something that would be encouraged over a sea of unlabeled arguments. But even before the Swift 3 naming conventions were hammered out, the natural names for argument labels didn’t seem to match the names you’d want to use in the function. So we split the names of parameters off from the names of tuple elements.

(This was precipitated by wanting to import Objective-C methods, but I think it would have come up regardless.)

As seen earlier in the thread, argument labels don’t make for good tuple element labels. Especially with the Swift 3 guidelines, argument labels usually don’t make sense without the context provided by the base name, and two methods that happen to share argument labels might not actually be very similar, while two methods that are duals of each other might have different argument labels due to, well, English (e.g. 'add(to:)' vs. 'remove(from:)’).

The real blow, however, came with that very first idea: that we could treat methods with different argument labels as simple overloads in type. This led to poor diagnostics where the compiler couldn’t decide whether to believe the types or the argument labels, and might tell you you have the wrong argument labels rather than a type mismatch. For pretty much every Apple API, this was the wrong decision. On top of all that, it was really hard to refer to a method when you didn’t want to call it. (Most methods with the same base name still have unique labels, so you don’t need the types to disambiguate.)

So we introduced the notion of “full names”, which are the things you see written as ‘move(from:to:)` (and which are represented by DeclName in the compiler). Almost immediately diagnostics got better, testing optional protocol requirements got shorter, and a lot of compiler implementation got simpler.

And then we kind of got stuck here. We have full names used throughout the compiler, but tuple labels still appear in types. They’re still used in mangling. We got rid of the “tuple splat” feature, but still model out-of-order arguments as “tuple shuffles”. And we allow a number of conversions that look like they should be invalid, but aren’t.

(And it’s important that we continue allowing them, or at least some of them, because we want to be able to pass existing functions to things like map and reduce without worrying about conflicting labels.)

So we’ve given up the perfect ideal of tuple-to-tuple. But we did it because we value other things more than that ideal: variadics, default values, trailing closures, inout, autoclosure, distinct argument labels and parameter names, referencing a function by full name, and diagnostics that better match the user’s likely intent (particularly given the naming guidelines and existing libraries). I think that’s a worthwhile trade.

Jordan

P.S. Anyone is allowed to think this is not a worthwhile trade! But part of the purpose of this story is to show that we’re already 90% of the way towards making tuples and function arguments completely separate, even if they have similar syntax. This proposal gets us to maybe 95%.

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

Jordan, Thanks for the very insightful explanation! It all makes a lot of sense in perspective.

Apparently I was thinking about this issue a bit while I was sleeping, and now it seems to me that part of the problem is because one strives for the function signature to be linguistically meaningful (as in natural language meaningful). It was mentioned that argument labels often don’t make much sense when detached from the function name, and that some function seem to have semantically compatible signatures and some don’t, e.g. the example of drawLineTo(x: Float, y: Float) and drawLineWith(angle: Float, distance: Float)

So this is actually a linguistic problem. More precisely, its a problem of predicate frames/argument semantics. Consider verbs like ‚hit' and ‚kill'. In term of precise semantics, we can model them as something like

kill(killer:victim:)
hit(hitter:victim:)

However, the actual status of the arguments here is compatible. Both verbs describe asymmetric actions, with one argument playing an active role in (negatively) changing the state of the other , passive one. So both are compatible with a broad metatype (what we often call predicate type in linguistics)

P(agent:undergoer:)

However, there are plenty of verbs that are quite different. Like go(person:to). Also two arguments, but the semantic status of them is very different.

If that is really the case, then there is indeed no general solution which keeps the argument labels semantically meaningful. But there is still a potential conflict with the tuple/function signature labels.
The only reasonable choice that comes to my mind is to completely drop argument labels in closure variables and just look at the argument type. Basically, by assigning function variables, we drop any semantics and just look at the overall structure (cardinality/argument types)

In more detail:

1. Maintain that argument labels are not part of the type, but merely hints for the function dispatch/programmer convenience

They are not merely hints, they are part of the full name of the function. Functions with different names are different functions, even if they share the same base name. That is, they are syntactically meaningful.

···

On Jul 1, 2016, at 1:30 AM, Taras Zakharko via swift-evolution <swift-evolution@swift.org> wrote:

2. Disallow argument labels in closure variables. I.e.:

    var fun = drawLineTo(x:y:)

   has type (Float, Float) -> (), which makes the variable explicitly compatible with any function of cardinality 2 that takes Float arguments. At the sae time, any declaration like

    var fun : (x: Float, y: Float) -> ()

   should be illegal. This further makes clear that tuples and functions are not the same thing.

3. In the long term, maybe consider removing labels from tuples. At the same time, there should be a new mechanism in place that allows one to elevate tuples to structs, with properties taking the role of the current tuple labels (like Python’s namedtuple)

An alternative, in order to keep the cake and eat it too, would be something along the lines of making predicate types explicit. E.g. creating some sort of function ‚semantic metatype‘ and declaring function conformance to this metatype. But I can’t imagine that it is a good idea. Natural languages are really not a good source of inspiration in this regard.

Best,

Taras

On 01 Jul 2016, at 05:33, Jordan Rose <jordan_rose@apple.com <mailto:jordan_rose@apple.com>> wrote:

On Jun 30, 2016, at 13:36, Taras Zakharko via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On 30 Jun 2016, at 22:11, Austin Zheng <austinzheng@gmail.com <mailto:austinzheng@gmail.com>> wrote:

As for the label semantics, Swift's current behavior is actively misleading, please see the example in the prior email. There are no meaningful semantics in the label, because implicit conversion between differently-labeled function types means that there is no way to usefully enforce these invariants to begin with.

That is a good point. I admit to not knowing this (I strongly expected that labels would be semantically meaningful). But what exactly is the status of the argument labels than in Swift? Just a documentation device for the programmer and a hint for the compiler to do function dispatch? But if the compiler indeed does dispatch on argument labels, then they are not completely void of semantics, are they? As I mentioned before, I think the problem here is much deeper.

The state of affairs I would prefer is something along these lines:

1. Labels are semantically meaningful
2. There is an explicit casting system for function signatures
3. This casting system should be in close correspondence to tuples. The "function argument lists look sort of like tuples“ is a very compelling reason actually, because of the principle of the least surprise. If I have two things in the language that look very similar, then its very confusing if they exhibit very different behaviour. Again, I am not proposing that one goes back to model functions in terms of tuples. But as long as there is a surface resemblance (and an obvious morphisms between the two), at least some aspects of their design should be kept in sync.

But again, this touches on some deep design decisions for the language, so I — as an amateur — don’t feel in my plate discussing this here. I believe that there currently might be some inconsistencies in the language design that should be sealed with (but maybe they are no inconsistencies at all and I simply have false expectations).

Language history, a.k.a. story time!

We started out with the “perfect” model of a function type being a map from a tuple to a tuple. Different argument labels were just overloads. It really was quite a simple model, other than not having 1-tuples. Well, and variadics and default values and trailing closures didn’t make sense anywhere but in functions, but still. Very simple.

(And inout. And autoclosure. And maybe a few more.)

Then we hit a snag: naming guidelines. We wanted argument labels to be something people felt comfortable using, something that would be encouraged over a sea of unlabeled arguments. But even before the Swift 3 naming conventions were hammered out, the natural names for argument labels didn’t seem to match the names you’d want to use in the function. So we split the names of parameters off from the names of tuple elements.

(This was precipitated by wanting to import Objective-C methods, but I think it would have come up regardless.)

As seen earlier in the thread, argument labels don’t make for good tuple element labels. Especially with the Swift 3 guidelines, argument labels usually don’t make sense without the context provided by the base name, and two methods that happen to share argument labels might not actually be very similar, while two methods that are duals of each other might have different argument labels due to, well, English (e.g. 'add(to:)' vs. 'remove(from:)’).

The real blow, however, came with that very first idea: that we could treat methods with different argument labels as simple overloads in type. This led to poor diagnostics where the compiler couldn’t decide whether to believe the types or the argument labels, and might tell you you have the wrong argument labels rather than a type mismatch. For pretty much every Apple API, this was the wrong decision. On top of all that, it was really hard to refer to a method when you didn’t want to call it. (Most methods with the same base name still have unique labels, so you don’t need the types to disambiguate.)

So we introduced the notion of “full names”, which are the things you see written as ‘move(from:to:)` (and which are represented by DeclName in the compiler). Almost immediately diagnostics got better, testing optional protocol requirements got shorter, and a lot of compiler implementation got simpler.

And then we kind of got stuck here. We have full names used throughout the compiler, but tuple labels still appear in types. They’re still used in mangling. We got rid of the “tuple splat” feature, but still model out-of-order arguments as “tuple shuffles”. And we allow a number of conversions that look like they should be invalid, but aren’t.

(And it’s important that we continue allowing them, or at least some of them, because we want to be able to pass existing functions to things like map and reduce without worrying about conflicting labels.)

So we’ve given up the perfect ideal of tuple-to-tuple. But we did it because we value other things more than that ideal: variadics, default values, trailing closures, inout, autoclosure, distinct argument labels and parameter names, referencing a function by full name, and diagnostics that better match the user’s likely intent (particularly given the naming guidelines and existing libraries). I think that’s a worthwhile trade.

Jordan

P.S. Anyone is allowed to think this is not a worthwhile trade! But part of the purpose of this story is to show that we’re already 90% of the way towards making tuples and function arguments completely separate, even if they have similar syntax. This proposal gets us to maybe 95%.

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

* What is your evaluation of the proposal?
a reluctant +1
        * Is the problem being addressed significant enough to warrant a
change to Swift?
Yes.
        * Does this proposal fit well with the feel and direction of Swift?
Yes.
        * If you have used other languages or libraries with a similar
feature, how do you feel that this proposal compares to those?
It works well enough
        * How much effort did you put into your review? A glance, a quick
reading, or an in-depth study?
Read a few times over.

The point that actually tipped my opinion came from Jordan Rose " Argument
labels are definitely part of the *name* of the function, but they aren’t
part of the *type*. A few functions happen to have argument labels "

I am of the general opinion that argument labels are part of the type,
which helps explain `stride(to:by:)` vs `stride(through:by:)` in terms of
overloading instead of name lookup. While this proposal would render that
explanation invalid, I realize that I was conflating the ideas of name and
type. I wish we could have the tuple to tuple model but I see how it isn't
tractable when combined with other features.

···

On Fri, Jul 1, 2016 at 12:56 PM, Michael Ilseman via swift-evolution < swift-evolution@swift.org> wrote:

On Jul 1, 2016, at 1:30 AM, Taras Zakharko via swift-evolution < > swift-evolution@swift.org> wrote:

Jordan, Thanks for the very insightful explanation! It all makes a lot of
sense in perspective.

Apparently I was thinking about this issue a bit while I was sleeping, and
now it seems to me that part of the problem is because one strives for the
function signature to be linguistically meaningful (as in natural language
meaningful). It was mentioned that argument labels often don’t make much
sense when detached from the function name, and that some function seem to
have semantically compatible signatures and some don’t, e.g. the example of
drawLineTo(x: Float, y: Float) and drawLineWith(angle: Float, distance:
Float)

So this is actually a linguistic problem. More precisely, its a problem of
predicate frames/argument semantics. Consider verbs like ‚hit' and ‚kill'.
In term of precise semantics, we can model them as something like

kill(killer:victim:)
hit(hitter:victim:)

However, the actual status of the arguments here is compatible. Both verbs
describe asymmetric actions, with one argument playing an active role in
(negatively) changing the state of the other , passive one. So both are
compatible with a broad metatype (what we often call predicate type in
linguistics)

P(agent:undergoer:)

However, there are plenty of verbs that are quite different. Like
go(person:to). Also two arguments, but the semantic status of them is very
different.

If that is really the case, then there is indeed no general solution which
keeps the argument labels semantically meaningful. But there is still a
potential conflict with the tuple/function signature labels.
The only reasonable choice that comes to my mind is to completely drop
argument labels in closure variables and just look at the argument type.
Basically, by assigning function variables, we drop any semantics and just
look at the overall structure (cardinality/argument types)

In more detail:

1. Maintain that argument labels are not part of the type, but merely
hints for the function dispatch/programmer convenience

They are not merely hints, they are part of the full name of the function.
Functions with different names are different functions, even if they share
the same base name. That is, they are syntactically meaningful.

2. *Disallow* argument labels in closure variables. I.e.:

    var fun = drawLineTo(x:y:)

   has type (Float, Float) -> (), which makes the variable explicitly
compatible with any function of cardinality 2 that takes Float arguments.
At the sae time, any declaration like

    var fun : (x: Float, y: Float) -> ()

   should be illegal. This further makes clear that tuples and functions
are not the same thing.

3. In the long term, maybe consider *removing* *labels from tuples*. At
the same time, there should be a new mechanism in place that allows one to
elevate tuples to structs, with properties taking the role of the current
tuple labels (like Python’s namedtuple)

An alternative, in order to keep the cake and eat it too, would be
something along the lines of making predicate types explicit. E.g. creating
some sort of function ‚semantic metatype‘ and declaring function
conformance to this metatype. But I can’t imagine that it is a good idea.
Natural languages are really not a good source of inspiration in this
regard.

Best,

Taras

On 01 Jul 2016, at 05:33, Jordan Rose <jordan_rose@apple.com> wrote:

On Jun 30, 2016, at 13:36, Taras Zakharko via swift-evolution < > swift-evolution@swift.org> wrote:

On 30 Jun 2016, at 22:11, Austin Zheng <austinzheng@gmail.com> wrote:

As for the label semantics, Swift's current behavior is actively
misleading, please see the example in the prior email. There are no
meaningful semantics in the label, because implicit conversion between
differently-labeled function types means that there is no way to usefully
enforce these invariants to begin with.

That is a good point. I admit to not knowing this (I strongly expected
that labels would be semantically meaningful). But what exactly is the
status of the argument labels than in Swift? Just a documentation device
for the programmer and a hint for the compiler to do function dispatch?
But if the compiler indeed does dispatch on argument labels, then they are
not completely void of semantics, are they? As I mentioned before, I think
the problem here is much deeper.

The state of affairs I would prefer is something along these lines:

1. Labels are semantically meaningful
2. There is an explicit casting system for function signatures
3. This casting system should be in close correspondence to tuples. The
"function argument lists look sort of like tuples“ is a very compelling
reason actually, because of the principle of the least surprise. If I have
two things in the language that look very similar, then its very confusing
if they exhibit very different behaviour. Again, I am not proposing that
one goes back to model functions in terms of tuples. But as long as there
is a surface resemblance (and an obvious morphisms between the two), at
least some aspects of their design should be kept in sync.

But again, this touches on some deep design decisions for the language, so
I — as an amateur — don’t feel in my plate discussing this here. I believe
that there currently might be some inconsistencies in the language design
that should be sealed with (but maybe they are no inconsistencies at all
and I simply have false expectations).

Language history, a.k.a. story time!

We started out with the “perfect” model of a function type being a map
from a tuple to a tuple. Different argument labels were just overloads. It
really was quite a simple model, other than not having 1-tuples. Well, and
variadics and default values and trailing closures didn’t make sense
anywhere but in functions, but still. Very simple.

(And inout. And autoclosure. And maybe a few more.)

Then we hit a snag: naming guidelines. We wanted argument labels to be
something people felt comfortable using, something that would be encouraged
over a sea of unlabeled arguments. But even before the Swift 3 naming
conventions were hammered out, the natural names for argument labels didn’t
seem to match the names you’d want to use in the function. So we split the
names of parameters off from the names of tuple elements.

(This was precipitated by wanting to import Objective-C methods, but I
think it would have come up regardless.)

As seen earlier in the thread, argument labels don’t make for good tuple
element labels. Especially with the Swift 3 guidelines, argument labels
usually don’t make sense without the context provided by the base name, and
two methods that happen to share argument labels might not actually be very
similar, while two methods that are duals of each other might have
different argument labels due to, well, English (e.g. 'add(to:)' vs.
'remove(from:)’).

The real blow, however, came with that very first idea: that we could
treat methods with different argument labels as simple overloads in type.
This led to poor diagnostics where the compiler couldn’t decide whether to
believe the types or the argument labels, and might tell you you have the
wrong argument labels rather than a type mismatch. For pretty much every
Apple API, this was the wrong decision. On top of all that, it was really
hard to refer to a method when you *didn’t* want to call it. (Most
methods with the same base name still have unique labels, so you don’t need
the types to disambiguate.)

So we introduced the notion of “full names”, which are the things you see
written as ‘move(from:to:)` (and which are represented by DeclName in the
compiler). Almost immediately diagnostics got better, testing optional
protocol requirements got shorter, and a lot of compiler implementation got
simpler.

And then we kind of got stuck here. We have full names used throughout the
compiler, but tuple labels still appear in types. They’re still used in
mangling. We got rid of the “tuple splat” feature, but still model
out-of-order arguments as “tuple shuffles”. And we allow a number of
conversions that *look* like they should be invalid, but aren’t.

(And it’s important that we continue allowing them, or at least some of
them, because we want to be able to pass existing functions to things like
map and reduce without worrying about conflicting labels.)

So we’ve given up the perfect ideal of tuple-to-tuple. But we did it
because we value other things more than that ideal: variadics, default
values, trailing closures, inout, autoclosure, distinct argument labels and
parameter names, referencing a function by full name, and diagnostics that
better match the user’s likely intent (particularly given the naming
guidelines and existing libraries). I think that’s a worthwhile trade.

Jordan

P.S. Anyone is allowed to think this is *not* a worthwhile trade! But
part of the purpose of this story is to show that we’re already 90% of the
way towards making tuples and function arguments completely separate, even
if they have similar syntax. This proposal gets us to maybe 95%.

_______________________________________________
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