Principles for Implicits in Scala 3

Side note re: DummyImplicit,

I’ve never understood why core types usually don’t define natural implicit: Unit, Option, Either, Tuple defining the natural implicits would good in my view. I have often wanted (and implemented) version of an implicit Option (prefer Some, fallback to None) and Either (prefer Right, fallback to Left).

For DummyImplicit just ask for an implicit Unit.

1 Like

Afaik DummyImplicit is basically required due to how Java handles type erasure/methods. Apart from that there is no real use for it (from what I have seen).

That is all fine and well, but why does it need to leak into scala? The compiler should do this automatically, IMO.

The compiler cannot do this, because it alters the binary API. So binary compatibility would be extremely unstable.

Also it can’t work when mixing in a class two such methods coming from two different parents, since they would have the same erased signature before mixing.

What you’re asking is wishful thinking.

1 Like

What I mean is, why do I need to use implicits/givens as a workaround? Any annotation will do, and the compiler can do the rest. E.g.:,
Instead of:

def foo(ps: String*) = ???
def foo(ps: Int*)(implicit i: DummyImplicit) = ???
def foo(ps: Any*)(implicit i1: DummyImplicit, i2: DummyImplicit) = ???

Do this:

@unique(1) def foo(ps: String*) = ???
@unique(2) def foo(ps: Int*) = ???
@unique(3) def foo(ps: Any*) = ???
2 Likes

That’s basically what @alpha does. So, it’s true we do not need DummyImplicit anymore for disambiguation of erasures.

3 Likes

Nice! so my example can look like:

@alpha("fooString") def foo(ps: String*) = ???
@alpha("fooInt") def foo(ps: Int*) = ???
@alpha("fooAny") def foo(ps: Any*) = ???

This is much better, but Is it possible to do this automatically with a @unique annotation, so it will act like an @alpha(signature) that uses some kind of string signature according to the type signature of the definition before erasure? That will allow preserving binary compatibility, no?
E.g.
@unique def foo(arg1 : Int, arg2 : String, arg3 : String*) : Unit
will act like
@alpha("`foo : (Int, String, Seq[String]) => Unit`") def foo(arg1 : Int, arg2 : String, arg3 : String*) : Unit

No, that won’t work in cases of overriding relationships. It’s possible for a method A.m to override B.m, yet they have different types before erasure. An @unique that derives a name based on the signatures before erasure would not work in those situations.

1 Like

Ok, understood, thanks!

@odersky currently the @alpha annotation does not affect the double definition error.

Can you open an issue for that? Thanks!

Already done :slight_smile:

So how would this work with subclassing? Suppose I have a class A defined as such:

class A {
  @alpha("fooString") def foo(ps: String*) = ???
  @alpha("fooInt") def foo(ps: Int*) = ???
  @alpha("fooAny") def foo(ps: Any*) = ???
}

and class B extends it, trying to override its methods. What would happen if the writers of B accidentally swap method names in the alpha annotation?

class B extends A {
  @alpha("fooInt") override def foo(ps: String*) = ???
  @alpha("fooString") override def foo(ps: Int*) = ???
}

Correct me if I’m wrong, but if you then have code like this, it will call fooInt instead of fooString:

val b: A = new B()
b.foo("Will", "this", "call", "fooInt?")

Currently, class B doesn’t compile because the alpha annotation can’t be used for double definitions yet, but once that is fixed, it won’t have any issue with external and internal names being different, so it might actually compile and then fail once run.

You cannot override with a different @alpha annotation

1 Like

That’s great! I forgot alpha annotations were checked.