(Seems like this is something that ought to have been pitched before? But I found no thread...)
For many years I've been wanting to be able to have a convenient way of "inferring" / "inheriting" / "following" / "reverse-forwarding" the default value of a parameter, either to init or other function.
Imagine we have syntax for conveniently working with Date, like so 30.years.ago, look at this simple struct Person:
public struct Person {
public let firstName: String
public let lastName: String
public let dateOfBirth: Date
public init(
firstName: String,
lastName: String,
dateOfBirth: Date = 30.years.ago
) {
self.firstName = firstName
self.lastName = lastName
self.dateOfBirth = dateOfBirth
}
public static func unknown(
dateOfBirth: Date = 30.years.ago
) -> Self {
Self.init(firstName: "Jane", lastName: "Doe", dateOfBirth: dateOfBirth)
}
}
See how we are duplicating the value 30, it is specified twice. But imagine we semantically mean to use the same value. We solve this ofc either by doing this:
public struct Person1 {
...
public static let defaultDateOfBirth: Date = 30.years.ago
public init(
firstName: String,
lastName: String,
dateOfBirth: Date = Self.defaultDateOfBirth
) {
...
}
public static func unknown(
dateOfBirth: Date = Self.defaultDateOfBirth
) -> Self {
Self.init(firstName: "Jane", lastName: "Doe", dateOfBirth: dateOfBirth)
}
}
Or by doing this:
extension Date {
public static let defaultDateOfBirth: Self = 30.years.ago
}
public struct Person2 {
...
public init(
firstName: String,
lastName: String,
dateOfBirth: Date = .defaultDateOfBirth
) {
...
}
public static func unknown(
dateOfBirth: Date = .defaultDateOfBirth
) -> Self {
Self.init(firstName: "Jane", lastName: "Doe", dateOfBirth: dateOfBirth)
}
}
This is ofc a very very simple scenario with just one default value for a parameters, but imagine we have 3 or 4 or more even. Both version struct Person1 and struct Person2 solution have their own drawback.
The problem with struct Person1 is that we are "polluting" the struct with static lets for the default values. The problem with struct Person2 is that we are "polluting" the type Date with domain specific values. And since both init and static func unknown are public the extension inside Date need also be public (otherwise this solution would not have been that bad).
Default values for parameters are awesome when used in just one place, because the do not have any of struct Person1 nor struct Person2s problem, the default value is just declare with = .defaultValue which is perfect, exactly what we want, but as soon as we need to reference semantically this default value in more than one place, we must retort to any of the two bad solutions.
But what if, we could with some compiler magic get this syntax possible?
public struct Person3 {
...
public init(
firstName: String,
lastName: String,
dateOfBirth: Date = 30.years.ago
) {
...
}
public static func unknown(
dateOfBirth: Date = .inferred // not possible today, and this is what I pitch!
) -> Self {
Self.init(firstName: "Jane", lastName: "Doe", dateOfBirth: dateOfBirth)
}
}
In struct Person3's func unknown we spell out the default argument as .inferred, which tells Swift that most likely during compilation (I'm not a compiler engineer, but this seams reasonable?), replace .inferred with = 30.years.ago from the function (init) called!
And this would also work in several steps/hops, so this would also work:
public struct Person3 {
...
public init(
firstName: String,
lastName: String,
dateOfBirth: Date = 30.years.ago
) {
...
}
public static func doe(
firstName: String,
dateOfBirth: Date = .inferred
) -> Self {
Self.init(firstName: firstName, lastName: "Doe", dateOfBirth: dateOfBirth)
}
public func unknown(
isMale: Bool,
dateOfBirth: Date = .inferred
) -> Self {
Self.doe(firstName: isMale ? "John" : "Jane", dateOfBirth: dateOfBirth)
}
}
What do you think? Have you found yourself in need of this ever?