Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to have a default function parameter referring to another function parameter?

Tags:

swift

I have a function like below:

func delay(_ description: String, deadline: Double = 2.0, timeout: Double = 3.0) {
...    
}

It'd make sense for the timeout parameter to be based on the deadline parameter as below to make it more foolproof:

func delay(_ description: String, deadline: Double = 2.0, timeout: Double = deadline + 1.0) {
...   
}

This will say however two errors:

Cannot find 'deadline' in scope

Failed to produce diagnostic for expression; please submit a bug report (https://swift.org/contributing/#reporting-bugs) and include the project

The official doc doesn't say anything about this: https://docs.swift.org/swift-book/documentation/the-swift-programming-language/functions/#Default-Parameter-Values

I guess it's unsupported. I know how to workaround it within the function body, but I just want to make sure that I'm trying it in the proper way. Is there a way to make it work like this? I couldn't find explicitly this being discussed elsewhere.

I also tried to not make the other parameter to have a default value, but it's the same outcome:

func delay(_ description: String, deadline: Double, timeout: Double = deadline + 1.0) {
...   
}

This question below is somewhat similar and the accepted reply is somewhat covering my question as well, however I'm still not convinced that my above try is a bad expectation. Swift: Class parameter as default function parameter

And this might answer my question, but I'm just more confused: When are Swift function default parameter values evaluated?

like image 881
grabz Avatar asked Jun 15 '26 06:06

grabz


2 Answers

This has been discussed here on forums.swift.org, and your guess is correct - it is not supported.

Implementing such a feature would affect the ABI. To quote one of the comments,

I think this might pose fun ABI problems. Specifically, Swift function default arguments are produced by little function thunks that the caller can call. These functions are currently zero-argument: to have them depend on earlier arguments, they'd need to take earlier arguments as their own arguments. Put another way, these little thunks would now be able to have an arity other than 0.

This change would mean that function default arguments sometimes leak into the ABI, at least in terms of whether they depend on a previous value. That seems like a bit of a subtle footgun to me. It's definitely manageable (tooling can report it if you get this wrong), but I do think it would be quite hard to communicate and likely surprising to people.

What this basically means is that when you don't pass an optional parameter in your source code, the compiler actually inserts a call to a thunk (a little function that evaluates the default parameter):

delay("foo")
// is actually
delay("foo", deadline: thunkForParam2(), timeout: thunkForParam3())
// thunkForParam2 and thunkForParam3 are generated by the compiler

If timeout now depends on deadline, the thunks now needs to be changed take parameters:

let param2 = thunkForParam2()
delay("foo", deadline: param2, timeout: thunkForParam3(param2))

Anyway, a simple workaround in your case is to just make the parameter optional and compute it inside the function:

func delay(_ description: String, deadline: Double = 2.0, timeout: Double? = nil) {
    let actualTimeout = timeout ?? deadline + 1
    // ...
}

Many existing APIs do something like this too, using nil to mean "some implementation-defined default behaviour".

like image 96
Sweeper Avatar answered Jun 16 '26 20:06

Sweeper


It is not possible in Swift to have a default parameter depend on the value of another parameter. But you can achieve the same effect by having two versions of your function:

func delay(_ description: String, deadline: Double = 2.0) {
    delay(description, deadline: deadline, timeout: deadline + 1.0)
}

func delay(_ description: String, deadline: Double = 2.0, timeout: Double) {
    ...   
}

The second version does all the work. Note that its timeout parameter does not have a default value. The first version is a wrapper which calculates the timout based on the deadline.

I saw @Sweeper’s answer after I posted mine. I think that solution is better because it does not duplicate the default value for deadline.

like image 27
Geoff Hackworth Avatar answered Jun 16 '26 21:06

Geoff Hackworth