24

I've seen many times pieces of scala code using Option (for simple values) or Either[List[Error], T] for handling errors.

this gives place to code like this

def createApplicationToken(accessToken: AccessToken): Either[List[Error], ApplicationToken] = {

// go to social info provider and fetch information
retrieveProviderInfo(accessToken).fold(
  errors  => Left(errors),
  info    => {
    // try to find user using the info from the provider
    // if it's not there, create user
    User.findOrCreateFromProviderInfo(info).fold(
      errors  => Left(errors),
      user    => {
        // try to create a fresh token and save it to the user
        user.refreshApplicationToken.fold(
          errors  => Left(errors),
          user    => Right(user.token)
        )
      }
    )
  }
)

Which produces a not so nice code nesting, forces you to deal with failures on every step, and also forces you to have all your functions return a Either[...]

So I'd like to know if

  • the use of exceptions is discouraged in scala (or functional programming in general)

  • there are any drawbacks in using them (regarding immutability or code concurrency)

  • exceptions are somehow in conflict with the principles or functional programming

  • you can think of a better way to code the given example

--

One could avoid the nesting by exiting the function as soon as an error is found using the return statement, but using return is also discouraged in scala...

opensas
  • 60,462
  • 79
  • 252
  • 386
  • 3
    Related http://stackoverflow.com/questions/12886285/throwing-exceptions-in-scala-what-is-the-official-rule – om-nom-nom Oct 22 '12 at 13:15

3 Answers3

25

The following version uses the fact that the right projection of Either is a monad, and is exactly equivalent to your code:

def createApplicationToken(accessToken: AccessToken) = for {
   info <- retrieveProviderInfo(accessToken).right
   user <- User.findOrCreateFromProviderInfo(info).right
   refr <- user.refreshApplicationToken.right
} yield refr.token

And does a much better job of showing off the advantages of Either.

More generally, the rules are the same as in Java: use exceptions for exceptional situations. You just might find that you change your definition of exceptional a bit when you're working in this style—e.g., invalid user input isn't really exceptional, a timed-out network request isn't really exceptional, etc.

Right-biased Either since Scala 2.12

You can now omit .right, so the following code is equivalent since Scala 2.12:

def createApplicationToken(accessToken: AccessToken) = for {
   info <- retrieveProviderInfo(accessToken)
   user <- User.findOrCreateFromProviderInfo(info)
   refr <- user.refreshApplicationToken
} yield refr.token
ziggystar
  • 28,410
  • 9
  • 72
  • 124
Travis Brown
  • 138,631
  • 12
  • 375
  • 680
  • 3
    What would be exceptional? Ask because you provided examples of things that aren't, some examples that are exceptional would be nice. – pedrofurla Oct 22 '12 at 14:21
  • In my example every step might fail, fethching info from twitter|facebook, creating the user fromm the twitter info, and creating a new token and saving it to the user... Would you consider them exceptional? Great answer, BTW – opensas Oct 22 '12 at 14:25
  • 1
    @opensas: I'm not claiming there's a hard line here, but no—I'd model all of those kinds of failure with `Either` (or Scalaz's `Validation`). – Travis Brown Oct 22 '12 at 14:28
  • 1
    @pedrofurla: To give a kind of minimal pair: I'd generally treat `java.io.InterruptedIOException` as truly exceptional (something I'll use `try ... catch` to deal with, if I have to deal with it), but not `java.io.FileNotFoundException`. – Travis Brown Oct 22 '12 at 14:32
  • 1
    One question about your example, what happens to the errors? does the last yield automatically returns a Right(refr.token) if everyting is ok and a Left(errors) is there is any error? – opensas Oct 22 '12 at 15:03
  • @opensas: that the beauty of monads :) – pedrofurla Oct 23 '12 at 02:14
  • 1
    This is my go to weapon of choice, for blocks with right projected Eithers. Makes for beautiful code, and powerful to boot (Scalaz likely goes far deeper, but as far as a language supported feature goes, this one completely rocks ;-)) – virtualeyes Oct 23 '12 at 06:30
6

As om-nom-nom said, I asked a similar question: Throwing exceptions in Scala, what is the "official rule"

But it's not the only one I asked that may interest you, because I used to code with a lot of boilerplate code and a lot of indentation levels because of pattern matching etc...


Community
  • 1
  • 1
Sebastien Lorber
  • 89,644
  • 67
  • 288
  • 419
  • thanks for the links, I hav a couple more questions on SO on my own: http://stackoverflow.com/questions/12067296/scala-how-to-handle-validations-in-a-functional-way?rq=1, http://stackoverflow.com/questions/12547419/scala-idiomatic-and-functional-way-to-add-errors-to-a-list-of-errors?rq=1 – opensas Oct 22 '12 at 15:12
2

The answer varies between what is ideal and what is practical. Ideally, avoid using exceptions. Practically, you can't live without them.

Scala seems to favor one-liners and along those lines v2.10 has the new monad Try:

import scala.util.Try

def percentCompleted( total:Int, done:Int ): Int = Try (done * 100 / total) getOrElse 100

percentCompleted( 0, 10 )    // Catches divide-by-zero and returns 100% instead
Keith Pinson
  • 1,725
  • 1
  • 14
  • 17