Question

I have the following code:

class CSplit(var s1: CanvNode, var s2: CanvNode) extends SplitPane
{         
   topComponent =  s1.merge
   bottomComponent = s2.merge
   def containsV(orig: MapCanvT): Option[MapCanvT]  = 
   {
      def containsIn(cn: CanvNode): Option[MapCanvT] = cn match
      {  
        case Left => None 
        case Right(mc) => if (mc == orig) Some(mc) else None                 
      }
      containsIn(s1) match
      {
        case Some(mc) => Some(mc)
        case None => containsIn(s2)
      }
    }
 }

I want to reduce the code of the containsV method. My first thought was to use a fold method to shorten the containsIn method. But Option doesn't have one, nor does it extend Class Either. Shouldn't Option[T] extend Either[T, None] ? Then at least one could use Either's fold method.

My final thought was to treat s1 and s2 as a List and do find over it but I can't get this to compile:

def containsV(orig: MapCanvT):
  Option[MapCanvT] = ::[CanvNode](s1, s2).find(_ == Right(orig))      
Was it helpful?

Solution

Let's start with the easy part:

  containsIn(s1) match
  {
    case Some(mc) => Some(mc)
    case None => containsIn(s2)
  }

is the same as

  containsIn(s1) orElse containsIn(s2)

Now we only have to deal with containsIn:

  def containsIn(cn: CanvNode): Option[MapCanvT] = cn match
  {  
    case Left => None 
    case Right(mc) => if (mc == orig) Some(mc) else None                 
  }

We can use fold on Either, which gets rid of most of the pattern matching:

 cn.fold(_ => None, Some(_))

But there's the orig thingy. We can handle it with a filter, though:

 cn.fold(_ => None, Some(_)) filter (orig.==)

Thus:

def containsV(orig: MapCanvT): Option[MapCanvT]  = {
  def containsIn(cn: CanvNode): Option[MapCanvT] =
    cn.fold(_ => None, Some(_)) filter (orig.==)
  containsIn(s1) orElse containsIn(s2)
}

I think orElse is much overlooked.

OTHER TIPS

Scala 2.10 adds fold to Option. In the meantime you can use map(f).getOrElse(g) instead:

// These produce identical results
o.fold(g)(x => f(x))
o.map(x => f(x)).getOrElse(g)

Edit: so, for example, the following three do the same thing:

val os: List[Option[Int]] = List(Some(5),None)

// Explicit match
os.map{ _ match {
  case Some(x) => x+3
  case None => 0
}}

// map+getOrElse
os.map{ _.map(_+3).getOrElse(0) }

// fold
os.map{ _.fold(0)(_+3) }

In the fold case, you give the default value for the None case first, and then the function that handles the case where there is a value. In each case you should get List(8,0).

It can be implemented with a list by using the collectFirst method

def containsV(orig: MapCanvT): Option[MapCanvT]
  = List(s1, s2).collectFirst {case i: MapCanvT if (i == (orig) => i}    
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top