App 2: Implementing handleUserInput (Scala 3 Video)
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 quita
, to add a new itemd
, to delete an itemh
, to show helpv
orl
(lowercaseL
), 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 inmatch
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.
Update: All of my new videos are now on
LearnScala.dev