Override extension methods, but does not work?

Hi,

Defining the two methods as overrided extension methods does not work. See the following code:

trait Term
//  def :- (body: Term*): String = "string"  // work
//  def :- (a: Unit) : String = "string"

case class Atom(name: String) extends Term

object t1 extends Term
object t2 extends Term

extension (term: Term)
  def :- (body: Term*): String = "string"  // not work
  def :- (a: Unit) : String = "string"

Now I can use it this way:

Atom("no arg") :- ()

But the following code can not be compiled:

Atom("Haha") :- (t1, t2)

The error message showed by the compiler is:

value :- is not a member of sample.test.Atom.
An extension method was tried, but could not be fully constructed:

    sample.test.:-()    failed with

        value :-: <overloaded sample.test.:-> does not take parameters bloop

if I defined the above two methods in Term as normal methods, it works well.

Defining them as extension methods is expected. But why does the above definition fail?

Tested against Scala 3.2.0

Thanks for your help

Guofeng

Looks a bit like this issue which was fixed.

Very strange. This works:

extension (term: Term)
  def :- (body: Term*): String = "string"
  //def :- (a: Unit) : String = "string"

Atom("Haha") :- (t1, t2)

This doesn’t work:

extension (term: Term)
  def :- (body: Term*): String = "string"
  def :- (a: Unit) : String = "string"

Atom("Haha") :- (t1, t2)

But this works again:

extension (term: Term)
  def :- (body: Term*): String = "string"
  def :- (a: Unit) : String = "string"

Atom("Haha").:-(t1, t2)

And for some unknown reason vararg splicing doesn’t work anymore in infix methods. But one syntax explains that it doesn’t work anymore while the other just gives an opaque error.

Atom("Haha") :- (arg*) 
// expression expected but ')' found
Atom("Haha") :- (arg:_*)
// `_*` can be used only for last argument of method application.
// It is no longer allowed in operands of infix operations.

Will it be an issue of Scala 3?

By the hint from that issue, If I use Tuple as the parameter, it passes the compilation.

extension (term: Term)
  def :- (a: Unit) : String = "string"
  def :- (body: Tuple): String = "string"

But how to constraint the type of the element of the tuple to be Term?

Thanks for your reply.

You may be able to do it like this

trait Term(val foo: String)
case class Atom(name: String) extends Term(name)
object t1 extends Term("t1")
object t2 extends Term("t2")

extension (term: Term)
  def :- (a: Unit) : String = "string"
  def :- (body: Tuple)(using Tuple.Union[body.type] <:< Term): String =
    body.toList.map(_.foo).mkString

Atom("foo") :- (t1, t2, t1) // t1~t2~t1

I think it would we good to report it. It seems unlikely to me that varargs, overloading, and extension methods are supposed to interact like this. But it could also be that the conclusion will be that varargs should never work in infix notation…

I reported it here, and get the response from Odersky:

An infix def is a binary operator, so varargs don’t make sense. There is a special case to keep the old behavior but that only kicks in if the method is not overloaded.

Thanks for the support!

BTW, your suggestion about the typing constraints works well here.