Personally I'm fine with the way it is right now, but I do miss enforced labels 
at some point. At least it would be 'nice to have' feature like we already have 
@autoclosure or @noescape to enforce some specific behaviour.

I started the discussion about enforcing argument labels on tuples but with 
your feedback I totally see the point why this shouldn’t be something backed 
inside the language by default.

Optional way to enforce the usage of correct/strict/explicit labels could 
provide better readability from my point of view.

Remember the idea of cascading methods/initializer? For class types it’s almost 
possible to build that feature by yourself, but there is one problem that was 
discussed back then. What do we use inside the trailing closure to access the 
instance. 
Sure we could give it a custom name or just use `$0`.

```swift
let instance = ClassA() {

      // customName in
      // or $0
}

// imagine we had something optional like
class ClassB {

      @require_explicit_label_usage
      init(closure: (this: ClassB) -> Void) {

            closure(this: self)
      }
      func foo() {}
}

// now we could use it like

let b = ClassB() { this in // can be omitted due the use of 
@require_explicit_label_usage

       this.foo()
}

// I'll use ++ operator just for the example here

@require_explicit_label_usage
prefix func ++ (tuple: (exp: Int, earned: Int)) -> Int {

     return tuple.apples + tuple.amount
}

// this operator will only overload when the right labeling is applied to the 
tuple

++(exp: 10, earned: 5) // result would be 15

// the operator wont work with
++(100, 50)
// give another dev this snippet and ask him what 100 and 50 means

```
This example is very abstract but I'm sure you should get the point of the 
possible need of an optional label enforcement.

By the way, why does Swift allow something like this anyway?

var a = (a: 10) // is of type Int

Where something like the next example is not allowed at all:

var a: (a: Int) = (a: 10)


-- 
Adrian Zubarev

Am 21. April 2016 bei 09:14:16, Haravikk via swift-evolution 
([email protected]) schrieb:

I think the important thing to remember is that the label check is intended to 
prevent cases like this:

let a:(left:Int, right:Int) = (1, 2)
var b:(right:Int, left:Int) = a

While the two tuples are compatible by type, the meaning of the values may 
differ due to the different labels; in this case the values are represented in 
a different order that a developer should have to explicitly reverse to ensure 
they aren’t making a mistake, or they could represent radically different 
concepts altogether.

It’s certainly annoying when the labels are only different due to minor 
differences, but the compiler doesn’t know that. So yeah, I think that in any 
case where there are external labels that differ a warning should be raised; 
this comes down to being able to later ignore types of warnings, which could 
avoid the boiler-plate in future.

The alternative would be if we had some syntax for mapping parameters more 
cleanly, for example:

hi(1, y: 2, fn: sum1 where left = lhs, right = rhs)

Or something along those lines anyway?

On 21 Apr 2016, at 06:18, David Owens II via swift-evolution 
<[email protected]> wrote:


On Apr 20, 2016, at 4:47 PM, Chris Lattner <[email protected]> wrote:

On Apr 20, 2016, at 12:31 PM, David Owens II via swift-evolution 
<[email protected]> wrote:
This is similar to another concern I raised with functions and being able to 
essentially erase the function argument names and apply two different named 
parameters just because their types match.

It seems reasonable to me that you can go from (x: Int, y: Int) => (Int, Int). 
However, going from (x: Int, y: Int) => (a: Int, b: Int) feels somewhat odd. 
Yes, the types can obviously slot in there fine, but how much importance do the 
labels for the types bring to the table?

Similarly, should this (Int, Int) => (x: Int, y: Int) be allowed through an 
implicit means? If so, then it's really just an intermediate step for (x: Int, 
y: Int) => (a: Int, b: Int) working.

I completely agree, I think it makes sense to convert from unlabeled to labeled 
(or back) but not from “labeled" to "differently labeled”.

So what matters more, type signatures or label names?

Here's an example:

typealias Functor = (left: Int, right: Int) -> Int

func hi(x: Int, y: Int, fn: Functor) -> Int {
    return fn(left: x, right: y)
}

hi(1, y: 2, fn: +)
hi(1, y: 2, fn: *)

If we say that the parameter names are indeed vital, then the above code cannot 
work as the operators that match the type signature are defined as: 

public func +(lhs: Int, rhs: Int) -> Int

Obviously, given a name to the parameter brings clarity and can be self 
documenting, but if we want the above to work while making names just as vital 
as the type signature, then we need to declare `Functor` as such:

typealias Functor = (_ left: Int, _ right: Int) -> Int

However, that's not even legal code today, and even if it were, is that really 
better?

I don’t think this follows, since operator parameters are always unlabeled.  I 
suspect we don’t reject it, but I’d be in favor of rejecting:

func +(lhs xyz: Int, rhs abc: Int) -> Int { }

So maybe I think about this incorrectly, but I always think of any parameter 
without an explicit label to have one that is equal to the parameter name. So 
these two functions signatures would be equivalent:

func sum1(lhs: Int, rhs: Int) -> Int
func sum2(lhs lhs: Int, rhs rhs: Int) -> Int

It’s only when you explicit “erase” the label where there is none:

func sum(_ lhs: Int, _ rhs: Int) -> Int

So back to the example above, it’s still somewhat odd that all of these are 
valid:

hi(1, y: 2, fn: sum1)
hi(1, y: 2, fn: sum2)
hi(1, y: 2, fn: sum)   // makes the most sense, no label to labeled promotion

But if we did reject the differently labeled version, that would mean that we 
would need to declare the `Functor` above as:

typealias Functor = (Int, Int) -> Int

Is that better? I’m not terribly convinced that it is.

If `Functor` keeps the labels, I suspect it would just lead to additional 
boiler-plate code that would look like:

typealias Functor = (left: Int, right: Int) -> Int

hi(1, y: 2, fn: { left, right in sum1(lhs: left, rhs: right) })

While it does seem technically correct, is that really the kind of code we want 
in Swift? 

-David
_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to