Functions Tutorial, Part 3

You can download the source code for this tutorial here.

Pattern Matching

Pattern Matching on Tuples

Let’s talk about pattern matching in a little more detail. But first, we need to talk about tuples. A tuple is a structure that combines multiple values of potentially multiple types into a single value:

myTuple: (AWS.Us-west-1, EC2.T1_micro)

As you can see, the above tuple combines the values AWS.Us-west-1 and EC2.T1_micro into a single value. Each value is of a different type: AWS.Region and EC2.InstanceType. Tuples are expressed in parentheses.

Tuples can be used in functions, too. The below function pattern-matches the contents of a tuple and returns a string. The parameter for this function is a tuple of (AWS.Region, EC2.InstanceType), and it returns a string of an AMI ID. This is handy if you have a particular AMI you want to run for a given region and instance type. We mentioned the purpose of the underscore in the last line during Exercise 2, but to recap, it’s a catch-all where any other value not matching the previously listed values returns the string "ami-44444444".

fun getAmiForRegionAndType(region: AWS.Region, instance: EC2.InstanceType) -> String:
  case (region, instance) of
    | (AWS.Us-west-1, EC2.T1_micro) -> "ami-00000000"
    | (AWS.Us-west-1, EC2.M1_small) -> "ami-11111111"
    | (AWS.Us-east-1, EC2.T1_micro) -> "ami-22222222"
    | (AWS.Us-east-1, EC2.M1_small) -> "ami-33333333"
    | _                             -> "ami-44444444"

Pattern Matching on Optional Values

An Optional value has two possible states: Either the value exists, or it doesn’t. The function below pattern-matches to access the possible values of Optional.

fun envVarToString(name: Optional<String>) -> String:
  case name of
    | None          -> "Develop"
    | Optional name -> name

Here, the function takes an Optional<String> parameter and returns a string. The string it returns depends on whether the Optional string exists. If the Optional string doesn’t exist, meaning its value is None, the function returns the string "Develop". If the Optional string does exist, then it returns the contents of that string.

You can use this function to populate the value of an AWS.Tag key-value pair with the string contents of an environment variable. If the environment variable has been set, the tag’s value will be the environment variable’s value. If the environment variable hasn’t been set, the tag’s value will be "Develop" by default. This is how you’d call the function in this scenario:

myTag: AWS.tag("Environment", envVarToString(String.getEnv("TESTVALUE")))

While it’s totally to valid obtain what is in an Optional value by pattern-matching, it’s a little simpler to do so with Optional.unpack. The function below does the same thing as the one above:

fun envVarToStringOptUnpack(name: Optional<String>) -> String:
  Optional.unpack("Develop", name)

And it can be called in the same way:

testTag: AWS.tag("Environment", envVarToStringOptUnpack(String.getEnv("TESTVALUE")))

Optional.unpack treats the first argument within the parentheses as the default value (the string "Develop" above), and the second argument as the optional value to “unpack” (the name parameter above). Since there are two possible states of an optional value – either it exists, or it does not exist – unpacking an optional value means that you are providing a value for either state.

Exercise 3

Ready for your next exercise? Below is a function, but there’s something wrong with it. Find the mistake and fix it, so that the module compiles.

fun isOregonA(region: AWS.Region, az: AWS.AvailabilityZone) -> Bool:
  case (region, az) of
    | (Us-west-1, AWS.A) -> True
    | _                      -> False

When fixed, the isOregonA function takes a tuple of AWS.Region and AWS.AvailabilityZone and returns a Boolean value.

Hint: If you try to compile the module as is, the compiler will give you a tip about what’s wrong.

You can download the file here. Compile it by executing the following:

lwc FunctionTutorial3.lw