Currently in Swift¹ if you create a function with an optional input:
func takesOptional(foo: Foo?)
You cannot omit the
foo parameter and just call:
You have to type
takesOptional(foo: nil), which is wordy and doesn’t match with Swift’s behavior for variables, e.g.:
var foo: Foo? let bar = foo // works fine, bar is set to nil
real-world use case
While I think it’s totally reasonable to assume
nil for any optional function parameter (that doesn’t already have a value specified), it’s especially annoying that this isn’t the case in older or poorly-written APIs that haven’t been updated for Swift. For example NSImage has:
open func cgImage(forProposedRect proposedDestRect: UnsafeMutablePointer<NSRect>?, context referenceContext: NSGraphicsContext?, hints: [NSImageRep.HintKey : Any]?) -> CGImage?
Which (besides the cardinal sin of taking a CGRect by reference) doesn’t provide default
= nil values and so requires:
let cgImage = image.cgImage(forProposedRect: nil, context: nil, hints: nil)
Instead of the infinitely-nicer:
let cgImage = image.cgImage()
The only source incompatibility I can think of with this proposal is that in an existing API if there already exists two functions / methods which do fundamentally the same thing but one takes fewer parameters, there could be confusion (for the compiler and programmer) on which version would be used, e.g. if an API had:
func draw(shape: Shape, bounds: CGRect?) func draw(shape: Shape)
And the programmer wrote:
Which version would get called?
My solution to this (and Swift’s solution to a similar ambiguity) is that the one with fewer parameters would always match first — with the justification that if the original API author went to the trouble to write a version with fewer parameters she might have addd some special sauce to it that it’d be good to have.
I also think this case should cause a soft warning when the API is compiled (but not used), but note that this ambiguity can be replicated in current Swift, if instead you add a default value to the first function:
func draw(shape: Shape, bounds: CGRect? = nil) func draw(shape: Shape) draw(shape: triangle)
Here the second one would be called (at least as of Swift 5.1).
This is my first pitch! Please let me know if I missed anything! And thank you for your feedback.
¹ Swift 5.1 at this writing