22

In Swift, when you call a function, you are required to label the arguments, unless the author of the function has explicitly allow you not to. Is there a reason for this in terms of language design? I had always viewed parameter labels as a good way to allow the caller to order the arguments in whatever way makes sense to them, but this way just feels like a lot of meaningless/useless boilerplate.

For example, if we define the function makeDog like this:

makeDog(legs: int, name: String)->Dog{}

Then it must be called like this:

makeDog(legs: 4, name: "fido")

And it can't be called like this (compiler error):

makeDog(name: "fido", legs: 4)

Even the Stack Overflow tag description for says:

Named parameters enable you to specify an argument for a particular parameter by associating the argument with the parameter's name rather than with the parameter's position in the parameter list.

TylerH
  • 20,799
  • 66
  • 75
  • 101
Alex N.
  • 654
  • 8
  • 21
  • 2
    What kind of answer would satisfy you? In other words, what does "why" mean in this question? Those are the rules of the language. You don't ask "why" a touchdown is six points. – matt Oct 11 '19 at 18:25
  • 3
    @matt I'm looking for some explanation of why named parameters have to be listed in the order they are declared. At some point, somebody decided that listing the parameters out of order, even when named was an error. My hope is that someone privy to that discussion has at some point said why they made it and I would like to see that. – Alex N. Oct 11 '19 at 18:32
  • "someone privy to that discussion" So the only "answer" would have to come from someone who _designed_ the language? That's not going to happen here. – matt Oct 11 '19 at 18:38
  • 3
    @matt Maybe not, but I was kind of hoping that there was a book, or blog post, or something that I just wasn't finding where they had said it. – Alex N. Oct 11 '19 at 19:13
  • Well asking for a book or a blog post is forbidden on Stack Overflow. So either this is opinion-based or it asks for a further reference. To put it another way: how are you going to know the right answer when you see it? If there's no way to know that, it's not a Stack Overflow question. – matt Oct 11 '19 at 19:15
  • 6
    @matt Almost every question that get's asked on SO could be answered with a book, blog post, or documentation. People usually post the questions here because they can't find or don't understand that material. In this case there are plenty of other ways to answer. For example, someone could post a use case that only works if both labels and ordering are enforced at the same time, or they could explain some optimization that it allows the compiler to perform. Obviously, the best answer would come straight from the horse's mouth, but there are plenty of other ways it could be handled. – Alex N. Oct 11 '19 at 19:32
  • 1
    Good question. I had no idea it was the case myself. Valuable question. Extremely good question, and one that needs addressing in the language revisioning. Why indeed. – Kalen Mar 04 '22 at 22:48
  • I can understand this question since in Kotlin, once you specify the parameter name it doesn't matter if it is in order or not. – Bitwise DEVS Jul 19 '22 at 11:21
  • 1
    Actually, some folks do ask why a touchdown is 6 points. It's called curiosity. I am always wondering why a game or language or anything is designed a certain way. It's a perfectly valid question, especially when the thing makes no sense when looked at from other conventions. – Merlin -they-them- Mar 06 '23 at 18:22

5 Answers5

24

TL;DR

To help understand the answer to your question, you first have to understand that unlike other languages which support named arguments, in the case of Swift, you are not actually seeing argument names at the call-site. What you are seeing are known as outward-facing labels, and while they often match the underlying 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 is the key to answering your question.

In short, outward-facing labels are words which are meant to 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...

  • Outward-facing labels (external)
    These 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 rather than as an identifier. Unlike argument names, outward-facing labels do not have to be unique, and they can also be omitted entirely by setting the label in the method's signature to an underscore (_), which means 'Don't use a label for this argument.`

  • Argument names (internal)
    These are meant to make sense only in the context of the function/method's internal body. As such, these do have to be unique, and should be named according to the implementation of the function and not based on how it's called/used externally.

As an example, consider a function that adds two numbers. Here's how it could be defined...

func add(_ valueA: Int, to valueB: Int) {
    return valueA + valueB
}

And here's how you call it.

let result = add(30, to: 12) // gives 42

Above, valueA and valueB are the argument names which only exist inside the implementation for add. Additionally, the first argument omits its label (via the underscore in its place), and the second argument defines its label as to because that makes the call-site read like a sentence... 'Add 30 to 12'... making it very clear at the call-site what's going on.

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 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 other 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.

Bonus Topic - Like BK, 'Have It Your Way!'™

Ok, so you now understand how important labels are to the Swift language, and you also get why order is important. But sometimes, you're just stuck with a poorly-designed API, one that makes absolutely no sense whatsoever. Maybe that's the reason you want to call arguments in any order you wish.

As an example, say there's a package called BadMath that you don't own, don't like, but for reasons out of your control, you have to use in your project. Now say it defines an add function like so...

class BadMath {

    func add(_ value: Int, to value2: Int, butNotBeforeFirstMultiplyingItBy valueMultiplier: Int) -> Int {
        return (value * valueMultiplier) + value2
    }
}

(Ugh... It's even painful for me to write that as an example!)

So you're stuck using this awful API that clearly calls things in the wrong order, and your code too now looks like garbage as a result. And you don't like garbage code! You like clean code, and your code is normally stellar! But man... that function...

Don't you just wish you could change it?! Wouldn't it be sooo great if you could clean that API up and make it work in a way that logically makes more sense to you? I mean... it would be really awesome if you could do something, say, like this...

extension BadMath {

    func multiply(_ value: Int, by factor: Int, thenAdd addlValue, Int) -> Int {
        add(value, to: addlValue, butNotBeforeFirstMultiplyingItBy: factor)
    }
}

Oh, wait! You can, and that just did!

The point being, if you're using an API that you decide you really can't stand, and you really want to name and/or call the arguments in the order that you want, then go crazy with extensions in your own codebase and 'fix' their APIs!

I personally do this all the time with Swift Packages, and even some of Apple's own frameworks.

It's all in the goal of making the code more readable at the call site, so anything that works towards that goal is always the right... ahem... 'call' (Heyoooo! I'm here all week, folks! Tip the wait staff! )

Mark A. Donohoe
  • 28,442
  • 25
  • 137
  • 286
  • Must outward labels be unique? Your examples using `to` and `by` let me think that I should be able to use them more than once? – GACy20 Jul 28 '22 at 08:56
  • 1
    No, they don't have to be unique at all because again, they are also bound by ordinals. Consider this... `func add(_ item1: Int, and item2: Int, and item3: Int) -> Int` which you would call like this... `let result = add(1, and:2, and:3)`. Again, they are there to make the grammar read more like a sentence so they don't have to be unique. Good to add to my answer tho. Thanks! – Mark A. Donohoe Jul 28 '22 at 15:32
  • 1
    this is something i absolutely like about swift. The functions read like sentences. – Raildex Jul 28 '22 at 15:45
  • Couldn't agree more. The more I use this concept of external labels and get away from thinking of argument names, my APIs become clearer and clearer, and as they say, with clarity at the call site, when reviewing code it makes it *soooo* much easier to follow. Their API Design Guidelines is gospel to me. One of the best, most thought-out guidelines I've seen. – Mark A. Donohoe Jul 28 '22 at 15:49
  • this is a great explanation and example, but I do wonder if the same would apply to non-math-related functions. I mean, order matters for this function, because order matters in math. But, purely as a mental exercise, for non-math-related functions, would the reasoning still hold up, ie. would order still matter? – Zonker.in.Geneva May 23 '23 at 21:46
  • Instead of thinking about it in math-vs-non-math, or in order-vs-non-order, I think the better question is are you saying order is important or not? I say that because if it is important, Swift will enforce that for you. If it isn't important, it equally shouldn't be important to you to have to call them in any way you want because, by definition, that isn't important, so just call it in the order it was defined. (Continued...) – Mark A. Donohoe May 24 '23 at 04:10
  • (...continued) In other words, like I asked above, what does having the ability to specify them in a 'random' order give you? It definitely doesn't make things clear. And in cases where you have the same type of 'thing', but an unknown *amount* of them, you still have access to variadics. And if the signature *really* bums you out, you also can always simply write an extension with the signature defined however *you* want. You then can delegate to the underlying call. (I'll add that as an example.) – Mark A. Donohoe May 24 '23 at 04:12
3

There are a couple of reasons Swift does this.

One is the influence of Objective-C on the designers of the language. Named parameters are a feature of the Objective-C language (and others as well.)

The other reason is that use of named parameters allows the code to be self documenting.

It would be interesting if the named parameters could be specified in any order, as you can do in other languages.

You can read more about the specifics of Swift functions here: Functions

Gene Z. Ragan
  • 2,643
  • 2
  • 31
  • 41
  • Oh jebus. Objective C looks even more painful than Swift. It look like they have some kind of half bastardized version of named arguments. https://stackoverflow.com/questions/3026432/dont-understand-multiple-parameter-declarations-in-objective-c?rq=1 – Alex N. Oct 11 '19 at 18:08
  • It is all part of the evolution of languages, as designers try to figure out the best ways to find a balance between performance and ease of use. We are still trying! – Gene Z. Ragan Oct 11 '19 at 18:12
  • 1
    Yeah, this is my first foray into Swift and it's interesting. I feel a lot like I did learning to program in the late 90's. There are lot's of obvious language features that are missing and every time something goes wrong I have to figure out if it's my code, the IDE, the compiler, or the runtime. – Alex N. Oct 11 '19 at 18:24
  • I can buy the first part of the argument, evolution from ObjC, but self-documenting... AndroidStudio shows the target parameter label for this purpose, without requiring you to type it, and it serves that exact same purpose. You could say, that's coupling the language to the development environment and that's a bad design... but I wouldn't like hearing that from something it is in practice only meant to work in XCode. Even single parameter methods are requiring you to put the label FG'S. – Azurlake Dec 09 '21 at 08:22
3

Names are used for clarifying the purpose of each.

In swift, parameters can be anonymous and you can use the their order number in order to use them. You usually see them in closures and in Swift, functions are closures.

So order matters and the names are for more clarification.

Imagine the sorted function on an [Int]:

[1, 2].sorted(by: <#(Int, Int) throws -> Bool#>)

How could you know which one of these two arguments should be on the right or left side of the < operator? Their order!

For the sake of self-documented code, you may name them as first and second to prevent losing track of their order inside the function:

[1, 2].sorted { first, second -> Bool in
    return first < second
}

But as you may know, you could get rid of the names and just use the order number:

[1, 2].sorted {
    return $0 < $1
}

As you may know, this < is an infix function in Swift like this:

func <(lhs: Int, rhs: Int)->Bool { return lhs < rhs }

So you can pass its name to the entire function as an argument of the sorted:

[1, 2].sorted(by: <)

It's arguments have names, but how many times you use them? Zero! so how compiler knows which one to use when you pass?

function signature!

The types of the arguments, their order, and the type(s) of the function's return value(s)

hippietrail
  • 15,848
  • 18
  • 99
  • 158
Mojtaba Hosseini
  • 95,414
  • 31
  • 268
  • 278
  • 3
    Just to clarify, I understand why you order arguments in most languages and why it's important in your example, but your arguments aren't named. If they were named, you could tell them apart by their names. – Alex N. Oct 11 '19 at 18:22
  • they can have names, or just index. But they can't have both since it's nonsense. Like you use a dictionary but you expect to use index of items ‍♂️ – Mojtaba Hosseini Oct 11 '19 at 18:25
  • Now I'm lost. In a standard function definition they **do** have a name and an index and both are enforced when you call the function. – Alex N. Oct 11 '19 at 18:29
  • There is something called `function signature` and that is only: type of arguments, order of them, and type(s) of the return value(s) of the function. Names are useless from the compiler side. – Mojtaba Hosseini Oct 11 '19 at 18:32
  • I have added some more information. Hope it helps you to understand. – Mojtaba Hosseini Oct 11 '19 at 18:38
  • That's actually pretty interesting. If you define a function with the closure syntax, you aren't required to name the parameters (and obviously you can't use them when you call it), but if you define it with the func syntax, like I did in the question, you are required to use the names. I wonder what the difference is between the standard function and the closure? – Alex N. Oct 11 '19 at 19:41
  • "they can have names, or just index. But they can't have both since it's nonsense". They absolutely can have both, python has done this for decades (though to be fair it's a dynamically typed language). For example, in python you can do `def foo(a, b): return f"{a} + {b}"`. You can then call it either as `foo("a", "b")` or `foo(b="b", a="a")` with the same result. Saying the answer is due to compiler function signature spec doesn't really answer anything - if the language designers had wanted to support this feature they could have specified function signatures differently. – Bennet Huber Dec 05 '21 at 19:50
  • @BennetHuber, I think what the other commenter was trying to say is not both ***in the same call***, meaning using your example, you can't do this... `foo("A", a="A")` because you've defined `a` twice, and didn't define `b` at all. Same with `foo(b="B", "B")`. *(I'm not a python person myself, but I'm guessing `foo("A", b="B")` does work?)* Either way, I'm sure you can see it's hella confusing at the call site, and definitely does ***not*** make things clear, which is exactly what Swift was trying to solve (...and succeeded at imho. It's painful to go back to languages without it now.) – Mark A. Donohoe May 24 '23 at 04:07
2

The way to understand this is to realize that the label's purpose is not to tell the compiler which parameter this is. The order and type, combined with default values, does that.

The label's purpose is purely to dictate to the caller what the caller must say.

So for example:

func f(p1 param1:String = "one", p2 param2:Int = 2) {}

f(p1:"hey", p2:42)
f(p1:"hey")
f(p2:42)
f()
// but not:
// f(p2:42, p1:"hey")

The label p1: does not mean, "Hey, compiler, this is the param1 parameter."

It means, "Hey, caller, if you include this parameter (which must come first if you include both), you must label it p1:."

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • 3
    I'm not sure sure I buy "does not mean, "Hey, compiler, this is the param1 parameter." If I skip the first parameter, and only include param2, that p2 label *is* telling the compiler that this is param2, not param1, which comes first in the definition. – Alex N. Oct 11 '19 at 19:12
  • If there were no labels, you couldn't skip the first parameter to begin with. But that's not because the labels differentiate, it's because there's no way to _express_ doing that. – matt Oct 11 '19 at 19:14
2

One thing not mentioned yet is for functions or initializers that have multiple parameters with defaults and you only want to override one or two of the defaults. For example:

func foo(aString: String = "", anInt: Int = 42, aFloat: Float = 10) {}

foo(aFloat: 50) // Skip the params where you want to use the defaults.
Rudedog
  • 4,323
  • 1
  • 23
  • 34
  • I use this approach for the functions that receive tens of parameters, where they all have reasonable default values. However, placing the parameters in the right order is always a challenge when there are many parameters. Compiler and Xcode are very helpful to suggest the right order, but it would have been very valuable to have the ability to place the parameters in any order along with their labels as in Python function calls. – Ismet Sahin Feb 20 '23 at 19:18