scalamonads

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!


Solution

  • 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)
      }
    
    
    }