Home > Software engineering >  Implicit conversion of Int* to custom class in function call
Implicit conversion of Int* to custom class in function call

Time:07-10

I have a custom type MySeq extending IndexedSeq[Int] and its implicit conversion:

package example

import scala.language.implicitConversions

class MySeq(vals: IndexedSeq[Int]) extends IndexedSeq[Int] {
  def apply(i: Int): Int = vals(i)

  def length: Int = vals.length
}

object MySeq {
  implicit def seq2MySeq(vals: Int*): MySeq = new MySeq(vals.toIndexedSeq)
}

I want to be able to call some function of type MySeq => T as follows:

package example

object Hello extends App {
  def foo(xs: MySeq): MySeq = xs

  val ret = foo(1, 2, 3)
}

But I get a Too many arguments for method foo(MySeq) error when compiling with scala-2.13.8. What am I missing/misunderstanding?

CodePudding user response:

You can implicitly convert only 1 value to another 1 value, you cannot use conversion to "overload" unary method into variadic method.

You could implement conversions so that

foo(Seq(1, 2, 3))

would become

foo(MySeq.seq2MySeq(Seq(1, 2, 3))

but to be able to use

foo(1, 2, 3)

you would have to overload the method e.g.

def foo(ints: Int*) = foo(MySeq(ints))

CodePudding user response:

implicit conversions can be useful, but they have always been easy to misuse.

From the Baeldung Blog:

The major drawbacks with implicit in Scala 2 are:

  • Usage of the keyword implicit for unrelated functionalities
  • Unintended conversions due to implicit conversions
  • Unclear compiler error messages

You said you are still using 2.13.8, and this post talks mostly about what has changed in Scala 3, but the caveats of implicit can be difficult to debug and understand, and they are highlighted at the beginning of this post.

In Scala 3, for these reasons (and others), they have reworked implicits (and introduced a new system that is far more explicit and powerful given/using [but that's a whole 'nother can of worms; very powerful and useful, but only in Scala 3 ])

In your case, I would recommend a simple overload to your apply method for MySeq, no implicit conversion necessary:

class MySeq( vals: IndexedSeq[Int] ) extends IndexedSeq[Int] {

    // Unchanged
}
    
object MySeq {

    def apply( vals: Int* ): MySeq = new MySeq( vals.toIndexedSeq )
}
    
@main
def runner(): Unit = {

    // use overloaded apply, convenient but explicit conversion
    val mySeqFromVarArg: MySeq = MySeq( 1, 2, 3 )
}
  • Related