1

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?

Volchik
  • 184
  • 1
  • 7

4 Answers4

2

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)
ppressives
  • 51
  • 2
  • Thank you for example. It brings me some new ideas on my case. But as solution for this particular problem it is not good. Logic of processing is moved from trait Process to outside. It is unacceptable. But may be i could slightly modify it to fit my case – Volchik Oct 23 '18 at 13:39
  • @Volchik You can put implicits `processString` and `processStringWithSomeMix` to `object Process`. And defining implicits for a type class in its companion object is a standard pattern in Scala, it's not "moving logic outside". – Dmytro Mitin Oct 24 '18 at 08:06
1

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)
    }
  }
}
Lasf
  • 2,536
  • 1
  • 16
  • 35
  • No because processMix could work only with T extended with SomeMix. In real example it calls some methods that require both functionality from T and SomeMix – Volchik Oct 23 '18 at 01:50
1

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

Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66
  • Thank you. Your example is works well and could be used in case when i really will need to match generic type. But my case was simpler. I just need to match with SomeMix not loosing already known fact that t of type T. – Volchik Oct 24 '18 at 11:03
1

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.

Alexey Romanov
  • 167,066
  • 35
  • 309
  • 487
  • Thank you. For my case its really enough. And i understand that matching with generic is bad idea. But in my case it was just consequence of eliminating type T in process of matching with SomeMix. – Volchik Oct 24 '18 at 11:00