Avoid using var with this one weird trick

update: as of Swift 1.2, there is another way to do this, as you can now defer assignment to let.

Ever find yourself having to use var when a let would be better, because you’re having to choose between multiple options?

var direction: Direction
switch (char) {
    case "a": direction = .Left
    case "s": direction = .Right
    default:  direction = .Forward

var cry: String
if direction == .Forward {
    cry = "To glory!"
else {
    cry = "Tum-tee-tum"

Often you’ll see people declaring those vars as optionals set to nil, because they think you have to give it a value. This isn’t actually necessary – so long as the compiler can tell it will always be assigned to before it is used, as it is here, it’ll let you defer setting the value. But you still can’t use let instead of var.

Most people are aware of the ternary operator, that means you can replace the if version with this:

let cry = direction == .Forward ? "To glory!" : "Tum-tee-tum"

But this doesn’t scale at all well with multiple choices:

// hours of code formatting enjoyment available making this look sensible
let direction: Direction =
    char == "a" ? .Left
  : char == "s" ? .Right
  :               .Forward

But don’t despair, there’s a better way! Just wrap your switch in a closure expression:

let direction: Direction = { 
// you could put "_ -> Direction in" here instead of
// typing the let, if you prefer

    switch (char) {
    case "a": return .Left
    case "s": return .Right
    default:  return  .Forward

}() // call the expression immediately

There, both sanity and let preserved.

Of course, it’d be nice if there were a version of switch (and maybe even if) that was an expression, so you didn’t need the closure. Especially if it meant Swift could fully infer the type for you (with the closure trick, you have to give the type).

So that’s on my letter to Father Christmas. What would be really nice would be something like this:

let direction = case(char) {
    when "a" { .Left }
    when "s" { .Right }
    otherwise { .Forward }

Calling the outer keyword case (and the default otherwise) is lifted from LISP. This would help differentiate it from the switch statement format, to avoid confusion and backwards-compatibility troubles. Ditching the : and replacing it with braces seems more consistent with the rest of Swift syntax rather than sticking with a hangover from C.1 The blocks against the when clauses could follow the same pattern as closures, i.e. automatically return if they’re single expressions.2 The compiler would need to detect when the clauses returned incompatible types and throw an error (the ternary operator behaves similarly).

Anyway, there’s a glass of sherry waiting for Santa if he visits.3 In the meantime, give the closure trick a go.

  1. This idea courtesy of @OldManKris 
  2. Maybe they should even be closures (and you could pass function names instead), not sure about this one. 
  3. I think the Swift dev team have posted on the forums acknowledging switches-as-expressions would be useful and that they’ll probably get to it some time.