Search code examples
scalagenericsscala-3type-parameter

Scala 3 generic method type parameter not working


Works:

trait Transformer[A, B] {
  def transform(a: A): B
}

class TransformerImpl extends Transformer[Int, String] {
  override def transform(value: Int): String = {
    value.toString
  }
}

This is what I want, because the trait having one parameter is simpler and because the concrete value of B is determined by A. However, it does not work.

trait Transformer2[A] {
  def transform[B](a: A): B
}

class Transformer2Impl extends Transformer2[Int] {
  override def transform[String](value: Int): String = {
    value.toString
  }
}

Error:

-- [E007] Type Mismatch Error: --------
3 |    value.toString
  |    ^^^^^^^^^^^^^^
  |    Found:    String
  |    Required: String

This too, with a different method signature, does not work:

class Transformer2IntToIntImpl extends Transformer2[Int] {
  override def transform(value: Int): Int = {
    value
  }
}

Error:

-- Error: ----------------------------------------------------------------------
1 |class Transformer2IntToIntImpl extends Transformer2[Int] {
  |      ^
  |class Transformer2IntToIntImpl needs to be abstract, since def transform[B](a: A): B in trait Transformer2 is not defined 
  |(Note that
  | parameter A in def transform[B](a: A): B in trait Transformer2 does not match
  | parameter Int in override def transform(value: Int): Int in class Transformer2IntToIntImpl
  | )
-- [E038] Declaration Error: ---------------------------------------------------
2 |  override def transform(value: Int): Int = {
  |               ^
  |method transform has a different signature than the overridden declaration
  |-----------------------------------------------------------------------------

2 errors found

Based on the errors, I tried several alternatives, but with no success.


Solution

  • This worked for me:

    trait Transformer2[A] {
      type B
      def transform(a: A): B
    }
    
    class Transformer2Impl extends Transformer2[Int] {
      type B = String
      override def transform(value: Int): String = {
        value.toString
      }
    }
    
    class Transformer2IntToIntImpl extends Transformer2[Int] {
      type B = Int
      override def transform(value: Int): Int = {
        value
      }
    }
    
    val ans1 = new Transformer2Impl().transform(5)
    // val ans1: String = 5
    
    val ans2 = new Transformer2IntToIntImpl().transform(5)
    // val ans2: Int = 5
    

    I would still like to know if the original code failed - albeit with convoluted error messages - because it is syntactically incorrect.