When Are Argument Labels Required in Swift

When are argument labels required in Swift?

As of Swift 3.0 this has changed again: all methods, functions, and initializers require argument labels for all parameters, unless you have explicitly opted out using the external name _. This means methods such as addChildViewController(_:) are now written like this:

func addChildViewController(_ childController: UIViewController)

This was proposed and approved as part of the Swift Evolution process, and was implemented in SR-961.

When we should have argument label in Swift function design, and when we should omitting argument label?

In terms of naming in Swift, you should check out API Design Guidelines. These guidelines will give you a general feel of how to name things in Swift. Fair warning though, these rules are rather theoretical and at the end you are to decide how to name your functions in specific cases. Don't get too hung up on this really, even the most experienced developers have troubles with this, so I've been told.

In your particular situation you definitely shouldn't omit argument labels because type information is very generic and doesn't provide any clue as to what you're passing in.

Hope this helps!

Swift 4 custom argument labels - required?

In Swift each function parameter can have both argument label and a parameter name.

  1. argument label: used in function call
  2. parameter name: used inside the function definition

By default, parameters use their parameter name as their argument label.

These are the possible function declarations that can be used in swift:

Case 1: parameter name is same as the argument label

func greet (person: String, day: String) -> String {
return "Hello \(person) on \(day)"
}

Function Call: greet(person: "John", day: "23")

Case 2: different parameter name and argument label

func greet (person person: String, on day: String) -> String {
return "Hello \(person) on \(day)"
}

Function Call: greet(person: "John", on: "23")

Case 3: using _ as argument label

func greet (_ person: String, _ day: String) -> String {
return "Hello \(person) on \(day)"
}

Function Call: greet("John", "23")

For more on how function parameters work, you can refer to: https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Functions.html

Let me know if you still face any issues.

Are Swift 4 custom argument labels ALWAYS required or just for disambiguation of functions?

If you have both someParam functions (each with a differently named parameter), then the line:

let funny = someParam

no longer works since it isn't known which of the two someParam functions you are referring to. This is solved by providing an unambiguous name:

let funny = someParam(p1:) // or someParam(p2:)

Note that funny has a type of (String?) -> String?. Both someParam functions have that same type. This is why you need to provide more detail to disambiguate the two. The parameter names don't change the function type.

When directly calling a function you must provide the full function name which includes any named parameter labels.

But when you create a closure variable and assign it the value of a function, you don't need to provide more than the base name of the function if that name is unambiguous in the given context.

swift argument labels and keywords

or just that these keywords can also be used as argument labels?

Exactly. This is introduced in SE-0001 Allow (most) keywords as argument labels. The motivation is that:

Sometimes, the most natural label for an argument coincides with a language keyword, such as in, repeat, or defer. Such keywords should be allowed as argument labels, allowing better expression of these interfaces.

Though in SE-0001 it is said that inout, var and let are not allowed to be argument labels (back then they are used to describe the mutability of the parameter). This restriction was later relaxed. Now, only inout is not allowed as an argument label, and you get a warning if you use var or let as an argument label.

Why does Swift require parameter names if it also requires argument order?

TL;DR

To help understand the answer to your question, you first have to understand that unlike other languages which support named arguments, in Swift, you are not actually seeing the argument names at all. You are seeing what are known as outward-facing labels. While they often do match the argument names, that is merely a coincidence/convenience. They are still very much separate things, with an entirely different set of rules, and understanding that difference helps explain the answer to your question.

That's why in Swift, when you're referring to the call site, don't think about argument names at all. Think of what you're seeing as words which add clarity at the call site by helping it read more like a sentence, and just as you can't randomly reorder words in a sentence, you shouldn't be able to randomly reorder those labels either.

The Rules

A simple set or rules describing the above can be summed up as follows...

  • Argument labels (external) are meant make sense only at the call site so it's clear what you are passing. As such they may function more in a 'grammar' fashion than an 'identifier' fashion (e.g. the function add(4, to: 3) uses the label 'to:' for the second argument, and no label at all for the first argument as the function name itself completes the grammar.) Also, labels do not have to be unique as they are also bound by ordinal position.

  • Argument names (internal) are meant to make sense only in the context of the function/method's internal body. The above function may be written as add(_ valueA: Int, to valueB: Int) which uses valueA and valueB only within the function's implementation.

  • If such a name makes sense in both contexts, you only need to specify the argument name, and it will implicitly use that same name for the label (e.g. someFunc(a: Int) is the equivalent of someFunc(a a: Int))

I highly recommend checking out Swift.org's API Design Guidelines as it goes into much more detail about why these decisions were made.

The Details (e.g. the reason)

In most languages, there is no concept of outward-facing labels. You simply specify a function's argument values by ordinal position, usually separated by a comma.

Now in some languages, like C#, in addition to specifying argument values ordinally, you can also/instead specify them by argument name. If you are specifying all arguments by name, it seemingly logically makes sense to be able to specify them in whatever order you want, but there are no requirements to use names so you can mix and match with ordinals.

But if you can do that, what happens when you specify some ordinally and some by name? Now you're forced to put the named ones at the end because if you didn't, how would it know the position of the unnamed arguments? Or more confusing, what happens if you specify an argument value in the third position but you also specify that same argument by name later on? It can be confusing.

In contrast, Swift strives to not only be much more consistent in its calling conventions, but it also strives to be a self-documenting language with a focus on clarity at the point of use (i.e. the call-site). As such, Swift added the concept of external/outward labels which default to being required, but it allows you to make them optional if it makes sense. And if you don't explicitly specify a label for your argument, the label is implicitly set to the same name as the argument, but again, they are still very different things.

Now as to the why this is a good thing, let's look at some examples.

Examples

Let's start with a language like C# that does let you reorder named arguments.

var result = multiplyValues(valA: 20, valB: 5)

Since you can reorder it, you can write this as well...

var result = multiplyValues(valB: 5, valA: 20)

Now in Swift, what you use at the call site aren't argument names, they are labels to help in the overall context of the signature itself.

Consider this slightly more complex function definition...

func multiply(value: Int, by factor: Double, thenAdd addlAmount: Int){}

When called, it reads like this...

let result = multiply(value: 20, by: 1.5, thenAdd: 5)

Isn't that much clearer than the C# example? But it's important to note that 'by' is not the argument name, but rather a label that makes sense given its position in the call. The actual, underlying argument is called 'factor' and that's what's used inside the body of the function's implementation.

Following the Swift design guidelines, one may even make the first argument's label optional, which now reads like this...

let result = multiply(20, by: 1.5, thenAdd: 5)

Again, it's incredibly clear what's going on, almost reading like a sentence.

Now imagine if you didn't use those labels and just used the argument names. Now the names matter in the external context. For instance, look at this

let result = multiply(value: 20, addlAmount: 5, factor: 1.5)

Clarity starts to get obscured. Are you multiplying 20 by 1.5 then adding 5, or are you multiplying 25 by 1.5? Is the answer 35 or 37.5?

And what if you then used the external names/labels? Now it's even worse!

let result = multiply(by: 1.5, thenAdd: 5, 20)

What the heck is going on?

When you reorder them in this way, it's not exactly clear what you're multiplying by 1.5, because if this is in an entity (struct, class, etc.) it could be easily mistaken that you're multiplying the implicit self by 1.5 (i.e. self.multiply(by:)), then there's some rando parameter 20 at the end without context. Again, it's not clear.

Of course, naturally you may respond 'But that's with an optional label! The order shouldn't matter if and only if all labels have to be specified.' but now you're cluttering up the rules and breaking consistency for a single, specific use-case.

Perhaps a more pertinent question is what do you actually get with that one-off use-case? With all these up-sides of the existing rules, what's a down-side? What are you not able to do by not allowing a random order?

It's the above aversion to inconsistency that led Swift to introduce a breaking change in earlier versions of Swift where the first argument's label wasn't required even if you specified one. It implicitly added a _ if you will. But why should that first argument get special treatment when compared to the others? The answer... it shouldn't! All that did was confuse people, so they changed it in a future version because again, consistency is more important than cleverness, even over introducing a breaking change.

Argument Names have to be unique. Label names do not.

Since label names are meant to offer clarity at the call site, helping it read more like a sentence, it is possible to use the same label more than once since the order is also important.

Argument names however must be unique because they need to coexist within the implementation.

Here's a example demonstrating this. Note how the label 'and' is used twice, but the argument names 'a', 'b' and 'c' must be unique...

func add(_ a: Int, and b: Int, and c: Int) -> Int {
return a + b + c
}

let result = add(1, and:2, and:3)

print(result)

To Sum Up...

External-facing labels should only make sense at the call site and should strive to complete the grammar of a sentence. Argument names however should only make sense in the context of a function's implementation.

Follow those simple guidelines and it will become very clear what should go where and in what order, and why it's a good thing that you can't simply reorder your arguments at the call site.

why does the argument label become irrelevant in this Swift code?

Fact: any function in Swift can be reduced to a closure, and they actually are when passed around. f and g and h can all be reduced to closures of type (String) -> String.

Next, your (some_computation)("hello") code what in fact does is to tell to the compiler that you want to create an anonymous function (aka closure), that you call with the "hello" argument.

And due to the above, (0 < 100 ? g : h)("hello") instructs the Swift compiler to transform the g and h functions to closures of type (String) -> String. And closures don't have argument labels, thus any function, with any label, matches.

However, in the problematic code snippet, the compiler can no longer uniquely identify which h "overload" to use. Thus, it bails out.

If you want to unambiguously match one of the h functions, then you need to fully qualify it:

(0 < 100 ? g : h(foo:))("hello")

The rules work the same for member functions, as long as the compiler can decide which closure to pass along, it will match the function name, without having to specify its complete name.

Main point is that in Swift you don't circulate functions, you circulate closures. There's no way to specify a variable, or a function argument, or a property, to have labeled arguments.



Related Topics



Leave a reply



Submit