Using and expressing "case let" statements

After seveal years with Swift, I still find it hard to use the if case let or while case let, even worse with opotional pattern if case let x?.


So, I would like to find an expression to "speak" case let more naturally.

Presently, to be sure of what I do, I have to menatlly replace the if case by the full switch statement, with a single case and default ; pretty tedious.


I thought of canMatch or canMatchUnwrap … with:


So, following would read


if case let x = y { // if canMatch x with y


if case let x? = someOptional { // if canMatchUnwrap x with someOptional


while case let next? = node.next { // while canMatchUnwrap next with node.next


Am I the only one with such problem ? Have you found a better way ?

Accepted Reply

have others found a trick to make it easier to think about

I think this breaks down into two steps:

  1. Understanding patterns in general.

  2. Dealing with the

    if case
    syntax itself.

For 2, it took me a while to remember the syntax (and, specifically, to remember that the value I’m testing goes on the right of the

=
) but now I don’t need any further mnemonics.

For 1, I created myself a cheat sheet that summarises the 8 different pattern grammars and then refer to that (I can’t post it here, alas, because it’s intimately connected with other text that’s in no state to share).

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Replies

I don’t think anyone would claim that the

if case
syntax is easy to learn )-: However, I’m not sure about the purpose of your post. The examples you posted confirm that you do actually understand the syntax, which raises two possibilities:
  • You’re asking whether there’s a better syntax that you can use right now.

  • Or you’re asking whether we can improve the language in the future.

The answer to the former is “No.” And if you’re looking to discuss the latter, your best option is to read up on the Swift Evolution Process.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Thanks Quinn.


Yes, I (hope) understand the syntax. But it is a serious effort each time to use (really powerful not very natural)).


So my point was : have others found a trick to make it easier to think about ; I'm not thinking of language change.


Maybe I just have to train myself more until it becomes natural ? 😉

have others found a trick to make it easier to think about

I think this breaks down into two steps:

  1. Understanding patterns in general.

  2. Dealing with the

    if case
    syntax itself.

For 2, it took me a while to remember the syntax (and, specifically, to remember that the value I’m testing goes on the right of the

=
) but now I don’t need any further mnemonics.

For 1, I created myself a cheat sheet that summarises the 8 different pattern grammars and then refer to that (I can’t post it here, alas, because it’s intimately connected with other text that’s in no state to share).

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Thanks.


I'm sure you internal document would have been much useful.

I'm sure [your] internal document would have been much useful.

Pack the bags it’s guilt trip time! (-:

Seriously though, I had some time this week so I decided to tidy this up and post it here. And, on the plus side, creating and verifying all the examples below gave me a much better understanding of how patterns work (-:

IMPORTANT This isn’t an “internal document” but rather a cheat sheet that I created for my own personal use. I hope you find it useful too, but be aware that it isn’t an official Apple document, hasn’t been formally reviewed, and if it contains any mistakes then they’re all my fault!

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"


There are eight types of patterns, summarised in the table below.

TypeKindType Annotation?Syntax Summary
wildcarddestructuringoptional_
identifierdestructuringoptionalidentifier
value binding[1]no( var
tuple[1]optional( ( [ identifier : ] pattern )… )
enumeration casematchingno[ type-identifier ] . enum-case-name [ tuple-pattern ]
optionalmatchingnoidentifier ?
type castingmatchingnois type
expressionmatchingnoexpression

Notes

  1. These patterns are destructuring if any of their sub-patterns are destructuring.

Destructuring patterns allow you to pull apart a structured value. The can be used in any context. In contrast, matching patterns can only be used in:

  • The case of a switch statement
  • The catch clause of a do statement
  • ( if | while | guard | for ) case statements

A type annotation is a colon followed by a type. For example, consider the following:

let answer: Int = 42
                ^^^^ initializer
          ^^^^^      type-annotation
    ^^^^^^           identifier-pattern
    ^^^^^^           pattern
^^^^^^^^^^^^^^^^^^^^ constant-declaration

Note In these examples the syntax terms are taken directly from The Swift Programming Language.


Wildcard Pattern

A common use for a wildcard pattern is in a switch statement where you want to do a wildcard match for all possible values.

switch (indexPath.section, indexPath.row) {
case (0, 0): … handle a fixed item in section 0 …
case (0, 1): … handle a fixed item in section 0 …
case (0, 2): … handle a fixed item in section 0 …
case (1, 0): … handle a fixed item as the first item of section 1 …
case (1, _): … handle a variable number of items following that …
default: fatalError()
}

This wildcard case breaks down as follows:

case (1, _):
         ^   wildcard-pattern
         ^   pattern
      ^      expression-pattern
      ^      pattern
     ^^^^^^  tuple-pattern
     ^^^^^^  pattern
     ^^^^^^  case-item-list
^^^^^^^^^^^^ case-label

Identifier Pattern

You use the identifier pattern with every if let statement.

if let message = message {
    print(message)
}

The pattern breaks down as follows:

let message = message
            ^^^^^^^^^ initializer
    ^^^^^^^           identifier-pattern
    ^^^^^^^           pattern
^^^^^^^^^^^^^^^^^^^^^ optional-binding-condition
^^^^^^^^^^^^^^^^^^^^^ condition
^^^^^^^^^^^^^^^^^^^^^ condition-list

Note that this is not a value binding pattern; the let is part of the optional-binding-condition construct.

Value Binding Pattern

The following contains two instances of the value binding pattern.

switch (indexPath.section, indexPath.row) {
case (0, let row): … handle all items in section 0, using `row` as an index …
case let (section, _): … handle all remaining sections, using `section` …
}

The first instance breaks down as follows:

case (0, let row):
             ^^^   identifier-pattern
             ^^^   pattern
         ^^^^^^^   value-binding-pattern
      ^            expression-pattern
      ^            pattern
     ^^^^^^^^^^^^  tuple-pattern
     ^^^^^^^^^^^^  pattern
     ^^^^^^^^^^^^  case-item-list
^^^^^^^^^^^^^^^^^^ case-label

And the second:

case let (section, _):
                   ^   wildcard-pattern
                   ^   pattern
          ^^^^^^^      identifier-pattern
          ^^^^^^^      pattern
         ^^^^^^^^^^^^  tuple-pattern
         ^^^^^^^^^^^^  pattern
     ^^^^^^^^^^^^^^^^  value-binding-pattern
     ^^^^^^^^^^^^^^^^  pattern
     ^^^^^^^^^^^^^^^^  case-item-list
^^^^^^^^^^^^^^^^^^^^^^ case-label

Tuple Pattern

This snippet uses a tuple pattern to destructure the tuple coming back from enumerated()

for (offset, element) in ["Hello", "Cruel", "World!"].enumerated() {
    …
}

The pattern breaks down as follows:

(offset, element)
         ^^^^^^^  identifier-pattern
         ^^^^^^^  pattern
 ^^^^^^           identifier-pattern
 ^^^^^^           pattern
^^^^^^^^^^^^^^^^^ tuple-pattern
^^^^^^^^^^^^^^^^^ pattern

Enumeration Case Pattern

Enumeration case patterns most commonly show up in the case of a switch statement.

let error: Error? = …
switch error {
case .none: … handle the `nil` case …
case .some(let x): … handle the non-`nil` case, with x unwrapped …
}

There are two examples here. The first does not contain the trailing tuple pattern, and it breaks down as follows:

case .none:
     ^^^^^  enumeration-case-pattern
     ^^^^^  pattern
     ^^^^^  case-item-list
^^^^^^^^^^^ case-label

The second enumeration case pattern does contain the trailing tuple pattern, which in turn contains a value binding pattern wrapped around an identifier pattern.

case .some(let x):
               ^   identifier-pattern
               ^   pattern
           ^^^^^   value-binding-pattern
           ^^^^^   pattern
          ^^^^^^^  tuple-pattern
          ^^^^^^^  pattern
     ^^^^^^^^^^^^  enumeration-case-pattern
     ^^^^^^^^^^^^  pattern
     ^^^^^^^^^^^^  case-item-list
^^^^^^^^^^^^^^^^^^ case-label

Optional Pattern

The optional pattern is a short cut for an enumeration case pattern. You can rewrite the example above as:

let error: Error? = …
switch error {
case nil: … handle the `nil` case …
case let x?: … handle the non-`nil` case, with x unwrapped …
}

Here the pattern in the second case breaks down as follows:

case let x?:
         ^   identifier-pattern
         ^   pattern
         ^^  optional-pattern
         ^^  pattern
     ^^^^^^  value-binding-pattern
     ^^^^^^  pattern
     ^^^^^^  case-item-list
^^^^^^^^^^^^ case-label

Type Casting Pattern

I most commonly use type casting patterns in catch clauses, but here’s an example of something completely different.

class Cell { }
class DIPSwitchCell : Cell { }
class LEDCell : Cell { }

let cell: Cell = … some unspecified subclass …
switch cell {
case is DIPSwitchCell: break // do nothing for a DIP switch
case let led as LEDCell: … flash the LED represented by `led` …
default: fatalError() // crash for any other subclass
}

The pattern in the first case breaks down as follows:

case is DIPSwitchCell:
        ^^^^^^^^^^^^^  type
     ^^^^^^^^^^^^^^^^  is-pattern
     ^^^^^^^^^^^^^^^^  type-casting-pattern
     ^^^^^^^^^^^^^^^^  pattern
     ^^^^^^^^^^^^^^^^  case-item-list
^^^^^^^^^^^^^^^^^^^^^^ case-label

And the one in the second like so:

case let led as LEDCell:
                ^^^^^^^  type
         ^^^             identifier-pattern
         ^^^             pattern
         ^^^^^^^^^^^^^^  as-pattern
         ^^^^^^^^^^^^^^  type-casting-pattern
         ^^^^^^^^^^^^^^  pattern
     ^^^^^^^^^^^^^^^^^^  value-binding-pattern
     ^^^^^^^^^^^^^^^^^^  pattern
     ^^^^^^^^^^^^^^^^^^  case-item-list
^^^^^^^^^^^^^^^^^^^^^^^^ case-label

Speaking of catch clauses, let’s break down some common patterns, starting with this:

catch let error as NSError
                   ^^^^^^^  type
          ^^^^^             identifier-pattern
          ^^^^^             pattern
          ^^^^^^^^^^^^^^^^  as-pattern
          ^^^^^^^^^^^^^^^^  type-casting-pattern
          ^^^^^^^^^^^^^^^^  pattern
      ^^^^^^^^^^^^^^^^^^^^  value-binding-pattern
      ^^^^^^^^^^^^^^^^^^^^  pattern
^^^^^^^^^^^^^^^^^^^^^^^^^^^ catch-clause

My next example breaks down in the same way:

catch let error as MyErrorType
                   ^^^^^^^^^^^  type
          ^^^^^                 identifier-pattern
          ^^^^^                 pattern
          ^^^^^^^^^^^^^^^^^^^^  as-pattern
          ^^^^^^^^^^^^^^^^^^^^  type-casting-pattern
          ^^^^^^^^^^^^^^^^^^^^  pattern
      ^^^^^^^^^^^^^^^^^^^^^^^^  value-binding-pattern
      ^^^^^^^^^^^^^^^^^^^^^^^^  pattern
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ catch-clause

This example differs by having a where clause:

catch let error as MyErrorType where error.myProperty == 42
                                     ^^^^^^^^^^^^^^^^^^^^^^ expression
                                     ^^^^^^^^^^^^^^^^^^^^^^ where-expression
                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ where-clause
                   ^^^^^^^^^^^                              type
          ^^^^^                                             identifier-pattern
          ^^^^^                                             pattern
          ^^^^^^^^^^^^^^^^^^^^                              as-pattern
          ^^^^^^^^^^^^^^^^^^^^                              type-casting-pattern
          ^^^^^^^^^^^^^^^^^^^^                              pattern
      ^^^^^^^^^^^^^^^^^^^^^^^^                              value-binding-pattern
      ^^^^^^^^^^^^^^^^^^^^^^^^                              pattern
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^                             catch-clause

Finally, here’s how to catch one specific error:

catch MyErrorType.myErrorCode
      ^^^^^^^^^^^^^^^^^^^^^^^  enumeration-case-pattern
      ^^^^^^^^^^^^^^^^^^^^^^^  pattern
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ catch-clause

Expression Pattern

Here’s some very bad code that uses the expression pattern for frog pluralisation.

switch frogCount {
case 1, -1: print("frog")
case _: print("frogs")
}

The first case breaks down as follows:

case 1, -1:
     ^  ^^  expression-pattern
     ^  ^^  pattern
     ^^^^^  case-item-list
^^^^^^^^^^^ case-label