Destructuring

Dear Computer

Chapter 9: Types Revisited

Destructuring

In Java, we get at the fields of a compound data type by adding accessors or making the fields public. Some languages provide an alternative means of accessing fields via a mechanism that we have already met: destructuring via pattern matching.

Haskell

Recall that destructuring may happen in Haskell in a case expression, in the list of formals in a function or lambda header, or in a let or where binding. A destructuring pattern for a tagged union names the variant to be matched and then gives names to the variant's fields in serial order.

Suppose we have a union storing a user's preferred contact method:

Haskell
data ContactMethod =
  Email String |        -- email address
  Phone String String   -- cell number, work number

To see if a value of type ContactMethod is an email address, we match it against the pattern Email address. If the value is an email address, the field within is assigned to the local variable address. To see if it's a phone number, we match it against the pattern Phone cell work. If the value is a phone number, the fields within are assigned to the variables cell and work. The names given to the fields are entirely at the discretion of the programmer writing the pattern. The definition of ContactMethod does not name them.

This function uses a case expression to respond to values matching either pattern:

Haskell
connect :: ContactMethod -> String
connect method =
  case method of
    Email address -> "Email me at " ++ address
    Phone cell _  -> "Call me at " ++ cell

The work number is discarded using the _ wildcard. This alternative definition uses pattern matching in the formal parameters:

Haskell
connect :: ContactMethod -> String
connect (Email address) = "Email me at " ++ address
connect (Phone cell _)  = "Call me at " ++ cell

Parentheses are needed to separate the pattern from the surrounding tokens.

Java

Java 17 has pattern matching that looks a bit like Haskell's. We can use it to write conditional logic that walks through a sequence of type checks to decide what action to take or what value to produce based on the type of the value. Here pattern matching is used in a switch expression to turn an arbitrary object into a Double:

Java
double x = switch (object) {
  case Integer i -> i.doubleValue();
  case Float f -> f.doubleValue();
  case String s -> Double.parseDouble(s);
  default -> 0.0;
}

Java's pattern matching doesn't currently support destructuring, so we can't use it to break an object up into its fields. That may change in the future. Java has been acquiring features from other languages rather rapidly in recent versions, including lambdas, multiline string literals, type inference, and syntactic shortcuts that make it easier to pass code to higher-order functions.

JavaScript

Objects in JavaScript are destructured with a pattern that selects out the fields we wish to bind to local variables:

JavaScript
const date = {year: 1865, month: 6, day: 19};
const {year, month, day} = date;

We can also collect up all the unnamed fields into a “rest” object, unpack nested objects, provide default values when keys are missing, or bind values to names other than their keys. The expressive reach of JavaScript's destructuring assignments is immense.

← Enums with DataTypeclasses →