How do I call a method that only exists on one of the 2 types in an Either?

Viewed 64

I have an array of objects of type Either[A, B]. If I know for a particular element whether it is an A or a B, how do I call a method on it that only exists on one of the 2 types. For example:

import scala.util.Random

object EitherTest extends App {

    def newObj(x: Int): Either[A,B] = {
        if (x == 0)
            Left(new A())
        else
            Right(new B())
    }

    val random = new Random()
    val randomArray = (0 until 10).map(_ => random.nextInt(2))
    val eitherArray = randomArray.map(newObj) 

    (0 until 10).foreach(x => randomArray(x) match {
        case 0 => eitherArray(x).aMethod()
        case 1 => eitherArray(x).bMethod()
        case _ => println("Error!")
    })

}

class A {
    def aMethod() = println("A")
}

class B {
    def bMethod() = println("B")
}

When I compile this code, the lines

case 0 => eitherArray(x).aMethod()
case 1 => eitherArray(x).bMethod()

both have the error "value aMethod is not a member of Either[A,B]". How can I solve this?

3 Answers

I don't know why fold doesn't get the respect it deserves. It can be so useful.

eitherArray.foreach(_.fold(_.aMethod(), _.bMethod()))

Well, you can do it if you exctract the logic to another method, and do some pattern matching over the value Either, then check if it is Right or Left, and that's it!

object HelloWorld {
    import scala.util.Random
   def main(args: Array[String]) {
        val random = new Random()
        val randomArray = (0 until 10).map(_ => random.nextInt(2))
        val eitherArray = randomArray.map(EitherTest.newObj) 
       (0 until 10).foreach(x => randomArray(x) match {
        case 0 => EitherTest.callmethod(eitherArray(x))
        case 1 => EitherTest.callmethod(eitherArray(x))
        case _ => println("Error!")
        })
       println("Hello, world!")
   }
}


class EitherTest 
object EitherTest {

    def callmethod(ei : Either[A,B]) = {
        ei match {
            case Left(a) => a.aMethod()
            case Right(b) => b.bMethod()
        }
    }

    def newObj(x: Int): Either[A,B] = {
        if (x == 0)
            Left(new A())
        else
            Right(new B())
    }

}

class A {
    def aMethod() = println("A")
}

class B {
    def bMethod() = println("B")
}

Will print for you, for one random example:

A
B
A
B
A
A
A
B
B
B
Hello, world!

Basically, the way you do with Either is projections: Either.left gives you the projection of the left type, and Either.right gives you that of the right. The projections are somewhat similar to options, in that they can be empty (if your Either is a Right, then the left projection is empty and vice versa), and you can use the usual monadic transformations with them, like map, flatMap, foreach, getOrElse etc.

Your example, could look like this:

 randomArray.foreach { either =>
     either.left.foreach(_.aMethod)
     either.right.foreach(_.bMethod)
 } 

You could also use pattern-matching instead, that's less general, but, perhaps looks a bit clearer in this case:

 randomArray.foreach { 
   case Left(a) => a.aMethod
   case Right(b) => b.bMethod
 }
Related