Pattern matching

Programming languages that support algebraic data types often support a set of features to work with fields of composite types or variants of a type. These features are essential in defining functions to operate on different fields or variants in a type-safe manner.

One such feature is called pattern matching that enables us to define functions that operate differently on each of a type's variants and extract individual fields from a composite type while maintaining the language's type safety guarantees.

In fact, the compilers of many languages with pattern matching will issue warnings or errors if we do not handle all of a type's fields or variants properly. These warnings help us write safer and more robust code.

The following example presents simple pattern matching with a switch statement:

let theTeam = MLSTeam.montreal

switch theTeam {
case .montreal:
    print("Montreal Impact")
case .toronto:
    print("Toronto FC")
case .newYork:
    print("Newyork Redbulls")
case .columbus:
    print("Columbus Crew")
case .losAngeles:
    print("LA Galaxy")
case .seattle:
    print("Seattle Sounders")
}

In this example, the Swift compiler infers theTeam as MLSTeam; therefore, we do not need to write MLSTeam for each case.

We use the switch case to match the pattern as it is a basic way of pattern matching for enumerations in Swift. This code block will print Montreal Impact as it matches the .montreal case.

To further explore pattern matching, we can look at the other example, the Dimension enumeration. Using pattern matching, it is easy to write a function, convertDimension, that will take Dimension as a parameter and convert it to the other variant (US measurements to Metric and vice versa):

func convertDimension(dimension: Dimension) -> Dimension {
    switch dimension {
    case let .us(length, width):
        return .metric(length * 0.304, width * 0.304)
    case let .metric(length, width):
        return .us(length * 3.280, width * 3.280)
    }
}

let convertedDimension = convertDimension(dimension:
  Dimension.metric(5.0, 4.0))

In this function, we check for the dimension type with a switch case code block. We extract the associated values with our let statement and use length and width in our return statement.

To test our function, we provide a metric dimension of 5.0 and 4.0 so that the resulting us length will be 16.4 and the us width will be 13.12.

Swift requires us to handle all of the cases of an enumerated type; if we do not cover all the cases, the Swift compiler will warn us and prevent us from introducing runtime errors. For instance, if we remove the second case, the compiler will warn us, as shown in the following image:

Pattern matching

In case we have a lot of cases that we want to handle generically, we can use the default keyword. As an example, let's add a default case to our convertDimension function:

func convertDimension(dimension: Dimension) -> Dimension {
    switch dimension {
    case let .us(length, width):
        return .metric(length * 0.304, width * 0.304)
    default:
        return .us(0.0, 0.0)
    }
}

The preceding example serves only as a default usage example and we should avoid a default case as much as possible.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset