Cristian Vrabie
Cristian Vrabie

Reputation: 4068

Scala Option and flatMap

I'm trying to get the hang of working "the Scala way" so I was wondering if the following code is how things should be done in this case.

So I have the entities User and Company (mapped with LiftWeb mapper). User has currentUser which contains an Option[User] and Company has currentCompany which is an Option[Company]. In order to compare if the current user is the owner of the current company I'm doing something like:

Company.currentCompany.map{_.owner.get == User.currentUser.map{_.id.get}.openOr(-1) }.openOr(false)

It works but somehow it feels kinda verbose. Is it good? Is it not? Any better ideas? Thanks!

Upvotes: 1

Views: 5944

Answers (3)

mistertim
mistertim

Reputation: 5293

Have you looked at using for-comprehensions? You could do something like the following:

for(
  company <- Company.currentCompany.map{_.owner};
  user <- User.currentUser.map{_.id}
) yield (company == user).getOrElse(false)

This will return true if Company.currentCompany is Some[value], and User.currentCompany is Some[value], and company.owner == user.id.

I feel there should be some way of getting rid of that getOrElse on the end, and returning the unwrapped boolean directly, hopefully someone else might be able to shed some light on this!

Upvotes: 10

AndreasScheinert
AndreasScheinert

Reputation: 1918

Given:

Case class user(name:String)
Case class company(owner:Option[User])

Val currentcompany=company(Some("Karl"))
Val currentuser=Some(user("Karl"))

Possible solution:

currentcompany.foldLeft(false) { 
  case (a,b) => currentuser.isDefined && b.owner == currentUser.get
}

Upvotes: 0

Andy Petrella
Andy Petrella

Reputation: 4345

Using for-comprehension is definitively the solution, actually... or flatMap but less readable

To recall every generators are bound using flatMap function of the Monadic Option, except the last which is mapped (like any for and yield). Here is a good slideshow on the underneath concepts Monad

So the for comprehension is used to pass through all steps while they aren't encoded in the fail state (None for Option).

Here is a full example with four tests (the four basic cases) for each options (for and flatMap)

case class User(id: String) {

}


object User {

  def currentUser(implicit me: Option[User]): Option[User] = me

}

case class Company(owner: Option[User]) {

}


object Company {

  def currentCompany(implicit myCompany: Option[Company]): Option[Company] = myCompany

}

object Test extends App {

  test1()
  test2()
  test3()
  test4()
  test5()
  test6()
  test7()
  test8()

  def test1() {
    implicit val me: Option[User] = None
    implicit val myCompany: Option[Company] = None

    val v: Boolean = (for {
      c <- Company.currentCompany
      u <- User.currentUser
      o <- c.owner if o.id == u.id
    } yield true) getOrElse false

    println(v)
  }

  def test2() {
    implicit val me: Option[User] = Some(User("me"))
    implicit val myCompany: Option[Company] = None

    val v: Boolean = (for {
      c <- Company.currentCompany
      u <- User.currentUser
      o <- c.owner if o.id == u.id
    } yield true) getOrElse false

    println(v)
  }

  def test3() {
    implicit val me: Option[User] = None
    implicit val myCompany = Some(Company(me))

    val v: Boolean = (for {
      c <- Company.currentCompany
      u <- User.currentUser
      o <- c.owner if o.id == u.id
    } yield true) getOrElse false

    println(v)
  }

  def test4() {
    implicit val me: Option[User] = Some(User("me"))
    implicit val myCompany = Some(Company(me))

    val v: Boolean = (for {
      c <- Company.currentCompany
      u <- User.currentUser
      o <- c.owner if o.id == u.id
    } yield true) getOrElse false

    println(v)
  }

  def test5() {
    implicit val me: Option[User] = None
    implicit val myCompany: Option[Company] = None


    val v:Boolean = Company.currentCompany.flatMap(c => User.currentUser.flatMap( u => c.owner.map(o => if (u.id == o.id) true else false))) getOrElse false

    println(v)
  }

  def test6() {
    implicit val me: Option[User] = Some(User("me"))
    implicit val myCompany: Option[Company] = None

    val v:Boolean = Company.currentCompany.flatMap(c => User.currentUser.flatMap( u => c.owner.map(o => if (u.id == o.id) true else false))) getOrElse false

    println(v)
  }

  def test7() {
    implicit val me: Option[User] = None
    implicit val myCompany = Some(Company(me))

    val v:Boolean = Company.currentCompany.flatMap(c => User.currentUser.flatMap( u => c.owner.map(o => if (u.id == o.id) true else false))) getOrElse false

    println(v)
  }

  def test8() {
    implicit val me: Option[User] = Some(User("me"))
    implicit val myCompany = Some(Company(me))

    val v:Boolean = Company.currentCompany.flatMap(c => User.currentUser.flatMap( u => c.owner.map(o => if (u.id == o.id) true else false))) getOrElse false

    println(v)
  }


}

Upvotes: 6

Related Questions