Combining overloaded methods

Not very, as a general solution, because

class Foo(val i: Int) {}
class Bar() extends Foo(2) {}
def inc2[A <: Int | Foo](a: A): A = a match
  case i: Int => (i + 1).asInstanceOf[A]
  case f: Foo => Foo(f.i + 1).asInstanceOf[A]
var b = Bar()
b = inc2(b)  // ClassCastException

And even with yours, where the classes are final, you can still have subtypes:

scala> var s: ("eel" | "herring") = "eel"
var s: "eel" | "herring" = eel
                                                                                
scala> s = inc(s)
s: String = eel1

scala> def fishy(x: "eel" | "herring") = x match
     |   case "eel" => "long"
     |   case "herring" => "cold"
     | 
def fishy(x: "eel" | "herring"): String
                                                                                
scala> fishy(s)
scala.MatchError: eel1 (of class java.lang.String)

So, yeah, kind of playing with fire.

2 Likes

Also, even apart from the casting horror, this can only work for methods that can be expressed as (a: A) => A or (a: A) => a.B. With match types or typeclasses you can have any ad-hoc mapping you want.

type AddOne[A] = A match
  case Int => Long
  case String => String

// or

trait AddOne[A] {
  type B
  def addOne(a: A): B
}

implicit val addOneInt: AddOne[Int] { type B = Long } = ...
implicit val addOneString: AddOne[String] { type B = String } = ...
3 Likes

@Ichoran laid out the details, but I’ll also note that we spent something like a year debating this, in some truly epic-length threads, while designing Scala 3.

The given/using syntax we landed on actually steps up a conceptual level – instead of saying “what” it is (creating a type class), it expresses why it works that way, with syntax that is grounded in the underlying mathematical thinking (“Given a Oneable…”, “Using a Oneable…”)

But basically, there’s no single best answer. It depends on the lens through which you’re thinking about the problem, and there are a lot of valid ways to express it. I think most people agree that given/using is an improvement over the original implicit keyword (which is specifically saying what is happening at the most technical level), but beyond that we simply had to eventually stop bike-shedding the keywords and choose something.

3 Likes

Was it really only a year?

1 Like

Maybe then it makes sense to define per type extension methods