Reputation: 5681
I have
trait OptionTransaction {
def data: Data
}
BuyOptionTransaction extends OptionTransaction
SellOptionTransaction extends OptionTransaction
I use these with a Formatter type class to create string representations of various transactions
trait Formatter[T] {
def format(ot:T):String
}
object Formatter {
def apply[T](implicit screen: Formatter[T]) = screen
implicit val buyOT = new Formatter[BuyOptionTransaction] {
def format(ot: BuyOptionTransaction):String = ot.x.toString
}
implicit val sellOT = new Formatter[SellOptionTransaction] {
def format(ot: SellOptionTransaction):String = ot.y.toString
}
}
This is the entry point:
import Formatter._
val closeTransactions: List[OptionTransaction] = ...
closeTransactions.map(startFormat)
The problem is that closeTransactions
has type List[OptionTransaction]
and the type class needs OptionTransaction
downcast to BuyOptionTransaction
or SellOptionTransaction
otherwise it won't find the implicit formatter.
How can I achieve this downcast automatically?
Upvotes: 1
Views: 1865
Reputation: 34393
If you want to deal with the polymorphism runtime, you need to implement some kind of dynamic (runtime) dispatch instead of Type classes, which are static one (compile time). It could look like this:
type Data = String
trait OptionTransaction {
def data: Data = ""
}
class BuyOptionTransaction extends OptionTransaction {
def x: String = "X"
}
class SellOptionTransaction extends OptionTransaction {
def y: String = "Y"
}
trait Formatter[T] {
def format(ot:T):String
}
object Formatter {
def apply[T](implicit screen: Formatter[T]) = screen
def selectFormatter[T](obj: T)(implicit formatter: Formatter[T]) = formatter
implicit val buyOT = new Formatter[BuyOptionTransaction] {
def format(ot: BuyOptionTransaction):String = ot.x.toString
}
implicit val sellOT = new Formatter[SellOptionTransaction] {
def format(ot: SellOptionTransaction):String = ot.y.toString
}
implicit val ot = new Formatter[OptionTransaction] {
def format(ot: OptionTransaction):String = ot match {
case ot: BuyOptionTransaction =>
selectFormatter(ot).format(ot)
case ot: SellOptionTransaction =>
selectFormatter(ot).format(ot)
}
}
}
def startFormat[T](ot: T)(implicit ev: Formatter[T]) = {
ev.format(ot)
}
import Formatter._
val closeTransactions: List[OptionTransaction] = List(new BuyOptionTransaction, new SellOptionTransaction)
closeTransactions.map(startFormat(_))
Upvotes: 2
Reputation: 9100
You can collect
the appropriate types:
val closeTransactions: List[OptionTransaction] = ???
val buys = closeTransactions.collect { case b: BuyOptionTransaction => b}
val sells = closeTransactions.collect { case s: SellOptionTransaction => s}
Now you can apply the proper typeclasses.
It would be probably better to add the action/transformation you want to the OptionTransaction
trait and use that for dynamic binding. If you want to keep working only for one of them, please take a look at this answer.
Upvotes: 1