# Implementing wc with Try

Okay, given that background on how we handle possible errors in Scala, let’s get back to our Word Count application. We left off at the point where our boss said to us, “Um, yeah, that `wc` function needs to handle a string that represents the filename that contains the document, not the document itself.”

## The incorrect solution

To quickly re-state what I just wrote about functional error handling, recall that this is the incorrect solution, we do not write code like this in Scala/EOP:

``````@throws Exception
def wc(filename: String) ...``````

Exceptions short-circuit algebraic equations and blow up blueprints, so we don’t do that.

## Working with a file

Having read the previous error-handling lessons, your algebraic-oriented brain should now be thinking that we need to change our previous function signature:

``def wc(document: String): VectorMap[String, Int]``

to something like this:

``````def wc(filename: String): Option[?]
def wc(filename: String): Try[?]
def wc(filename: String): Either[?, ?]``````

The basic idea is that files may not exist, or they may not be readable, so we use the error-handling types to account for those possibilities.

If we focus on just `Try` for a moment:

``def wc(filename: String): Try[?]``

an excellent question at this point is, “What goes inside that `Try` declaration?”

### Try’s type

As we saw in the previous lessons, the type that goes inside `Try[]` is the success case data type. In this case — because the original `wc` function returns a `VectorMap[String, Int]` when it succeeds — our new function’s return type should look like this:

``def wc(filename: String): Try[VectorMap[String, Int]]``

To make that a little easier to see, here’s the success type, underlined:

``````def wc(filename: String): Try[ VectorMap[String, Int] ]
----------------------``````

### Option and Either

To be thorough with our error-handling options, `Option` is declared the same way:

``````def wc(filename: String): Option[ VectorMap[String, Int] ]
----------------------``````

And as you know, with `Either` you declare both the failure and success types. The failure type can be anything, but it’s usually an exception or a string, so its signature will typically be one of these:

``````def wc(filename: String): Either[ Throwable, VectorMap[String, Int] ]
def wc(filename: String): Either[ String,    VectorMap[String, Int] ]``````

I put extra spaces around those brackets to make things more obvious, but you usually don’t do that, so our new function that reads a document from a given filename will have signatures look like these:

``````def wc(filename: String): Try[VectorMap[String, Int]]
def wc(filename: String): Option[VectorMap[String, Int]]

def wc(filename: String): Either[Throwable, VectorMap[String, Int]]
def wc(filename: String): Either[String, VectorMap[String, Int]]``````

## Building the new solution

Now let’s look at the body of our new file-reading function.

### The Try solution

Again, if we focus only on the `Try` solution — which I prefer over `Option` for I/O-related functions, because we can see the exception when needed — I’ll start by renaming this new top-level function to `wcFromFile`:

``def wcFromFile(filename: String): Try[VectorMap[String, Int]]``

As you can imagine, the first thing you’ll do inside this function is to call a file-reading function. So far I know that I’ll give that function the filename, which is a `String`:

``````def readFile(filename: String)
----------------``````

And you also know two other things:

• I just mentioned that I prefer to use `Try` for I/O functions, so this function should return `Try`
• The function needs to return the document that it reads from that filename, and that document is a `String`

Those two items let us complete the function’s type signature:

``````def readFile(filename: String): Try[String]
-----------``````

Now that we know what `readFile` looks like, we add it as the first line to our new `wcFromFile` function:

``````def wcFromFile(filename: String): Try[VectorMap[String, Int]] =
...``````

### A verbose solution

So now `maybeDocument` either contains (a) a `String` version of the document, or (b) an exception. A verbose way to handle `maybeDocument` is the approach I showed earlier in the `makeInt` error-handling lessons, using a `match` expression:

``````def wcFromFile(filename: String): Try[VectorMap[String, Int]] =
maybeDocument match
case Success(docAsString) => Success(wc(docAsString))   // [1]
case Failure(e)           => Failure(e)                 // [2]``````

As shown in Note 1, in the `Success` case I pass the document (`docAsString`) to the old `wc` function. If you look back at that function, you’ll see that it has the type signature `VectorMap[String, Int]`. As you now know, this means that `wc` cannot fail. Because `wcFromFile` needs to return a `Try` type, I wrap this map in the `Success` type.

And then in the `Failure` case (Note 2), I return a new `Failure` instance that contains the exception that occurred when trying to read the file.

While this solution works, a problem with it is that I’m getting a `Try` from `readFile`, and then I unpack it into a `Success` or `Failure`, and then I re-pack it back into those types in the `match` expression. I can do better.

### A more concise solution

The more concise solution is to use the `map` method that’s available on the `Success` and `Failure` types. The way `map` works is that if it’s called on:

• `Failure`, it returns the exception
• `Success`, `map` lets you operate on the success value however you want to

So again, the first step of the function body stays the same:

``````def wcFromFile(filename: String): Try[VectorMap[String, Int]] =
...``````

After that, I replace the `match` expression with a `map` method call on `maybeDocument`, which is a `Try`. Inside `map` I pass the document to the `wc` function:

``````def wcFromFile(filename: String): Try[VectorMap[String, Int]] =
maybeDocument.map(doc => wc(doc))``````

The result of that expression has the type `Try[VectorMap[String, Int]]`, which matches our function’s return type.

If you prefer not using intermediate variables, you can write this solution more concisely, like this:

``````def wcFromFile(filename: String): Try[VectorMap[String, Int]] =

or this, using Scala’s underscore shortcut in the anonymous function:

``````def wcFromFile(filename: String): Try[VectorMap[String, Int]] =

Whichever approach you prefer, the key here is that both `Success` and `Failure` implement the `map` method, and with `Success`, `map` operates on the success value — so it works with the `wc` call — and with `Failure`, `map` just returns the exception it holds.

### The Either solution

Because `Either` is also a good solution for I/O functions, let’s see how it works. First, its file-reading function signature looks like this:

``def readFile(filename: String): Either[Throwable, String]``

Next, its top-level function looks like this:

``def wcFromFile(filename: String): Either[Throwable, VectorMap[String, Int]]``

And finally, the short version of its function body looks like this:

``````def wcFromFile(filename: String): Either[Throwable, VectorMap[String, Int]] =

Because of its flexibility, `Either` is a bit more verbose, but this signature:

``Either[Throwable, VectorMap[String, Int]]``

means that if something goes wrong, you’ll get an exception:

``````Either[Throwable, VectorMap[String, Int]]
---------``````

but if all goes well, you’ll get the sorted map that we’re really after:

``````Either[Throwable, VectorMap[String, Int]]
---------------------``````

## One last point about types

I showed this before, so I’ll just briefly touch on this again here. These function signatures:

``````def wcFromFile(filename: String): Try[VectorMap[String, Int]]
def wcFromFile(filename: String): Either[Throwable, VectorMap[String, Int]]``````

are probably easier to read if we use Scala 3’s opaque types to create more meaningful type names like these:

``````def wcFromFile(filename: String):
Try[MapSortedByValueDesc[Word, Count]]

def wcFromFile(filename: String):
Either[Throwable, MapSortedByValueDesc[Word, Count]]``````

And lastly, if you really want to have some fun — and make your code easier to read — you can create type aliases like these:

``````def wc(filename: String): WordCountMapTry
def wc(filename: String): WordCountMapEither``````

With type aliases and opaque types, solutions like these are limited only by what you think is best, and easiest to read and maintain.