Home > Net >  Scala - Typeclass overriding the abstract method
Scala - Typeclass overriding the abstract method

Time:12-18

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

  • Related