Global publisher is a bad idea as it will be non-typed then. But if you really need it - just don't bind your publisher to the concrete type:
object Oper extends Publisher[Any]
class Oper[A, B] extends Operator[A, B]{
def publishOper(elem: A): B = {
val res = operate(elem)
Oper.publishOper(res)
res
}
}
But this is a bad design. I'd recommend to define Oper
as trait with external dependency:
trait Publisher[-A]{ //"-" - if can store Any then can store Int
type T >: A //to compensate covariant position for set or any other your internal providers; implementations without explicit T will produce existential type `_ >: A` for T to guarantee that storage will have a biggest A type
var storage: Set[T] = Set[T]()
def publishOper(elem: A) = storage += elem
}
trait Operator[A, B]{
def operate(elem: A): B = elem.asInstanceOf[B] //just mock
}
trait Oper[A, B] extends Operator[A, B]{
def publisher: Publisher[B]
def publishOper(elem: A): B = {
val res = operate(elem)
publisher.publishOper(res)
res
}
}
Example:
scala> val pbl = new Publisher[Any]{}
pbl: Publisher[Any] = $anon$1@49dbe5f0
scala> class Oper1 extends Oper[Int, Int] { val publisher: Publisher[Int] = pbl }
defined class Oper1
scala> new Oper1
res10: Oper1 = Oper1@4bd282fd
scala> res10.publishOper(3)
res11: Int = 3
scala> res10.publishOper(4)
res12: Int = 4
scala> res10.publisher.storage
res13: Set[res10.publisher.T] = Set(3, 4)
scala> class Oper2 extends Oper[Double, Double] { val publisher: Publisher[Double] = pbl }
defined class Oper2
scala> new Oper2
res14: Oper2 = Oper2@271f68d2
scala> res14.publishOper(2.0)
res15: Double = 2.0
scala> res14.publishOper(3.0)
res16: Double = 3.0
scala> res14.publisher.storage
res17: Set[res14.publisher.T] = Set(3, 4, 2.0)
So now, depending on your context - you can choose the biggest type, that your publisher may work with (in my example it was Any
). The compiler will automatically check if it's fine with your Oper
- that's why we need contravariant Publisher
here.
P.S. Interesting note: 3.0 was casted to 3 automatically as expected existential type of collection was Int, so for my example there is no duplication Set(3,4, 2.0, 3.0)
but strings of course will be different: Set(3, 4, 2.0, "3")
:
scala> class Oper3 extends Oper[String, String] { val publisher = pbl }
defined class Oper3
scala> new Oper3
res23: Oper3 = Oper3@f93893c
scala> res23.publishOper("3")
res38: String = 3
scala> res23.publisher.storage
res39: Set[res23.publisher.T] = Set(3.0, 4.0, 2.0, 3)
class Oper[A, B] extends Operator[A, B] with Publisher[B] { ...
– Rich Henryclass Oper[A,B](implicit publisher: OperPub[B]) extends Operator[A,B]
– Rich Henry