11

When reading Swift's documentation, Apple commonly uses functionName(_:name:) or something similar. What exactly is this pattern that sometimes is _:_:, sometimes just _:, and _:name:. I think it has to do with parameter shorthand, but I'm not sure, and can't find the explanation in Swift's programming guide. Thank you!

Example:

insert(_:atIndex:)
rb612
  • 5,280
  • 3
  • 30
  • 68

4 Answers4

12

The underscore indicates that there is no external parameter name for the function. Apple's Swift Documentation talks about this concept in terms of when you're writing your own functions.

Take the case where you write this function (from the documentation):

func sayHello(to person: String, and anotherPerson: String) -> String { ... }

If you were to use the function you would write the following:

sayHello(to: "Bill", and: "Ted")

The signature for this would be sayHello(to:and:). However, what if we wanted to use the function as sayHello("Bill", "Ted")? How would we indicate that we didn't want an external parameter name? Well that's where the underscore comes in. We could rewrite the function as:

func sayHello(person: String, _ anotherPerson: String) -> String { ... }

Note the first parameter doesn't need the _, but subsequent ones will. The first is inferred to have no parameter name. This makes the method signature for this call sayHello(_:_:) because you as the caller don't have a named parameter.

Update Swift 3.0:

Swift 3.0 treats all parameters equally. The first parameter now requires an underscore to indicate the absense of an external parameter name. In the above example of having sayHello("Bill", "Ted") at the call site, your corresponding function or method declaration would have to be

func sayHello(_ person: String, _ anotherPerson: String) -> String { ... }

Note the addition of the underscore before the internal parameter name 'person'.

Lucas Derraugh
  • 6,929
  • 3
  • 27
  • 43
  • 1
    Great answer! Thanks! – rb612 Jun 25 '15 at 03:14
  • How is your last definition different than `func sayHello(person: String, anotherPerson: String) -> String { ... }` ? – 7stud Jun 25 '15 at 03:46
  • @7stud As of Swift 1.2, it depends if you're making a function or a method. If you make a standalone function, it isn't any different. However, if you declare this inside of a class (making it a method), it will assume that the second parameter (and subsequent parameters) with only the internal name also serve as the external name. Basically, if `func sayHello(person: String, anotherPerson: String) -> String` is written as a function, it becomes `sayHello(_:_:)`. In a class as a method it becomes `sayHello(_:anotherPerson:)`. I think Swift 2 made a change to this though. – Lucas Derraugh Jun 25 '15 at 03:52
  • Thanks. I'm adding an answer and quoting the docs to explain that "feature". – 7stud Jun 25 '15 at 03:58
  • @7stud I'm pretty sure this has been changed though in Swift 2 so that function syntax works the same way as method syntax. Just fyi. – Lucas Derraugh Jun 25 '15 at 04:03
  • Okay. Then for swift version 1.2 people the explanation is in an answer. – 7stud Jun 25 '15 at 04:06
1

The underscore indicates an ignored value. You can read more about it here: What's the _ underscore representative of in Swift References?

In the documentation it is being used as a wildcard to indicate a function that takes an unnamed parameter.

Community
  • 1
  • 1
Subcreation
  • 1,353
  • 12
  • 26
0

share my learning experience.

//MARK: Default
func join1(s1: String, s2: String, joiner: String) -> String {
    println("join1" + s1 + joiner + s2)
    return s1 + joiner + s2
}

//MARK: External Parameter Names
// To make the purpose of these String values clearer, define external parameter names for each join function parameter
func join2(string s1: String, toString s2: String, withJoiner joiner: String) -> String {
    println("join2" + s1 + joiner + s2)
    return s1 + joiner + s2
}

//MARK: External Names for Parameters with Default Values
func join3(string s1: String, toString s2: String, withJoiner joiner: String = " ") -> String {
    println("join3" + s1 + joiner + s2)
    return s1 + joiner + s2
}

//MARK: No External Names for Parameters with Default Values
func join4(s1: String, s2: String, joiner: String = " ") -> String {
    println("join4" + s1 + joiner + s2)
    return s1 + joiner + s2
}

//MARK: instead of an explicit external name
// You can opt out of this behavior by writing an underscore (_) instead of an explicit external name when you define the parameter. However, external names for parameters with default values are preferred.

func join5(s1: String, _ s2: String, _ joiner: String = " ") -> String {
    println("join5" + s1 + joiner + s2)
    return s1 + joiner + s2
}

//MARK: Shorthand External Parameter Names
// Provide an external parameter name with the same local parameter name
// Swift provides an automatic external name for any parameter that has a default value. The automatic external name is the same as the local name, as if you had written a hash symbol before the local name in your code.

func join6(#s1: String, s2: String, joiner: String = " ") -> String {
    println("join6" + s1 + joiner + s2)
    return s1 + joiner + s2
}

And you can see how to use them

join1("s1", s2: "s2", joiner: "-")

join2(string: "s1", toString: "s2", withJoiner: "-")

join3(string: "s1", toString: "s2", withJoiner: "-")
join3(string: "s1", toString: "s2")

join4("s1", s2: "s2", joiner: "-")
join4("s1", s2: "s2")

join5("s1", "s2", "-")
join5("s1", "s2")

join6(s1: "s1", s2: "s2", joiner: "-")
join6(s1: "s1", s2: "s2")
Reming Hsu
  • 2,215
  • 15
  • 18
-1

From the Swift docs:

Function parameters can have both a local name (for use within the function’s body) and an external name (for use when calling the function), as described in External Parameter Names. The same is true for method parameters, because methods are just functions that are associated with a type. However, the default behavior of local names and external names is different for functions and methods.
...
...

Specifically, Swift [makes] the first parameter name in a method a local parameter name by default, and [makes] the second and subsequent parameter names both local and external parameter names by default. This convention matches the typical naming and calling convention you will be familiar with from writing Objective-C methods, and makes for expressive method calls without the need to qualify your parameter names.

Consider this alternative version of the Counter class...

class Counter {
    var count: Int = 0
    func incrementBy(amount: Int, numberOfTimes: Int) {
        count += amount * numberOfTimes
    }
}

This incrementBy(_:numberOfTimes:) method has two parameters—amount and numberOfTimes. By default, Swift treats amount as a local name only, but treats numberOfTimes as both a local and an external name. You call the method as follows:

let counter = Counter()
counter.incrementBy(5, numberOfTimes: 3)
// counter value is now 15

You don’t need to define an external parameter name for the first argument value, because its purpose is clear from the function name incrementBy. The second argument, however, is qualified by an external parameter name to make its purpose clear when the method is called.

This default behavior effectively treats the method as if you had written a hash symbol (#) before the numberOfTimes parameter:

func incrementBy(amount: Int, #numberOfTimes: Int) {
    count += amount * numberOfTimes
}

The default behavior described above means that method definitions in Swift are written with the same grammatical style as Objective-C, and are called in a natural, expressive way.

So, to nullify the external name of the second parameter of a method you can explicitly write '_' for the external name:

class Counter {
    var count: Int = 0
    func incrementBy(amount: Int, _ numberOfTimes: Int) {
        count += amount * numberOfTimes
    }
}

Now the syntax for the method name becomes:

incrementBy(__:__:)

The syntax tells you how to call the method. In this case, the syntax tells you that there are two un-named method arguments, so you call the method like this:

 incrementBy(3, 2)

If the method name is incrementBy(_:numberOfTimes:), the syntax tells you that the first argument is un-named while the second argument's name is numberOfTimes, so you call the method like this:

 incrementBy(3, numberOfTimes:2)
7stud
  • 46,922
  • 14
  • 101
  • 127