0

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))      
giampaolo
  • 6,906
  • 5
  • 45
  • 73
Rich Oliver
  • 6,001
  • 4
  • 34
  • 57

3 Answers3

5

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).

Rex Kerr
  • 166,841
  • 26
  • 322
  • 407
  • I'm using 2.10.0M5, but was using the 2.9.2 library documentation. Could you show us the fold syntax for this example. I can't work it out. – Rich Oliver Aug 23 '12 at 22:55
2

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}    
Rich Oliver
  • 6,001
  • 4
  • 34
  • 57
1

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.

Daniel C. Sobral
  • 295,120
  • 86
  • 501
  • 681
  • Thanks for the detailed answer, I was struggling with the fold syntax on Either, and the filter method will be very useful. – Rich Oliver Aug 23 '12 at 22:51