App 2: Handling View

Next up, the “View” use case is a little more complicated than the first two. Basically this code needs to attempt to read the database and then take one of two possible actions from there:

  • In the success case, it prints the list of to-do items
  • In the failure case, it prints an error message about the database being inaccessible

This is straightforward to do because we already wrote the selectAll database method previously, and it returns a Try value, so we just need to handle its Success and Failure cases in a match expression.

Here’s the code that does what I just described, with a few comments to make those steps clear:

def handleView(): Try[Unit] = Try {

    // attempt to read all the tasks from the database.
    // this function returns a list of strings, wrapped inside
    // a Try:
    val res: Try[Seq[String]] = db.selectAll()

    // handle the Success and Failure cases:
    res match
        case Success(tasks) => 
            // in the Success case we get a list of tasks (strings),
            // so print those out in a nice format:
            printTasks(tasks)
        case Failure(t) => 
            // in the Failure case, where we can’t access the database,
            // print the exception (a Throwable) we get from selectAll:
            System.err.println(t)
}

private def printTasks(tasks: Seq[String]): Unit = 
    for
        (task, count) <- tasks.zip(Stream from 1)
    do
        println(s"${count}. $task")

Here’s a shorter version of handleView, without the comments:

def handleView(): Try[Unit] = Try {
    val res: Try[Seq[String]] = db.selectAll()
    res match
        case Success(tasks) => 
            for
                (task, count) <- tasks.zip(Stream from 1)
            do
                println(s"${count}. $task")
        case Failure(t) => 
            System.err.println(t)
}

Note that the cases of the match expression print to STDOUT and STDERR, so they both have the return type Unit. And because the entire function body is wrapped in a Try constructor call:

def handleView(): Try[Unit] = Try { // body here ... }
                              ------------------------

this means that both cases of this function correctly result in returning the Try[Unit] type.

Again, if any of this seems like overkill, remember to imagine that we’re accessing a remote database or REST service, and in the real world, those things may be down.