Reputation: 396
I have several Event classes defined and generated externally by an Interface Description Language. The definition of these types are beyond my control- I only have access to the simple generated case classes:
sealed trait Event
case class EventOpen(msg: String) extends Event
case class EventClose(msg: String) extends Event
Given a list of these events, I need to fold them into a single value. The intent is to build up a library of folding functions that can be used as needed for a given context. For example, to determine whether a stream is open or closed. In order to avoid a gigantic pattern match inside the folding function, as the list of events is quite long, I was hoping to use implicit classes that add the functionality to each event as needed, and package those up to be imported as needed:
sealed trait Status
case object Open extends Status
case object Closed extends Status
case object Unknown extends Status
// Do nothing unless a specific EventOp is defined.
implicit class EventNoOp(event: Event) {
def accumulate(status: Status): Status = status
}
implicit class EventOpenOp(event: EventOpen) {
def accumulate(status: Status): Status = Open
}
implicit class EventCloseOp(event: EventClose) {
def accumulate(status: Status): Status = Closed
}
val initial: Status = Unknown
List(EventOpen("yeeha"), EventClose("adios")).foldLeft(initial)((status, event) => event.accumulate(status)) // => Unknown
The issue is that the specific type of Event is lost in the folding, so only EventNoOp is ever invoked, resulting in "Unknown" instead of "Closed". Explicitly pattern matching inside the folding function solves the problem, but that defeats the purpose of the approach in the first place:
val initial: Status = Unknown
List(EventOpen("yeeha"), EventClose("adios")).foldLeft(initial)((status, event) => {
event match {
case e: EventOpen => e.accumulate(status)
case e: EventClose => e.accumulate(status)
}
})
Is there any way to avoid an explicit pattern match on each event?
Upvotes: 1
Views: 135
Reputation: 33019
Is there any way to avoid an explicit pattern match on each event?
I think your solution using implicits actually leads to more boilerplate than using pattern matching... This seems much more intuitive to me than the implicit-based solution:
def accumulateStatus(status: Status, event: Event): Status = event match {
case EventOpen(_) => Open
case EventClose(_) => Closed
case _ => status
}
val initial: Status = Unknown
List(EventOpen("yeeha"), EventClose("adios")).foldLeft(initial)(accumulateStatus)
Since you can't modify the Event
classes directly, you're going to be stuck with pattern matching (or doing something even more awkward with run-time type comparisons / reflection).
Upvotes: 4