How to combine two consumers?

Given a simple attribute that consumes values โ€‹โ€‹of type T:

trait Consumer[-T] extends (T => Unit) {
  def +[U, P](other: Consumer[U]): Consumer[P] = ???
}

How to implement a function +that combines two consumers into one that accepts a valid supertype for AnyRef or a wider type for AnyVal? For the following cases:

-AnyRefs that have a common supertype:

trait Base
trait A extends Base
trait B extends Base

val c1: Consumer[A] = _
val c2: Consumer[B] = _
val cc = c1 + c2 //cc must have type of Consumer[Base]

-AnyVals:

val c1: Consumer[Int] = _
val c2: Consumer[Long] = _
val cc = c1 + c2 //cc must have type of Consumer[Long]
+4
source share
2 answers

You are trying to implement the concept of a partial function. In doing so, you need to know that you sacrifice type safety for agility. In a strongly typed language, you usually cannot do this without any obvious hack, for example. softening the rules for applying types or variances.

, Scala PartialFunction : , . ,

trait Animal
case class Dog() extends Animal
case class Cat() extends Animal

"" Dog => T Animal => T ( ):

val pf: PartialFunction[Animal, Unit] = {
  case Dog() => println("dog")
}

:

pf(Cat()) // MatchError

, Scala Scala , - , .. -T +T. @uncheckedVariance:

trait Consumer[+T, +V] extends (T@uncheckedVariance โ‡’ V) {
  def handle(command: T@uncheckedVariance): V
}

:

val lookup: Map[Class[_], Consumer[Animal]] = Map(
  classOf[Dog] โ†’ dogConsumer,
  classOf[Cat] โ†’ catConsumer
)

( ).

+3

, , , , , :

trait A
trait B extends A
trait C extends A
trait D extends A

val x: Consumer[A] = (new Consumer[B]) + (new Consumer[C])
x(new D)   // Uh-oh, nobody knows how to handle this!

, , , , , , , .

, , , - , , , -

abstract class Consumer[A: reflect.ClassTag] {
  def apply(a: A): Unit
  def consume(a: Any) {
    val c = implicitly[reflect.ClassTag[A]].runtimeClass
    if (c isInstance a) apply((c cast a).asInstanceOf[A])
  }
  def +[A1 >: A : reflect.ClassTag](that: Consumer[A1]) = {
    new Consumer[A1] {
      def apply(a1: A1) = { consume(a1); that.consume(a1) }
    }
  }
}
+1

Source: https://habr.com/ru/post/1622803/


All Articles