Volchik
Volchik

Reputation: 309

Scala generic with trait matching

Look at this code.

trait SomeMix {

}

trait Processor[T] {

  def processMix(t: T with SomeMix) = {
    println("processing T with Mix")
  }

  def processAsUsual(t:T)= {
    println("processing T")
  }

  def process(t:T) = {
    t match {
      case mix: SomeMix => processMix(mix) // <---- error here 
      case _ => processAsUsual(t)
    }
  }
}

Stupid Scala compiler shows error here:

Error:(22, 39) type mismatch; found : mix.type (with underlying type SomeMix) required: T with SomeMix case mix: SomeMix => processMix(mix)

It does not understand that expression I matching to SomeMix is already of type T. Ok lets help him. Changed code:

   def process(t:T) = {
    t match {
      case mix: T with SomeMix => processMix(mix) // <---- warning here 
      case _ => processAsUsual(t)
    }
  }

Now it agrees that all is correct but show warning:

Warning:(22, 17) abstract type pattern T is unchecked since it is eliminated by erasure case mix: T with SomeMix => processMix(mix)

Is any good way to avoid both error and warning here?

Upvotes: 1

Views: 300

Answers (4)

Dmytro Mitin
Dmytro Mitin

Reputation: 51703

You can do this at compile time like @ppressives proposed. If you really want to do this at runtime you should find a way to keep types there after compile time. In Scala standard way to do this is TypeTags.

Try

import reflect.runtime.universe.{TypeTag, typeOf}

def typ[A: TypeTag](a: A) = typeOf[A]

def process(t: T)(implicit ev: TypeTag[T with SomeMix], ev1: TypeTag[T]) = {
  t match {
    case mix if typ(t) <:< typeOf[T with SomeMix] => processMix(mix.asInstanceOf[T with SomeMix])
    case _ => processAsUsual(t)
  }
}

val p = new Processor[Int] {}
p.process(10) //processing T

val p1 = new Processor[Int with SomeMix] {}
val ten = 10.asInstanceOf[Int with SomeMix]
p1.process(ten) //processing T with Mix

Check

Pattern match of scala list with generics

Pattern matching on generic type in Scala

Upvotes: 1

Alexey Romanov
Alexey Romanov

Reputation: 170899

Since, as you mention, it's definitely an instance of T, you can just suppress the unchecked warning:

case mix: (T @unchecked) with SomeMix

Note that it's still unchecked and at runtime only tests that the matchee is an instance of SomeMix; if you change to e.g.

def process(t: Any) = ...

you'll get bad results.

Upvotes: 1

ppressives
ppressives

Reputation: 51

Scala compiler is not stupid. You can't check t is instance of T with SomeMix because of type erasure. Instead of dynamic type dispatching try to use typeclasses with static dispatching.

For example

trait SomeMix {
  def someMethod: String = "test2"
}

class SomeType

def process[T](t: T)(implicit P: Process[T]): Unit = P.process(t)

trait Process[T] {
  def process(t: T): Unit
}

implicit val processString: Process[SomeType] = s =>
  println(s"processing $s as usual")
implicit val processStringWithSomeMix: Process[SomeType with SomeMix] = s =>
  println(s"processing $s with mix ${s.someMethod}")

process(new SomeType)
process(new SomeType with SomeMix)

Upvotes: 2

Lasf
Lasf

Reputation: 2582

Like this?

trait SomeMix {

}

trait Processor[T] {

  def processMix(t: SomeMix) = {
    println("processing SomeMix")
  }

  def processAsUsual(t:T)= {
    println("processing T")
  }

  def process(t:T) = {
    t match {
      case mix: SomeMix => processMix(mix)
      case _ => processAsUsual(t)
    }
  }
}

Upvotes: 1

Related Questions