App 2: Implementing handleUserInput

Now that we know that we want a handleUserInput function, let’s start working on it. As usual I begin with a sketch of the function’s signature, which we know from the last lesson:

def handleUserInput(input: String): Try[Unit]

What this function needs to do is to branch off into different directions based on the user input. As I showed in the introduction to the To-Do List app, it is written to accept these commands:

  • q, to quit
  • a, to add a new item
  • d, to delete an item
  • h, to show help
  • v or l (lowercase L), to list items

In other languages you might handle this situation with an if/then/else structure, and in Scala you can do that, but there’s also a different way: a match expression.

match expressions let you perform pattern matching on a given value, and the way it works when you use match as the body of a function is that you start like this:

def handleUserInput(input: String): Try[Unit] = input match
                                                -----------

This is a way of saying, “We’re about to perform some pattern matching on the input variable.”

What you do then is start writing the cases that you want to match. For our function we know what strings we want to match against, so I sketch those cases like this:

def handleUserInput(input: String): Try[Unit] = input match
    case "q" => 
        ???
    case "h" => 
        ???
    case "v" | "l" => 
        ???
    case add if add.startsWith("a ") =>
        ???
    case del if del.startsWith("d ") => 
        ???
    case _ =>
        ???

Believe it or not, that’s actually the complete function body, except that we now need to fill in those ??? areas.

Explaining the cases

Here’s a brief discussion of each case. These first two cases are the simplest, and they can be read as, “When you see this exact string, do the thing on the right side of the => symbol”:

case "q" => 
case "h" => 

So if input is a "q", do the first thing, but if it’s an "h", do the second thing.

Just as in other programming languages, strings like "q" and "h" are called string literals.

The third case is similar to the first two, except this one can be read as, “If input is a "v" or an "l", do the thing on the right side of the => symbol”:

case "v" | "l" => 

With this syntax you can string together as many alternatives as you want, so if you also want to accept an "s" (for “show”), write this:

case "v" | "l" | "s" => 

In the next pattern, things get more interesting:

case add if add.startsWith("a ") =>

This isn’t a perfect use-case for this syntax, but for now you can read it as, “Create a variable named add from the variable input, and if the string add starts with the letter a followed by a blank space, do the thing on the right side of the => symbol.” And then you use the variable add on the right side of the expression.

Where this syntax really shines is when you create an expression that makes add different from input. I don’t have a need to do that here, but I wanted to show this as another technique for Scala tool belt.

And then I use the same technique for the next case as well:

case del if del.startsWith("d ") => 

The final case looks like this, and it means “match everything else”:

case _ =>

So if input doesn’t match any of the previous cases, control comes to this case, and whatever is on the right side of this => symbol is what gets executed. It’s a “catch all” case, and in our code, when they user types anything other than the allowed commands, control comes here.

In Scala, the => used to be called the “rocket” symbol by some people, though that never really caught on. But as I describe it in my other books, it’s really a transformer symbol. Whenever you see it, it means that the thing on the left is being transformed into the thing on the right, so in match expressions we match the cases on the left, and transform them into the values on the right of the =>.

Remember EOP

It’s also important to remember that every line of code in Scala is an expression. So with match expressions, those values on the right side of each => are the values that are returned from the expression.

In this function, because I already declared that the function returns Try[Unit]:

def handleUserInput(input: String): Try[Unit]
                                    ---------

this means that my expectation is that every expression on the right side of those => symbols will return Try[Unit]. We’ll see how that works as we implement each of those cases in the following lessons.