[swift-evolution] Add an implicit return nil if function reaches end before return explicitly called

Gwynne Raskind gwynne at darkrainfall.org
Wed Jun 22 16:31:21 CDT 2016


I thought of this myself at one point, but I looked at a list of other languages to see if they did it and, if so, how much it actually improved anything.

The only language I could find that I have experience in which does this outside of closures was Bash shell scripting, and there wasn’t much to judge there because return doesn’t even mean the same thing in a shell script that it does in most programming languages. Nonetheless, it felt weird to me; lack of a return statement has always been for void functions ("there’s nothing *to* return").

Adding this to Swift would create a lot of confusing cases - if the return type is already Optional, can I then write "return" instead of "return nil" for places in the control flow that need to return without falling off the closing brace? If it isn’t, do I have to make it Optional, or will the compiler do that for me? If it does it for me, will it add a second level of Optional to the first one? ('cause while "Int??" (for example) might have uses, there are almost certainly better ways to express it.) Which of these options for behavior will be the least confusing? How do I tell the compiler "Don’t do that, warn me/error instead", especially when returning Optionals already? If I have to annotate functions I want explicit errors for, do I have to effectively put back a different form of the very @warn_unused_result attribute we just finally got rid of needing for the common case? How does this interact with error handling, especially in the presence of closures and "rethrows"? How does this interact with the implicit return from closures, and do closures now get the same semantics? Does "{}" in function type context now implicitly mean "{ ()->Void? in return nil }"? And if so, how can that change be justified given that it will change the semantics of many closures (workitems for DispatchQueue.async() come to mind) to be effectively wrong? If that isn’t the effect, how do you resolve the confusion developers will experience when they try to mix the enclosing function’s implicit return with a closure’s? What defines a function’s exit point for the purposes of the implicit return? The "end" of a function isn’t always where it seems to be. Can this be expressed reasonably by SIL in its current form without adding considerable extra logic to the compiler? Would this save enough code (a single, fairly short line per function) to justify so massive a semantic change, especially given that it violates the expectations of almost every language Swift typically replaces (C, C++, Objective-C, C#, Java, Perl, PHP, Python, Ruby, just to name a few)?

Ultimately I don’t feel like this would add anything but confusion to the language; couldn’t your example be rewritten as "func toInt(string: String?) -> Int? { return string?.intValue }"? Optional chaining would usually solve such cases more cleanly in my experience.

-- Gwynne Raskind



> On Jun 22, 2016, at 14:44, Logan Sease via swift-evolution <swift-evolution at swift.org> wrote:
> 
> I believe Swift should no longer require an explicit return on all functions and instead do an implicit nil return if the function reaches the end of its control flow and has an optional return type.
> 
> This could be useful to keep code clean and compact, by only having to write code for cases that our function handles and just returning nil otherwise automatically.
> 
> 
> Consider:
> 
> func toInt(string : String?) -> Int?
> {
> 	if let s = string
> 	{
> 		return s.intValue
> 	}
> 
> 	//Make this return implicitly happen instead of requiring it.
> 	//return nil 
> }
> 
> 
> 
> This also very much goes along with the implicit return within a guard statement that I have seen proposed. Here:
> 
> func toInt(string: String?) -> Int?
> {
> 	guard let s = string else {
> 		 //this could be implicitly returned using the same logic, since the guard means we have reached the end of our code path without returning
> 		//return nil 
> 	}
> 	return s.toInt()
> }
> 
> 
> These methods could be re-written as so:
> 
> This could allow us to write the examples below much cleaner
> func toInt(string : String?) -> Int?
> {
> 	if let s = string
> 	{
> 		return s.toInt()
> 	}
> }
> 
> func toInt(string: String?) -> Int?
> {
> 	guard let s = string else {} 
> 	return s.toInt()
> }
> 
> // it would be even cooler if we could omit the else {} and make them not it return by default. But that’s another thing all together
> func toInt(string: String?) -> Int?
> {
> 	guard let s = string
> 	return s.toInt()
> }
> 
> 
> Thanks for reading my first post to the Swift open source discussion board!
> -Logan
> 
> 
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution



More information about the swift-evolution mailing list