Home > Blockchain >  Scala 3 extension overloading with different generic types
Scala 3 extension overloading with different generic types

Time:10-15

I'm migrating from Scala 2.13 to Scala 3 and I'm trying to rewrite small utility functions. In 2.13 it was possible to write one more generic implicit and another one more specific but in Scala 3 it seems no longer possible.

  type Outcome[ E <: Fail, A] = Either[E, A]

  extension[A] (list: List[Outcome[ValidationFail, A]]) {
    def outcomeListAll: Outcome[ValidationFail, List[A]] = {
      val (left, right) = list.partitionOutcome
      if (left.isEmpty) {
        Right(right)
      } else {
        Left(left.reduce(_   _))
      }
    }
  }

  extension[F <: Fail, A] (list: List[Outcome[F, A]])
    @deprecated
    def outcomeListAll: Outcome[Fail, List[A]] = {
      val (left, right) = partitionOutcome
      if (left.isEmpty) {
        Right(right)
      } else {
        Left(Fail.generic(left.map(_.getMessage).mkString(", "), left.head))
      }
    }
    def partitionOutcome: (List[F], List[A]) = {
      val left: List[F] = list.collect {
        case Left(l) => l
      }
      val right: List[A] = list.collect {
        case Right(r) => r
      }
      (left, right)
    }

When I try compiling above code snippet I get double definition error. Which at least according to this article should resolve to methods with different signatures. Like so:

<extension> def < (x: String)(y: String): Boolean = ...
<extension> def  : (xs: Seq[Elem])(x: Elem): Seq[Elem] = ...
<extension> infix def min(x: Number)(y: Number): Number = ...

I know I can simply use pattern matching but it seems odd that I can't overload extensions like you could with methods.

Compilation error:

[error] -- [E120] Naming Error: /home/...testing/Main.scala:19:8 
[error] 19 |    def outcomeListAll: Outcome[Fail, List[A]] = {
[error]    |        ^
[error]    |Double definition:
[error]    |def outcomeListAll(list: scala.collection.immutable.List): scala.util.Either in object Main at line 7 and
[error]    |def outcomeListAll(list: scala.collection.immutable.List): scala.util.Either in object Main at line 19
[error]    |have the same type after erasure.
[error]    |
[error]    |Consider adding a @targetName annotation to one of the conflicting definitions
[error]    |for disambiguation.

CodePudding user response:

As user pointed out, problem can be solved by using annotation @targetName("...")

  import scala.annotation.targetName

  type Outcome[ E <: Fail, A] = Either[E, A]

  extension[A] (list: List[Outcome[ValidationFail, A]]) {
    @targetName("outcomeListAllForValidationFail")
    def outcomeListAll: Outcome[ValidationFail, List[A]] = {
      val (left, right) = list.partitionOutcome
      if (left.isEmpty) {
        Right(right)
      } else {
        Left(left.reduce(_   _))
      }
    }
  }

  extension[F <: Fail, A] (list: List[Outcome[F, A]])
    def outcomeListAll: Outcome[Fail, List[A]] = {
      val (left, right) = partitionOutcome
      if (left.isEmpty) {
        Right(right)
      } else {
        Left(Fail.generic(left.map(_.getMessage).mkString(", "), left.head))
      }
    }
    def partitionOutcome: (List[F], List[A]) = {
      val left: List[F] = list.collect {
        case Left(l) => l
      }
      val right: List[A] = list.collect {
        case Right(r) => r
      }
      (left, right)
    }
  • Related