Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Chaining method calls with Either

I'd like to know if it is possible to create some kind of "method call chain", with all methods returning the same Either[Error,Result].

What i'd like to do is: call all the methods successively, and when method returns a Left(Error), then stop the method calls and return the first Left found in the call chain.

I've tryied some stuff, with fold, map, projections... but i'm new to Scala and don't find any elegant solution.

I've tryed some thing like that:

def createUserAndMandatoryCategories(user: User) : Either[Error,User] = {
    User.create(user).right.map {
      Logger.info("User created")
      Category.create( Category.buildRootCategory(user) ).right.map {
        Logger.info("Root category created")
        Category.create( Category.buildInboxCategory(user) ).right.map {
          Logger.info("Inbox category created")
          Category.create( Category.buildPeopleCategory(user) ).right.map {
            Logger.info("People category created")
            Category.create( Category.buildTrashCategory(user) ).right.map {
              Logger.info("Trash category created")
              Logger.info("All categories successfully created created")
              Right(user)
            }
          }
        }
      }
    }
  }

But it doesn't work. And anyway i really don't like the indentation it takes. Besides i'd like to transform the Error into a new String describing the problem (i guess i should use fold?)

I'm looking for something written like that:

val result : Either[String,CallResult] = call1.something("error 1 description")
.call2.something("error 2 description")
.call3.something("error 3 description")
.call4.something("error 4 description")

Is it possible to do such a thing with Scala? Perhaps using both Either and Option?

One constraint would also be that if the first call fails, the other calls should not be made. I don't want a solution where i call everything and then join the eithers.

Thanks!

like image 641
Sebastien Lorber Avatar asked Aug 24 '12 10:08

Sebastien Lorber


Video Answer


1 Answers

There are better, more functional ways to do this (mostly involving Scalaz’s Validation and traverse/sequence) but your code is roughly equivalent to:

def createUserAndMandatoryCategories(user: User) : Either[Error,User] = for {
  _ <- User.create(user).right.map(Logger.info("User created")).right
  _ <- Category.create( Category.buildRootCategory(user) ).right.map(Logger.info("Root category created")).right
  _ <- Category.create( Category.buildInboxCategory(user) ).right.map(Logger.info("Inbox category created")).right
} yield user

Which at least gets rid of all the nesting. Since Scala’s Either is not right-biased by default, you have to specify that manually quite a few times, which reduces the readability a bit.

like image 194
Debilski Avatar answered Oct 28 '22 21:10

Debilski