2

Say you have a method definition like this in Scala:

def handle[T](fn: T => Unit): Unit

Is it possible to pattern match on the type of the function parameter T to call up a different method depending on the type of T?

Would you need to rewrite it instead to take a Function1 instead and then pattern match on it?

I've tried the following but does not work due to type erasure:

class A {
  def x(fn: A => Unit): Unit = fn(this)
}

class B {
  def y(fn: B => Unit): Unit = fn(this)
}

def handle[T](fn: Function1[T, Unit]): Unit = {
  fn match {
    case fnA: Function1[A, Unit] =>
      new A().x(fnA)
    case fnB: Function1[B, Unit] =>
      new B().y(fnB)
  }
}

Maybe with abstract types?

1
  • Is it OK to change the signature of handle? And to change A and B? Commented Apr 11, 2014 at 13:47

2 Answers 2

1

You can do this with a typeclass, then no reflection or type tagging is required:

  // Typeclass defines things that can be handled
  trait Handleable[T] {
    def handle(fn: T => Unit)
  }

  // Two typeclass instances, one for A and one for B
  implicit object AHandleable extends Handleable[A] {
    def handle(fn: A => Unit) = new A().x(fn)
  }

  implicit object BHandleable extends Handleable[B] {
    def handle(fn: B => Unit) = new B().y(fn)
  }

  // implicitly grab the instance for whichever type we are using (A, B...)
  def handle[T](f: T => Unit)(implicit h: Handleable[T]) = h.handle(f)
  //or equivalently:
  //def handle[T: Handleable](f: T => Unit) = implicitly[Handleable[T]].handle(f)

  handle((a: A) => println(a))                    //> A
  handle((b: B) => println(b))                    //> B

Since A and B have no useful common superclass (I am assuming you can't simply give them one!) the typeclass pattern allows us to "bolt on" a common supertrait without modifying the original classes ("ad-hoc polymorphism").

We can later support additional cases (for C, D, etc) without modifying the handle() method, by adding further typeclass instances.

Sign up to request clarification or add additional context in comments.

1 Comment

That looks neat, and I assume it'd perform better than reflection. Thanks!
0

I think I found an answer using TypeTag instances:

import scala.reflect.runtime.universe._

class A {
  def x(fn: A => Unit): Unit = fn(this)
  override def toString = "A"
}
class B {
  def y(fn: B => Unit): Unit = fn(this)
  override def toString = "B"
}

def handle[T : TypeTag](fn: Function1[T, Unit]): Unit = typeOf[T] match {
  case t if t =:= typeOf[A] => new A().x(fn.asInstanceOf[Function1[A, Unit]])
  case t if t =:= typeOf[B] => new B().y(fn.asInstanceOf[Function1[B, Unit]])
}

handle[A] { a: A =>
  println("It's " + a)
}
handle[B] { b: B =>
  println("It's " + b)
}

That prints the expected output:

It's A
It's B

If anyone has a better solution, let me know :)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.