Implicit "return nil" for Closures with an optional return type.
Introduction
We propose adding implicit return nil when Closure has optional return type and no other return statement is executed.
Motivation
As an example, check the following computed property:
var roleAndCompany: String? {
if let role, let company {
return "\(role) @ \(company)"
} else if let role {
return role
} else if let company {
return "@ \(company)"
} else {
return nil
}
}
With the proposed feature, we would be able to omit last else block with "return nil", since Closures with optional return types will implicitly have a nil value returned by default if no other return statement is called.
This would align with how optionals work in Swift. For instance, if you declare an optional property and never assign any value to it, its implicit default value is nil.
With proposed solution functions and computed properties with optional type will work the same and have nil value by default with no need for dedicated return nil statement.
This feature is present in Python, Ruby, Javascript, Lua and few other languages.
Other Information
I started discussing this feature on old thread started by @Logan_Sease, and I was suggested by @Jon_Shier and @xwu to create a dedicated topic.
With the help of SE-0255, SE-0345 and SE-0380 you could syntax optimise that fragment to:
var roleAndCompany: String? {
if let role, let company {
"\(role) @ \(company)"
} else if let role {
role
} else if let company {
"@ \(company)"
} else {
nil
}
}
Frankly I don't think omitting the last two lines is a good thing here.
@tera I am fully aware of SE-0255, SE-0345 and SE-0380. I wrote the example code with return statements for clarity purposes.
Do you have any specific concerns, or have in mind some specific problems implicit return nil feature for functions with optional return type would cause in your daily work?
I think this feature would align pretty well with recent Swift language features and especially with features implemented with proposals that you mentioned.
var roleAndCompany: String? {
switch (role, company) {
case let (role?, company?):
"\(role) @ \(company)"
case let (role?, nil):
role
case let (nil, company?):
"@ \(company)"
default:
nil
}
}
For consistency it would need to be permitted to omit the default: case. But Swift doesn't allow that for good reason - forgetting cases, or simply forgetting to finish a method implementation, are common programmer errors.
Omitting the return keyword is different, since doing so introduces no ambiguity w.r.t. the code's meaning and is much less likely to permit a typo.
So I tend to think implicit return values are not wise.
Please no. "Implicit" code make things harder to read and understand.
Also, a better way to write the example computed property is with a switch, that would require (for good reason) exhaustive case checking, so a nil would still be needed, even without return in Swift 5.9.
Strong negative for this. Swifts "explicitness" is one of its core feature. Implicit control flow is chaos because it makes it harder to reason about the code itself. You simply can "forget" this function returns nil as a default value when debugging
This would align with how optionals work in Swift. For instance, if you declare an optional property and never assign any value to it, its default value is nil.
That's not quite true. Optionals are initialized with value of nil for the same reason Int is initialized with 0. So it would not be initialized "garbage", for safety.
This feature is present in Python, Ruby, Javascript, Lua and few other languages.
And in all of those languages control flow is a mess, especially in python and ruby
I'm not sure what are you referring to, but yeah implicit nil value for optional properties is why I would like this idea implemented in first place. And yes, I do use this feature in other languages, and I do find it useful and logical.
My proposals is only about Closures (blocks) with optional return, omitting switch default case is cool idea, but its not within the scope of this proposal
To be honest I find this feature less evil in this regard than implicit return for one-liners. I think there were many more compromises needed to add such a language feature, and as well I understand all of the benefits.
Optional properties already have implicit default value of nil, I don't find it any more confusing if Closures with optional return type had implicit return nil.
How? Implicit return for one liners is definitely a much simpler concept that an implicit return in a complex conditional structure.
Only if they are var, and even that is not great, in fact you can opt out of the thing entirely by specifying, for example, Optional<Int> instead of Int?.
Also, I fail to see the relationship between complicating and obscuring control flow with implicit returns, and the fact that var optionals are initialized with a default nil value.
I didn't say it should compile. If you have a non exhaustive if expression, it's natural to implicitly transform its return type into an optional.
let number = if someCondition { 42 } // what's the return type?
Naturally it should be Int? as the else condition is not covered. It either has a value or not, which is what an Optional does.
We always have to answer the question: What does it enable us to do?
Aside from convenience here and there you could perform a flatMap like operation without the need to explicitly nil return every possible branch.
let transformedValue = if let nonOptional = value {
if let otherThing = nonOptional.someOptional {
transform(otherThing.value)
}
}
// instead of
let transformedValue = if let nonOptional = value {
if let otherThing = nonOptional.someOptional {
transform(otherThing.value)
} else {
nil
}
} else {
nil
}
The else { nil } is just redundant boilerplate code for if expressions.
If we'd do the same for non exhaustive switch expression then we can omit default: nil to achieve the same.
let value = switch something {
case .a:
"a"
case .b:
"b"
}
value is of type String? as we used a non exhaustive switch expression.
I personally think it's a natural extension for if and switch expressions, wether or not Swift will add those is a different story.
I found this example highly confusing, to even start comprehending what its "syntax optimised" version could be... what are you even trying to do here? I think you are mixing "if let x = ..." and "let x = if...". Please show the whole compilable example.
It's just something quick from the top of my head. Sorry if it confuses you. Also I'm not trying to do anything, it's just a hypothetical example, nothing more.
If I'm not mistaken the above could be rewritten as this:
let transformedValue = value
.flatMap(\.someOptional)
.map(\.value)
.map(transform)