Jeremy W. Sherman

stay a while, and listen

Intuition behind the Swift external/local parameter system

David Bryant Copeland picks out Swift’s external/local parameter system as something never before seen:

The notion of giving named parameters different names for the caller than are used in the implementation is not something I’ve seen before, and it’s kinda genius.

But further reflection convinced me that allowing different external and local parameter names is simply the Swift version of a common Objective-C practice.

External and Local Parameters in Obj-C

Consider these parallel Obj-C and Swift method declarations:

1
- (void)insertPerson:(Person *const)p atIndex:(const NSUInteger)i;
1
func insert(person p: Person, index i: Int)

The Obj-C version demonstrates “external” parameter names in the form of a verbose selector. In Swift, the selector components move into the parens as external parameter names.

The Obj-C formal parameter names are analogous to the local parameter names in Swift. The Swift external-then-local declaration order perfectly follows the Obj-C selector-chunk-then-argument order: Swift person p: Person vs. Obj-C Person:(Person *const)p.

Reaching back beyond even Obj-C, to C, this has been possible by exploiting the difference between a function prototype, commonly publicized in the header, and its implementation, commonly in a private implementation.

External and Local Parameters in C

In C, the only thing about arguments that the compiler cares about in a function prototype is the argument type; the names are purely documentary.

In the function implementation, so long as the types don’t change, you can name the formal parameters whatever you want.

So the C equivalent of the above would be:

1
2
3
4
5
/* prototype, in header */
void Insert(Person *const person, const NSUInteger index);

/* implementation, in .c file */
void Insert(Person *const p, const NSUInteger i)

About That Const

Take another look at the Obj-C and Swift versions of the function declaration:

1
- (void)insertPerson:(Person *const)p atIndex:(const NSUInteger)i;
1
func insert(person p: Person, index i: Int)

It’s uncommon to see const qualifiers on arguments in Obj-C. In this case, I was trying to remain faithful to the Swift default of const formal arguments.

You see, a Swift function declared like so:

1
func insert(person p: Person, index i: Int)

accepts an implicit let declaration of its parameters:

1
func insert(let person p: Person, let index i: Int)

The mutability-faithful version of the more common Obj-C declaration:

1
- (void)insertPerson:(Person *)p atIndex:(NSUInteger)i;

would similarly have mutable parameters in Swift:

1
func insert(var person p: Person, var index i: Int)

Swift parameters are const by default, and it’s great: it’s high time that was doable without stuttering const all over your codebase.

Summary

  • Swift’s external-local parameter declarations are a continuation of Obj-C selector chunk then parameter declarations: Obj-C insertPerson:(Person *const)p becomes Swift insert(person p: Person).
  • Swift function parameters are let-declared (const) by default. Qualify a parameter with var if you absolutely must have it mutable: insert(var person p: Person, var index i: Int).