Today I tried for the first time Scala’s Try
monad. I used it twice already and I’m sure I’ll use a lot more times!
Here is how I used it the first time. I wanted to parse some CSV data into a sequence of case classes. And of course, with input data you never know what can go wrong. I didn’t really want to handle the error, I just wanted to know if it was parsed correctly or not. Therefore I used an Option to return either Some(result) or None.
Here is the code for the attempt with Option:
def parseCSV(csv : String) = { try { Some { csv.split("\n").map { line => val tokens = line.split(";") ActivityData(tokens(0).toLong, tokens(1).toInt, tokens(2).toInt, tokens(3).toLong) } } } catch { case _ : Throwable => None } }
A bit complicated isn’t it? Using this function is cool (just as with every other Option) but actually reading it is painful. It’s just too messy with that try catch. Is there a better way of doing this?
Well, there comes the Try
monad from scala.util
package to save the situation!
def parseCSV(csv : String) = Try { csv.split("\n").map { line => val tokens = line.split(";") ActivityData(tokens(0).toLong, tokens(1).toInt, tokens(2).toInt, tokens(3).toLong) } }
Look at this! Wow! This is neat! Just surround your code with Try and that’s it!
And the best part, you can use it exactly the same way as you would use the Option monad (dah .. that’s what are monads all about).
parseCSV(csvdata).map { entries => //do something with the data }.getOrElse { BadRequest("Invalid CSV Data") //this is Play Framework specific (returns a 400 HTTP response with a message) }
Note: For those who are wondering how does this work, the Try
monad returns Success(something)
if everything was calculated without any exceptions and Failure(error)
if an error was thrown while executing the argument of Try().
Note (deep): Try
takes its argument by-name (not by-value), therefore it can execute it within a try, catch the exception and return either Success
or Failure
. Isn’t Scala a super powerful awesome language? Yes it is!