Search code examples
scalapattern-matchingtype-parameter

scala - How to completely ignore an object's type parameter when needed


Given the following Scala 2.11 code:

class Partials {
  class Aggregate
  class AggregateId[T <: Aggregate]
  class Event[T <: Aggregate]

  type EventListener = PartialFunction[Event[_], Unit]

  val listeners = mutable.Set[EventListener]()

  def addListener(l: EventListener) {
    listeners += l
  }

  def process(e: Event[_]): Unit = {
    listeners.foreach(listener => if (listener.isDefinedAt(e)) listener.apply(e))
  }

  addListener {
    case x => println(x)
  }
}

This is simplified from my actual code, some event processing, but it shows the same issue. I have events that refer to some aggregate, and I have listeners that should be able to handle these events. The listeners are PartialFunctions so they could handle some Event subtypes but not all of them.

As listed, I'm getting the following error for the addListener call at the end:

type arguments [_$1] do not conform to class Event's type parameter bounds [T <: Partials.this.Aggregate]
  addListener {

When I change the EventListener type alias to

type EventListener = PartialFunction[Event[_ <: Aggregate], Unit]

I instead get the following errors in the process method:

Error:(19, 60) type mismatch;
 found   : Partials.this.Event[_$2] where type _$2
 required: Partials.this.Event[_ <: Partials.this.Aggregate]
    listeners.foreach(listener => if (listener.isDefinedAt(e)) listener.apply(e))

Error:(19, 79) type mismatch;
 found   : Partials.this.Event[_$2] where type _$2
 required: Partials.this.Event[_ <: Partials.this.Aggregate]
    listeners.foreach(listener => if (listener.isDefinedAt(e)) listener.apply(e))

At the moment I don't really understand what is going on. Event needs its type parameter for things unrelated to this, but for the PartialFunctions that are supposed to handle events, I'd like to simply ignore that type parameter, which is what I thought I did with the [_]. What am I getting wrong?


Solution

  • This seems to work:

    import scala.collection.mutable
    
    class Partials {
      class Aggregate
      class AggregateId[T <: Aggregate]
      class Event[T <: Aggregate]
    
      type EventListener = PartialFunction[Event[_ <: Aggregate], Unit]
    
      val listeners = mutable.Set[EventListener]()
    
      def addListener(l: EventListener) {
        listeners += l
      }
    
      def process(e: Event[_ <: Aggregate]) {
        listeners.foreach(listener => if (listener.isDefinedAt(e)) listener.apply(e))
      }
    
      addListener {
        case x => println(x)
      }
    }
    

    Notice how process method is defined. Those error messages were telling you that when you define listener's type as PartialFunction[Event[_ <: Aggregate], Unit], then you need to pass instance of Event[_ <: Aggregate] to listener's isDefinedAt and apply methods.