Does assigning a function to a variable remove the default value of a parameter?

In the following code, why does _something() not work? I get this

error: missing argument for parameter #1 in call

I am concluding that assigning a function to a variable removes the default value of a parameter? Can anyone explain to me why / how this is beneficial and / or link me references when I can read more about this?

func something(_ i: Int? = nil) {
    print(i == nil ? "Optional Int" : "Int")
}


something(1)  // Int
something(nil)  // Optional Int
something()  // Optional Int

let _something = something
_something(1)  // Int
_something(nil)  // Optional Int
_something()  // error: missing argument for parameter #1 in call

Output of swift --version

swift-driver version: 1.87.3 Apple Swift version 5.9.2 (swiftlang-5.9.2.2.56 clang-1500.1.0.2.5)
Target: arm64-apple-macosx14.0

Edit:

If I give a name to the parameter then I get the following. Why did I lose the name information here? I can call it without the name _something(1) instead of _something(i: 1), but I am failing to understand the reasoning and benefits (or lack of?) in this behavior.

func something(i: Int? = nil) {
    print(i == nil ? "Optional Int" : "Int")
}


something(i: 1)  // Int
something(i: nil)  // Optional Int
something()  // Optional Int

let _something = something
// _something(i: 1)  // error: extraneous argument label 'i:' in --- correct way now is _something(1)
// _something(i: nil)  // error: extraneous argument label 'i:' in --- correct way now is _something(nil)
// _something()  // error: missing argument for parameter #1 in --- correct way now ???
1 Like

Yes, the default is attached to the declaration and gets lost when you convert it to an arbitrary value. Not doing so would either mean introducing a specific type for each function—Rust does this, though it doesn't have default values—or complicating closure types quite a bit!

(One thing that would be reasonable to do is allow specifying an explicit type to apply the default up front when converting to a function value. Today you have to use a full closure expression to do that.)

2 Likes

I tried it but it seems we still can't do it on Swift 5.9.2

let s1: (_ i: Int?) -> Void = something
s1(1)
s1(nil)

let s2: (_ i: Int) -> Void = something
s2(1)

// ❌ Default argument not permitted in a tuple type
let s3: (_ i: Int? = nil) -> Void = something 
1 Like

Right, you have to write let s2 = { something() } if you want to leave the argument out. There's no way to have the default in the function type, because that would significantly complicate the implementation, even if it would be convenient.

3 Likes

While we are on this, the following would be also nice to have:

// NOT current Swift:
func something(i: Int? = nil) { ... }
let s: () -> Void = something // ✅
1 Like