Advanced Pattern Matching

Guard Case

Using guard case

Another keyword which supports patterns is the newly introduced guard keyword. You know how it allows you to bind Optionals into the local scope much like if let only without nesting things:

func example(a: String?) {
    guard let a = a else { return }
    print(a)
}
example("yes")

guard let case allows you to do something similar with the power that pattern matching introduces. Let's have a look at our soldiers again. We want to calculate the required HP until our player has full health again. Soldiers can't regain HP, so we should always return 0 for a soldier entity.

let MAX_HP = 100

func healthHP(entity: Entity) -> Int {
    guard case let Entity.Entry(.player, _, _, hp) = entity 
      where hp < MAX_HP 
        else { return 0 }
    return MAX_HP - hp
}

print("Soldier", healthHP(Entity.Entry(type: .soldier, x: 10, y: 10, hp: 79)))
print("Player", healthHP(Entity.Entry(type: .player, x: 10, y: 10, hp: 57)))

// Prints:
"Soldier 0"
"Player 43"

This is a beautiful example of the culmination of the various mechanisms we've discussed so far.

  • It is very clear, there is no nesting involved
  • Logic and initialization of state are handled at the top of the func which improves readability
  • Very terse.

This can also be very successfully combined with switch and for to wrap complex logical constructs into an easy to read format. Of course, that won't make the logic any easier to understand, but at least it will be provided in a much saner package. Especially if you use enums.