Correct way to approach splitting a trait

I have 3 traits:

trait Worker

...

trait StringWorker extends Worker {
  def workString(): Iterator[String]
}

...

trait IntWorker extends Worker {
  def workInt(): Iterator[Int]
}

Some of my classes extend only StringWorker, while others extend both StringWorker and IntWorker

My code obtains the correct parser depending on some pattern matching like so:

def getCorrectWorkerProvider: () => Worker = {
  case SomeCase => getStringWorkerProvider()
  case _ => getIntWorkerProvider()
}

If I keep the traits the same as written above, then I'll pretty much always have to do something like this pseudocode:

if working with string, then getCorrectWorkerProvider().asInstanceOf[StringWorker].workString

if working with int, then getCorrectWorkerProvider().asInstanceOf[IntWorker].workInt

whereas if I changed the definition of the traits to something like this:

trait Worker {
  def workString(): Iterator[String]
  def workInt(): Iterator[Int]
}

trait StringWorker extends Worker
trait IntWorker extends Worker

then I would never have to use .asInstanceOf[SomeWorker] to invoke the correct method. I believe the first way is more correct and intuitive as the methods are specific to a certain Worker, but the second way seems to be less of a headache.

Upvotes: 1

Views: 68

Answers (1)

Ryan Stull
Ryan Stull

Reputation: 1096

What I believe you're you're looking for is called the typeclass pattern. Here's an article that explains the idea. Basically the way it works is you create a function that takes a value of type T and an implicit parameter which is the Worker[T], and then scala's implicit resolution mechanism finds the right Worker[T] for you.

Here's an example:

object Main {
    def main(args: Array[String]): Unit = {
        work("String")
        work(3)
    }

    def work[T](value: T)(implicit worker: Worker[T]) = {
        worker.work(value)
    }
}


trait Worker[T]{
    def work(t: T): Iterator[T]
}

object Worker {
    implicit val stringWorker: Worker[String] = (t: String) => Iterator(t)
    implicit val intWorker: Worker[Int] = (t: Int) => Iterator(t)
}

Upvotes: 5

Related Questions