3

I have a number of function pairs to manipulate resource, for example:

def loadFile(name: String): Option[String]
def writeFile(name: String, data: String): ???

loadFile is currently implemented to return resultant String using Option Some/None but what about writeFile?

Like loadFile, if writeFile fails I would like it to return a response wrapped in a nice Try or Option rather than throw an exception or return a null, Boolean, or response code.

What are some recommended best practices?

def writeFile(name: String, data: String): Try(Unit)

or

def writeFile(name: String, data: String): Option(Unit)

and just test success flag?

What is the best scala-thonic way to approach this?

IUnknown
  • 2,596
  • 4
  • 35
  • 52

3 Answers3

5

The other answers here are pretty much the same as Try[Unit], but require a little more boilerplate. This is about as simple as it gets (literally adding 3 letters to your function definition):

def writeFile(name: String, data: String): Try[Unit] = Try {
    // do write operation that may throw an exception
}

The huge advantage of Try is that it's a monad, so you're able to map and flatMap it (among other things) to compose multiple Try ops into a single one with ease. Say you need to do 3 of these operations together, and either return Unit or the first Exception.

val result: Try[Unit] = for {
    a <- writeFile(name1, data1)
    b <- writeFile(name2, data2)
    c <- writeFile(name3, data3)
} yield ()

You can match on the results:

result match {
    case Success(_) => println("Success!")
    case Failure(t) => println(s"Failure! {t.getMessage}")
}

You also have some other powerful combinator functions as seen in the API docs.

And you can also write a more generic method for retrying failures, as seen in the last block of code in this post.

Community
  • 1
  • 1
Michael Zajac
  • 55,144
  • 7
  • 113
  • 138
1

I've gone with an approach that looks like this in the past.

trait Result
case object Success extends Result
case class Error(errorReason: String) extends Result

def writeFile(name: String): Result

It works pretty well with pattern matching paradigms.

def writeFileWithRetry(name: String)(numRetries: Int = 2): Result = {
  writeFile(name) match {
    case e: Error => 
      LOG.errorLog(s"Could not write $name: ${e.errorReason}")
      if (numRetries <= 0) e else writeFileWithRetry(name)(numRetries - 1)
    case r => r
  }
}
Nate
  • 2,205
  • 17
  • 21
0
   def writeFile(name: String, data: String): Either[Exception, Unit] = try {
      doWrite(name, data)  
      Right(()) 
   } catch {
     case ex => Left(ex)
   }

    writeFile("foo", "bar") match {
       case Left(ex) => println("Failed with exception: " + ex)
       case _ => println("Success!")
    }
Dima
  • 39,570
  • 6
  • 44
  • 70