Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is a function's first parameter name in Swift not required when calling?

I'm learning Swift and I find it weird why when calling a function, the first parameter's name is not required.

func say(greeting: String, toName: String) {
    print("\greeting), \(toName)!")
}

say("Goodbye", toName: "Hollywood") // <-- why is there no "greeting" required?
like image 985
edmamerto Avatar asked Jan 10 '16 21:01

edmamerto


3 Answers

As others have said, it's a style issue originating with Objective-C.

To understand why someone would want this style, consider a modification of your example:

func say(greeting: String) {
    print("\(greeting)")
}

which you would call like this:

say("Hello!")

When you look at the names you're using, there's arguably some information missing. With a function called say(), you might reasonably think that this is a function to let you say anything at all. But when you look at your parameter name, it's pretty clear that this is a function for saying a greeting, not for saying anything.

So Objective-C would prefer you to write it like this:

func sayGreeting(greeting: String) {
    print("\(greeting)")
}

which you would call like this:

sayGreeting("Hello!")

and it is now clear that you are saying a greeting. In other words, the function name itself is more clearly describing what you are doing. For this reason, sayGreeting("Hello!") is preferable to say(greeting: "Hello!") because the key thing that a function does should be described by its name, not relegated to a parameter name and given secondary importance.

But this rationale only really works for the first argument. Suppose you want to add a name, as you have done. In a language like C, where you have no external parameter names at all, you might write:

void sayGreetingToName(char * greeting, char * person) { ...

and call it like:

sayGreetingToName("Hello", "Dave");

which is OK, but starts to break apart quickly when you have overloaded functions or default values, neither of which you have in C. If you wanted to write:

func sayGreetingToName(greeting: String, name: String? = nil) {
    if let name = name {
        print("\(greeting), \(name)!")
    }
    else {
        print("\(greeting)!")
    }
}

then calling it as:

sayGreetingToName("Hello", "Dave")

would look basically OK, but:

sayGreetingToName("Hello")

looks ridiculous, because the function name says you're providing a name, but you're not.

So instead, if you write:

func sayGreeting(greeting: String, toName: String? = nil) {
    if let name = toName {
        print("\(greeting), \(name)!")
    }
    else {
        print("\(greeting)!")
    }
}

you can call it in two ways:

sayGreeting("Hello")
sayGreeting("Hello", toName: "Dave")

and everything looks perfectly clear.

So to summarize, the idea behind this style of writing is that the function name itself should contain any information that the name of the first parameter contains, but that it doesn't make sense to extend this to subsequent parameters. So by default the first has no external name, but the rest do. The function is all about saying a greeting, all the time, so that should be inherent in the name of the function (and therefore not duplicated by insisting on the external name of the first parameter) but it may or may not be about saying it to a particular name, so that information should not be in the function name.

It also enables you to essentially read a function call as if it were English, because the names and the parameters are now approximately in the right order for you to do that:

sayGreeting("Hello", toName: "Dave")

Say (the) greeting, "Hello", to (the person with) name "Dave"

It's quite a nice style, once you get used to it.

like image 107
Crowman Avatar answered Nov 17 '22 23:11

Crowman


Chris Lattner talks about exactly this in the What's New In Swift 2 Talk:

Back in the days of Swift 1.x this behavior only applied to methods. Functions written outside of classes, structs or enums did not require any parameter to be named in a function call (unless you explicitly forced it by using an external parameter name in the function definition).

Due to conventions from Objective-C, methods were often named in a way, that the first parameter was already part of the method name.
Examples are: indexOf(_:) instead of index(of:), or charactersAtIndex(_:) instead of charactersAt(index:).
In Objective-C this would be written as indexOf: and charactersAtIndex:. There are no braces to separate function name from function parameters. So the parameters were basically part of the function's name.

As mentioned before, this behavior only applied to methods at first though. This resulted in confusion among programmers, when to add an external name to the first parameter, and when not. So finally the behavior was changed, so that the first parameter does not use the internal name as an external name by default, but all of the following parameters do.
This resulted in a more consistent usage of external and internal parameter names. And this is the behavior as it exists in Swift today.

tl;dr The behavior is a remnant from Objective-C

like image 30
Marcus Rossel Avatar answered Nov 17 '22 23:11

Marcus Rossel


This is the default behaviour for functions in swift, omitting the external name for the first function parameter.

By default, the first parameter omits its external name, and the second and subsequent parameters use their local name as their external name.

From Language Guide - Functions.

Note however that you can add an external name also to the first function parameter, if you so wish:

func foo(extBar bar: String, bar2: String) {
    print(bar+bar2)
}

foo(extBar: "Hello", bar2: "World")

Likewise, you can tell the 2nd (and so on) function parameters to omit their external names, by adding _ prior to the internal name of the parameter, in the function signature.

func foo2(bar: String, _ bar2: String) {
    print(bar+bar2)
}

foo2("Hello", "World")

Note however that for initializers, the external names are mandatory for all function parameters, including the first one.

As with function and method parameters, initialization parameters can have both a local name for use within the initializer’s body and an external name for use when calling the initializer.

However, initializers do not have an identifying function name before their parentheses in the way that functions and methods do. Therefore, the names and types of an initializer’s parameters play a particularly important role in identifying which initializer should be called. Because of this, Swift provides an automatic external name for every parameter in an initializer if you don’t provide an external name yourself.

From Language Guide - Initialization.

As an example, consider

struct Foo {
    var bar : Int

    init(extBar: Int) {
        bar = extBar
    }
}

var a = Foo(extBar: 1)

Also in this case, can you explicitly tell the constructor to let parameters omit their external names

struct Foo2 {
    var bar : Int

    init(_ intBar: Int) {
        bar = intBar
    }
}

var a = Foo2(1)
like image 32
dfrib Avatar answered Nov 18 '22 00:11

dfrib