I am struggling a little with scala 2.12:
I have the following hierarchy:
trait A
case class B(format: String) extends A
trait Writer {
def write(config: A): Unit
}
val writer = new Writer {
override def write(config: A) = println("hi")
}
val w = B("console")
writer.write(w)
which works fine. But I want to provide an alternate implementation for writer:
val writer = new Writer {
override def write(config: B) = println("hi")
}
But I get object creation impossible, since method write in trait Writer of type (config: Playground.A)Unit is not defined
I assumed that since B is an A, this should work. How can I override write
with a config of type B
where B <: A
Scastie: https://scastie.scala-lang.org/QBaiiDP4Sj2lptUjrWLJYw
EDIT: ------------------------------------------------------------
Based on some inputs, I changed the implementation to:
sealed trait A
case class B(format: String) extends A
trait Writer[ T] {
def write[S >: T](config: S): Unit
}
val writer: Writer[A] = new Writer[B] {
override def write[B](config: B) = println("hi")
}
val b = B("console")
writer.write(b)
which works.
But if I modify it to access the variables in config
, it breaks:
sealed trait A
case class B(format: String) extends A
trait Writer[ T] {
def write[S >: T](config: S): Unit
}
val writer: Writer[A] = new Writer[B] {
override def write[B](config: B) = println(config.format)
}
val b = B("console")
writer.write(b)
with value format is not a member of type parameter B
https://scastie.scala-lang.org/Xj2rKbbiTmG7raZgQZYfHA
Appreciate the inputs.
CodePudding user response:
It doesn't work because Writer
declares that its write
method will accept an arbitrary A
. What if someone decides to pass an A
that is not a B
to writer.write
? Then it wouldn't work, so the compiler stops you from doing that.
CodePudding user response:
You're very close with your latest version. As Matthias Berndt pointed out, the write
method declares a new type parameter, but should use the one declared on the trait. In addition, the type parameter should be contravariant.
This code compiles and prints console
:
sealed trait A
case class B(format: String) extends A
trait Writer[-T <: A] {
def write(config: T): Unit
}
val writer: Writer[B] = new Writer[B] {
override def write(config: B) = println(config.format)
}
val b = B("console")
writer.write(b)
Note that, because B
is a subtype of A
, you can also use a Writer[A]
with an instance of B
. Because Writer
is contravariant, you can assign a value of type Writer[A]
to a variable of type Writer[B]
:
val aWriter: Writer[B] = new Writer[A] {
override def write(config: A) = println(s"Got A: $config")
}
aWriter.write(b) // prints "Got A: B(console)"
You can't do the opposite (assign a Writer[B]
value to a Writer[A]
variable) because a Writer[A]
would be able to accept any value of type A
, while a Writer[B]
can only accept values of type B
.
https://scastie.scala-lang.org/TimMoore/bd5E1p99TLCDVfMbElKqFg/8