In Swift, "switch" should be an expression not a statement

Originator:alexisgallagher
Number:rdar://17258614 Date Originated:2014-06-10
Status:Open Resolved:
Product:Swift Product Version:beta
Classification: Reproducible:
 
It is wonderful that Switch has a switch statement which can be used for rich pattern matching.

## suggestion: switch should be an expression, not a statement

Respectfully, I'd like to suggest that it would also be great if their were a kind of "switch" which was not a statement but an expression that returned a value. You could then use these these switch-expressions to create compact inline value transformers without creating any unnecessary temporary mutable values.

## why this is a good idea

Consider the example from  The Swift Programming Language (p 516):

   switch self {
   case .Ace:
       return Values(first: 1, second: 11)
   case .Jack, .Queen, .King:
       return Values(first: 10, second: nil)
   default:
       return Values(first: self.toRaw(), second: nil)
       }
   }
   
The pattern-match here is lovely but it is awkward when you consider the likely case that you are not always writing this at the end of a function when you return value, and that usually you actually want to do something with the value immediately. Then you have to do:

   var temporary:Values! = nil
   switch self {
   case .Ace:
       temporary = Values(first: 1, second: 11)
   case .Jack, .Queen, .King:
       temporary = Values(first: 10, second: nil)
   default:
       temporary = Values(first: self.toRaw(), second: nil)
       }
   }

This works, and now I can use the value in "temporary". But there are many problems: 1. I've had to use an implicitly unwrapped optional just to get hold of my result. 2. The compiler will not use its exhaustiveness check to ensure I've assigned to the optional in all the cases. 3. It's misleading, since I know that the temporary will be constant but now it shows up in code as a variable. 4. For the same reason as 3, it wastes an optimization opportunity.

## possible syntax

Instead, we could let case statements implicitly return their last value, like so:

   let result =  switch self {
     case .Ace:
         Values(first: 1, second: 11)
     case .Jack, .Queen, .King:
         Values(first: 10, second: nil)
     default:
         Values(first: self.toRaw(), second: nil)
     }

Better, yes? Of course! And when type inference can determine the returned type, the syntax is exactly like a switch statement.

But I admit it does raise two questions: how to make this idea work when you need to provide type annotations? and with the existing switch statement more generally?

## syntax to indicate type of the return value

In cases where it is necessary to provide type annotations of the type returned by this switch form, I suggest using a syntax similar to what you've developed for closures:

   let result =  switch (self:Rank) -> Values {
     case .Ace:
         Values(first: 1, second: 11)
     case .Jack, .Queen, .King:
         Values(first: 10, second: nil)
     default:
         Values(first: self.toRaw(), second: nil)
     }

What I like about this syntax is that it looks like this "switch" is an anonymous function definition which transforms its argument expression to its return value -- which is just what it is.

What about the case where people don't care about the return value, and want switch to behave like a statement?

I'm not sure how to handle this, but here are a couple possibilities.

## compatibilty with switch as a statement

### option 1: require explicitly declaring -> Void

One option is that explicitly declaring that the switch statement returns Void is effectively asking it to be treated like a statement, just like before

   switch self -> Void {
   case .Ace:
       doSomethingHere() // irrelevant what I return
   case .Jack, .Queen, .King:
       doSomethingElse() // irrelevant what I return
   default:
       doAnotherThing() // irrelevant what I return
       }
   }

This is bad becomes it changes what people are used to right now. Fortunately, this is not necessary!

### option 2: implicitly return Void when no return type can be inferred

Another option is that a switch statement implicitly defaults to returning Void if the case expressions do not have a common return type:

   switch self {
   case .Ace:
       doSomethingHere() // might return Foo
   case .Jack, .Queen, .King:
       doSomethingElse() // return Void
   default:
       doAnotherThing() // might return Bar
       }
   }

Since there is no type here that can be inferred which works for both Foo, Void, and Bar, the language interprets this as indicating the switch expression should return Void. And now it acts exactly like a normal switch statement!

In other words, I think you could modify the language so that switch behaved like an expression, returning a value, in a way that would not break the existing grammar at all! And the new syntax that would be needed when the return type cannot be inferred, or when someone wanted to provide an additional annotation to the type of the switch expression's argument, is (I think) quite consistent with the syntax the language uses elsewhere for function definition.

Comments

Also, http://openradar.appspot.com/17257897

By alexisgallagher at June 10, 2014, 10:58 p.m. (reply...)

Please note: Reports posted here will not necessarily be seen by Apple. All problems should be submitted at bugreport.apple.com before they are posted here. Please only post information for Radars that you have filed yourself, and please do not include Apple confidential information in your posts. Thank you!