scalascala-2.12

Scala - Typeclass overriding the abstract method


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.


Solution

  • 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