Why does a function call require the parameter name in Swift?

28,334

Solution 1

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.

Solution 2

The parameter names in the function call are called keyword names, and they are trace their roots back to the Smalltalk language.

Classes and objects are often re-used from somewhere else, or form part of very large complex systems, and will not have active maintenance attention for long periods at a time.

Improving the clarity and legibility of the code is very important in these situations, as code often ends up as the only documentation, when developers are under deadline pressure.

Giving each parameter a descriptive keyword name allows maintainers to quickly see what the purpose of a function call by glancing at the function call, as opposed to delving deeper into the function code itself. It makes the implied meaning of the parameters explicit.

The latest language to adopt keyword names for parameters in function calls is Rust (link) - described as "a systems programming language that runs blazingly fast, prevents segfaults, and guarantees thread safety."

High uptime systems require greater code quality. Keyword names allow development and maintenance teams much more opportunity to avoid and to catch errors from sending the wrong parameter, or calling parameters out of order.

They can be wordy or terse, but Smalltalkers prefer wordy and descriptive to terse and meaningless. They can afford to be, because their IDE will do the bulk of such typing for them.

Solution 3

since you used calculator.multiply() in the example code I'm assuming this function is a method of the calculator object.

Swift inherits a lot of things from objective-c and this is one of them:

When in objective-c you would do (hypothetically):

[calculator multiply:@9834 factor2:@2321];

the equivalent in Swift is:

calculator.multiply(9834, factor2:2321);

Solution 4

Because your "multiply" function is a method, and like Objective-c, the parameters in methods are part of the name.

For example you can do this.

class Calculator {

    func multiply(factor1:Int, factor2:Int) -> Int{
        return factor1 * factor2
    }

    func multiply(factor1:Int, factor2:Int, factor3:Int) -> Int{
        return factor1 * factor2 * factor3
    }

}

Here there are two different methods, with different names, multiply(factor2) and multiply(factor2 factor3).

This rule only apply to methods, if you declare this like a functions outside of a class, then the function call don't require parameter name.

Share:
28,334
67cherries
Author by

67cherries

I've been programming for iOS for about four years. I've worked with objective-c, swift, python, java and javascript SO milestones: 6576th to the Strunk & White Badge :D

Updated on July 05, 2022

Comments

  • 67cherries
    67cherries about 2 years

    I have this Function in a class:

    func multiply(factor1:Int, factor2:Int) -> Int{
        return factor1 * factor2
    }
    

    I try to call the function using this:

    var multResult = calculator.multiply(9834, 2321)
    

    The problem is that the compiler wants it to look more like this:

    var multResult = calculator.multiply(9834, factor2: 2321)
    

    Why does the first one cause an error?

  • 67cherries
    67cherries about 10 years
    I plan on accepting this answer. I took a look at the documentation and the example they used there makes a bit more sense than mine did.
  • Jean-Philippe Pellet
    Jean-Philippe Pellet about 10 years
    I’ve added interoperability notes and the rules for init methods.
  • sivabudh
    sivabudh about 10 years
    Awesome explanation. Thanks!
  • Kokodoko
    Kokodoko almost 10 years
    But what is the difference between multiply1(f1: Double) and multiply1(f1param: Double)? Obviously the name of the parameter is different but why would that matter?
  • Kokodoko
    Kokodoko almost 10 years
    But that still doesn't explain why only the first parameter name can be omitted? Why can't I just add the parameter name for all parameters to keep things readable? This seems to be vintage code from the old Objective C, where the parameter was part of the function name. This caused functions to have very long names. It's quite odd that we still have to understand how Objective C works, if we want to learn Swift.
  • Kokodoko
    Kokodoko almost 10 years
    Quite confusing for people who have not worked with Objective C :)
  • Jean-Philippe Pellet
    Jean-Philippe Pellet almost 10 years
    It doesn’t. You just have to use whatever name you define.
  • Daniel
    Daniel almost 10 years
    The first parameter is omitted because standard names for methods in cocoa looks like: func multiplyFactor1(factor1: Int, factor2:Int) {} So, when you call the method, you omitted the first parameter for avoid repeat it’s name. myCalculator.multiplyFactor1(2, facto2:2) Is more readable than. myCalculator.multiplyFactor1(factor1: 2, facto2:2)
  • diegomen
    diegomen over 9 years
    Just one more interesting thing, if you want to show the first parameter name when using the method, you have to define it like this: func method(#firstParameter: type, secondParameter: Type) Doing this, when calling the fun it will appear like that: self.method(firstParameter: 3, secondParameter: 5) If you don't add the # it will appear like that: self.method(3, secondParameter: 5)
  • MK Yung
    MK Yung about 9 years
    But what is the rationale behind this design? Is it trying to prevent us from confusing the order of parameters? If so, why doesnt a function have such limitation?
  • Bastian
    Bastian almost 9 years
    This is better than the official documentation. Thank you for this great splendid answer!
  • Kof
    Kof over 8 years
    Since Swift 2: '#' has been removed from Swift; double up 'param param' to make the argument label the same as the parameter name
  • Euan M
    Euan M over 8 years
    Squeak isn't a "kiddy language". It's a fully-featured language, VM, GUI environment and IDE, that people have used to implement kid-friendly systems like Scratch, E-Toys and Dr-Geo, the mathematical function visualiser for teaching high-school mathematics.. It also has been used to implement: the Seaside web framework - a framework that does away with all problems to do with people using the 'back' and 'forward' buttons in the browser; DabbleDB; and much more.
  • Ankit Srivastava
    Ankit Srivastava over 8 years
    @Kokodoko it is more confusing for people who have worked with objective-c for 5 years :P
  • Kokodoko
    Kokodoko over 8 years
    For all the explanations given on this page, it still doesn't make much sense.... omit the first parameter because the function name already includes that? (it doesn't, not in the multiply example). Also, if Scratch omits the first parameter type because it's so confusing, why didn't they omit all types? Doesn't that make it more confusing? Sorry, just ranting here.
  • Edison
    Edison almost 8 years
    Since you're talking about initializing and methods could I please ask you to look at my question. I am trying to call an external function and am pretty sure I am required to implement initialization in order to call it. I've even set a bounty and no one seems up to the challenge. Thank you very much. stackoverflow.com/questions/38711226/…
  • Abhinav Parab
    Abhinav Parab over 6 years
    This is a good explanation. Regarding the language design, this does not justify the added verbosity since we can see the parameter names and the method description with Alt + Left Click.
  • dcow
    dcow over 6 years
    When I try to label function parameters in a typealias I get a compiler error. Has this changed?
  • Kal
    Kal almost 6 years
    Since Swift 3 of course, the first parameter behaves like all the rest, requiring an argument label unless the function or method uses _ (underscore) in place of an external name.