3
votes

I've recently started learning Scala's implicit "magic" and I'm having troubles with implicit Scala objects. I've tried all the possible variants but nothing seems to work.

Lets assume I have a class like this with some solve() function. It should return 2 Float values if the input a, b were Float. Otherwise it should return another type values:

class Solver[T](val a: T, val b: T) {
    def solve[A](implicit num: customNumeric[T]): Option[(T, T)] = {
        Option(
            num.f(num.g(a)),
            num.f(num.g(b)))
    }
}

Let's assume another-type-value is an object of class like this:

class MyClass[T] (x: T, y: T)(implicit num: customNumeric[T]) {
    val field : T = num.f(x)
}

And let's also assume that I dont have the functions I need in basic Scala Numeric so I should make my own custom numeric.

Here is what I've done:

I've made an abstract class for my own customNumeric with my methods f() and g() and couple of implicit objects that extend my customNumeric for some value types (Int, Float for example) and implemented method in them:

abstract class customNumeric[T] {
    def f(x: T): T
    def g(x: T): T
}

object customNumeric {
    implicit object IntIsCustomNumeric extends customNumeric[MyClass[Int]] {
        def f(x: MyClass[Int]) = new MyClass[Int](x.field + 5)
        def g(x: MyClass[Int]) = new MyClass[Int](x.field - 5)
    }
    implicit object FloatIsCustomNumeric extends customNumeric[Float] {
        def f(x: Float): Float = x + 3
        def g(x: Float): Float = x - 3
    }
}

In my opinion Solver's solve() should use implicit customNumeric object to get implementations for methods referenced inside solve() based upon type of the Solver's input values.

But this doesn't work as compiler says:

could not find implicit value for parameter num: customNumeric[Int]
        def f...

It also complains because of not enough arguments for constructor MyClass at the same line.

I've already tried making companion object to cast Int to MyClass:

object Fraction {
    implicit def int2MyClass(x: Int): MyClass[Int] = new MyClass[Int](x, 1)
}

But that also doen't seem to work. And I've tried to make another implicit object to implement methods I use in customNumeric[MyClass[Int]].

Do you have any ideas? Thanks in advance!

2

2 Answers

3
votes

The problem is that you're trying to define the implicit objects with classes that themselves require that same implicit object.

Meaning, this:

class MyClass[T] (x: T, y: T)(implicit num: CustomNumeric[T])

Requires an existence of an implicit CustomNumeric[T]. You cannot define IntIsCustomNumeric using that type:

implicit object IntIsCustomNumeric extends customNumeric[MyClass[Int]]

When you implement IntIsCustomNumeric, you need to implement it for type Int, not for type MyClass[Int]. When you do that, i.e:

object CustomNumeric {
  implicit object IntIsCustomNumeric extends CustomNumeric[Int] {
    override def f(x: Int): Int = x
    override def g(x: Int): Int = x
  }
}

Now, you can create an Solver[Int] which takes an implicit CustomNumeric[Int]:

def main(args: Array[String]): Unit = {
  import CustomNumeric._

  val solver = new Solver[Int](1, 2)
  println(solver.solve)
}

Now, it's also easier to create an implicit conversion from an Int type to something that creates a MyClass[Int]:

implicit object MyClassIsCustomNumeric extends CustomNumeric[MyClass[Int]] {
  override def f(x: MyClass[Int]): MyClass[Int] = new MyClass[Int](x.field + 5)
  override def g(x: MyClass[Int]): MyClass[Int] = new MyClass[Int](x.field + 3)
}

implicit def intToMyClass(i: Int) = new MyClass[Int](i)
0
votes

What do you think about this

object customNumeric {

  implicit object IntIsCustomNumeric extends customNumeric[Int] {
    def f(x: Int): Int = x + 3

    def g(x: Int): Int = x - 3
  }

  implicit object FloatIsCustomNumeric extends customNumeric[Float] {
    def f(x: Float): Float = x + 3

    def g(x: Float): Float = x - 3
  }

  implicit def int2MyClass(x: Int): MyClass[Int] = new MyClass[Int](x, 1)

  implicit object cn extends customNumeric[MyClass[Int]] {
    def f(x: MyClass[Int]) = x.field + 5

    def g(x: MyClass[Int]) = x.field - 5
  }

}