2

I bumped into a code snippet and not able to make sense out of it. The snippet is:

  implicit val dummyVisit = Visit("", 1L, 1, 1, 1, 1L)
  implicit val dummyOrder = Order("", 1L, 1, 1, 1, 1L)

  def process[T](events : Array[T])(implicit t: T):Unit = {
    println(t)
    if(!events.isEmpty)
      t match {
        case r: Order => processOrder(events.asInstanceOf[Array[Order]])
        case r: Visit => processVisit(events.asInstanceOf[Array[Visit]]);
      }
  }

  def processOrder(arr: Array[Order]): Unit = { println(arr.size) }
  def processVisit(arr: Array[Visit]): Unit = { println(arr.size) }

The implicit variable t, requires the dummyVisit & dummyOrder to exist.

Question:

  1. Is this a right way of using implicit parameter?

  2. Is there a better way for get the class-type of T, without using implicit parameter?

Mohitt
  • 2,809
  • 3
  • 25
  • 46
  • 1
    Implicits are searched using the implicit resolution rules http://stackoverflow.com/questions/5598085/where-does-scala-look-for-implicits I don't understand your question? you can call process(xs) on an array of either Visits or Orders. If the corresponding implicit is not in scope, process(xs) would not compile – Giovanni Caporaletti Feb 12 '16 at 08:39
  • 1
    Looks like a bad way of using polymorphism or lack thereof – yǝsʞǝla Feb 12 '16 at 09:03

1 Answers1

4

Capturing the type of an argument is one of the intended uses of implicit arguments.

Though I'd write something like this a bit differently:

import scala.reflect.ClassTag

// `sealed` ensures that no extra evidences can be defined elsewhere
sealed abstract class Evidence[T](implicit val tag: ClassTag[T])
object Evidence {
  implicit object visitEvidence extends Evidence[Visit]
  implicit object orderEvidence extends Evidence[Order]
}

def process[T](events: Array[T])(implicit ev: Evidence[T]) = {
  import ev.tag // import ClassTag[T] to allow matching on array element types
  events match {
    case Array() => // don't process empty arrays
    case arr: Array[Order] => processOrder(arr)
    case arr: Array[Visit] => processVisit(arr)
  }
}

This code avoids creating meaningless dummy instances, and typecasting with asInstanceOf.

A step further would be to capture the processing operation itself in the implicit argument and completely avoid the explicit match for every case. This is also known as typeclass pattern:

sealed trait ProcessArray[T] {
  def apply(arr: Array[T]): Unit
}
object ProcessArray {
  implicit object processVisitArray extends ProcessArray[Visit] {
    def apply(arr: Array[Visit]) = { println(arr.size) }
  }
  implicit object processOrderArray extends ProcessArray[Order] {
    def apply(arr: Array[Order]) = { println(arr.size) }
  }
}
def process[T](array: Array[T])(implicit proc: ProcessArray[T]) = {
  if (array.nonEmpty) proc(array)
}
Kolmar
  • 13,241
  • 1
  • 19
  • 24