I’m building a computer algebra library for Scala 3, and am using typeclasses to model algebraic structures, and extension methods to define algebraic operations on typeclass carriers. It is common that different algebraic operations on different algebraic structures share the same name, and when I try to model this in Scala 3, I run into two issues regarding resolution of extension methods.
Here is a minified example of the first issue:
trait A[TA]:
extension (a1: TA) def *(a2: TA): TA
trait B[TA: A, TB]:
extension (a: TA) def *(b: TB): TB
def breaks[TA: A, TB: [TB] =>>B[TA, TB]](a: TA): TA =
// Error: Found: (a : TA)
// Required: ?{ * : ? }
// Note that implicit extension methods cannot be applied because
// they are ambiguous;
// both parameter evidence$2 and parameter evidence$1 provide an
// extension method `*` on (a : TA)
a * a
I’m having a hard time understanding the documentation, but it seems to me that this shouldn’t be ambiguous?
- The selection is rewritten to
m[Ts](e)and typechecked, using the following slight modification of the name resolution rules:
If
mis imported by several imports which are all on the nesting level, try each import as an extension method instead of failing with an ambiguity. If only one import leads to an expansion that typechecks without errors, pick that expansion. If there are several such imports, but only one import which is not a wildcard import, pick the expansion from that import. Otherwise, report an ambiguous reference error.Note: This relaxation of the import rules applies only if the method
mis used as an extension method. If it is used as a normal method in prefix form, the usual import rules apply, which means that importingmfrom multiple places can lead to an ambiguity error.
- If the first rewriting does not typecheck with expected type
T, and there is an extension methodmin some eligible objecto, the selection is rewritten too.m[Ts](e). An objectois eligible if
2.1)
oforms part of the implicit scope ofT, or2.2)
ois a given instance that is visible at the point of the application, or2.3)
ois a given instance in the implicit scope ofT.This second rewriting is attempted at the time where the compiler also tries an implicit conversion from
Tto a type containingm. If there is more than one way of rewriting, an ambiguity error results.
Both extensions are available via rule 2.2), and there is only one expansion which typechecks, so it shouldn’t be ambiguous? Or am I misunderstanding something?
The second issue is similar to the first, but instead of an ambiguity error, the Scala compiler doesn’t even try the first extension:
trait A[TA]:
extension (a1: TA) def *(a2: TA): TA
trait B[TA: A, TB]:
extension (a: TA) def *(b: TB): TB
def breaks(a: TA): TA = a * a // Error: Found: (a: TA) required: TB
Is this expected behaviour? Again, as the second extension fails to typecheck, shouldn’t Scala continue resolution with the first extension method via rule 2.2)?