Using Function Parameter Names in Swift

Using Function Parameter Names in Swift

This is to follow an convention we were all used to from Objective-C, where the name of the first parameter is combined with the method name. Here's an example:

- (void)incrementByAmount:(NSInteger)amount
numberOfTimes:(NSInteger)times
{
// stuff
}

You could call the method like:

[self incrementByAmount:2 numberOfTimes:7];

And it feels more natural to read by incorporating the name of the parameter into the method's name. In Swift, you can achieve the same with the following:

func incrementByAmount(amount: Int, numberOfTimes: Int) {
// same stuff in Swift
}

And call the method like:

incrementByAmount(2, numberOfTimes: 7)

If you don't want to use this convention, Swift gives you the ability to be more explicit and define separate internal and external parameter names, like so:

func incrementByAmount(incrementBy amount: Int, numberOfTimes: Int) {
// same stuff in Swift
// access `amount` at this scope.
}

You can call the method like this:

incrementByAmount(incrementBy: 2, numberOfTimes: 7)

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 a function call require the parameter name in Swift?

Update for Swift 2.0: Now functions behave identically to methods, and for both, by default:

  • the first parameter has no external name; and
  • the other parameters have an external name identical to the internal name.

Other than that, the rules below still apply, except that the # shorthand syntax is now gone.


Here's a more general answer: functions behave differently when defined as true functions outside a class, and when defined as methods. Moreover, init methods have a special rule.


Functions

Suppose you define this:

func multiply1(f1: Double, f2: Double) -> Double {
return f1 * f2
}

Parameter names are here only local to the function, and cannot be used when calling the function:

multiply1(10.0, 10.0)

If you want to force using named parameters when calling the function, you can. Prefix each parameter declaration with its external name. Here, the external name of f1 is f1param, and for f2, we use the shorthand where we prefix it by # to indicate that the local name is to be used as the external name as well:

func multiply2(f1param f1: Double, #f2: Double) -> Double {
return f1 * f2
}

Then, named parameters must be used:

multiply2(f1param: 10.0, f2: 10.0)

Methods

Things are different for methods. By default, all but the first parameter are named, as you've discovered. Suppose we have this, and consider the multiply1 method:

class Calc {
func multiply1(f1: Double, f2: Double) -> Double {
return f1 * f2
}
func multiply2(f1param f1: Double, f2: Double) -> Double {
return f1 * f2
}
func multiply3(f1: Double, _ f2: Double) -> Double {
return f1 * f2
}
}

Then, you have to use the name of the second (and following, if any) parameters:

let calc = Calc()
calc.multiply1(1.0, f2: 10.0)

You can force to use a named param for the first argument by providing an external name for it, like for functions (or prefixing its local name with # if you want to use the same external name as its local name). Then, you have to use it:

calc.multiply2(f1param: 10.0, f2: 10.0)

Finally, you can declare an external name of _ for the other following arguments, indicating that you want to call your method without using named parameters, like this:

calc.multiply3(10.0, 10.0)

Interoperability note: If you prefix class Calc with the @objc annotation, then you can use it from Objective-C code, and it is equivalent to this declaration (look at parameter names):

@interface Calc
- (double)multiply1:(double)f1 f2:(double)f2;
- (double)multiply2WithF1param:(double)f1 f2:(double)f2;
- (double)multiply3:(double)f1 :(double)f2;
@end

Init Methods

The rule differs a bit for init methods, where all parameters have an external name by default. For instance, this works:

class Calc {
init(start: Int) {}
init(_ start: String) {}
}

let c1 = Calc(start: 6)
let c2 = Calc("6")

Here, you have to specify start: for the overload that accepts an Int, but you must omit it for the overload that accepts a String.

Interoperability note: this class would get exported to Objective-C like this:

@interface Calc
- (instancetype)initWithStart:(NSInteger)start __attribute__((objc_designated_initializer));
- (instancetype)init:(NSString *)start __attribute__((objc_designated_initializer));
@end

Closures

Assume you define a closure type like this:

typealias FancyFunction = (f1: Double, f2: Double) -> Double

The parameter names will behave very similar to those in a method. You will have to provide the names to the parameters when calling the closure unless you explicitly set the external name to _.

For example, executing the closure:

fund doSomethingInteresting(withFunction: FancyFunction) {
withFunction(f1: 1.0, f2: 3.0)
}

As a rule of thumb: even if you dislike them, you should probably try to keep using named parameters at least whenever two parameters have the same type, in order to disambiguate them. I'd also argue that it's good to also name at least all Int and Boolean parameters.

Can we use keywords as parameter names in SWIFT?

When using a keyword as a normal identifier you have to escape it using backticks ` like this

func setupTable(for: Genre) {
switch `for` {
case .drama: break
case .comedy: break
}
}

On a side note, as pointed out by @Hamish in a comment on the question, you should try to avoid using such names for variables, which (in this case) you can do by providing both an internal and external name for the parameter:

func setupTable(for genre: Genre) {
switch genre {
case .drama: break
case .comedy: break
}
}

Swift: with before parameter in a function

This is called external parameter name. the main purpose of it sentence formulation and make it easy for reader. can read more about Parameter Names from official documentation.

Swift function, two names for one parameter

From Apple's documentation:

Sometimes it’s useful to name each parameter when you call a function, to indicate the purpose of each argument you pass to the function.

If you want users of your function to provide parameter names when they call your function, define an external parameter name for each parameter, in addition to the local parameter name. You write an external parameter name before the local parameter name it supports, separated by a space:

func someFunction(externalParameterName localParameterName: Int) {
// function body goes here, and can use localParameterName
// to refer to the argument value for that parameter
}

Shorthand External Parameter Names

If you want to provide an external parameter name for a function parameter, and the local parameter name is already an appropriate name to use, you do not need to write the same name twice for that parameter. Instead, write the name once, and prefix the name with a hash symbol (#). This tells Swift to use that name as both the local parameter name and the external parameter name.

https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Functions.html

Spacing in parameter name on swift functions

That is not how parameters work sadly.

Take the example of the prepareForSegue func. It looks like this as a func:

override prepare(for segue: ...) {

However, if you were to call it, it would look like this:

prepare(for: UIStoryboardSegue, ...)

You can only ever have one parameter name for when you call a func.

Edit

To look at your example, you could have a func like this:

func saveReceipt(for customer: ...) {

And then call it like this:

saveReceipt(for: MyCustomerClass, ...) 

Swift 3 first parameter names

Yes, this is right. Swift is fixing a language inconsistency this way (this was always required for initializers).

If you don't want to use the external parameter name, just remove it explicitly:

func frobnicate(_ runcible: String) { 
print("Frobnicate: \(runcible)")
}

You can read the full rationale in Swift Evolution 0046



Related Topics



Leave a reply



Submit