Search code examples
scalaimplicit-conversionimplicit-declaration

Implicit conversion declared in companion object is not called


I am trying sample programs on Scala implicits. I am trying to implement one solution from the "Scala for the Impatient" book.

I have written as Fraction class having two member variables numerator and denominator with an operation overloading method "*(fraction)", so that we can call multiplication on Fraction objects like fration1 * fraction2 * fraction3.

Now, I tried to implement intToFraction and fractionToInt implicit methods, so that we can also write syntax like 2fraction13. I tried implementing those methods in companion object of Fraction class. But those methods are not getting called.

As suggested by Scala docs, Scala compiler will try to find out implicit implementation in the companion object. Here, we are performing operation on objects of Fraction class, so I assume, implicits declared in Fraction should have been called automatically. I also tried by importing implicit methods explicitly but it did not work.

Please find code snippet below for your reference.

package com.example.implicits.conversion

object ImplicitConversionTester extends  App {
  val f1 = Fraction(4,2)
  val result = 2 * f1
  println(s"${f1 * f1}")
  println(s"${result}")
 
}

class Fraction(val numerator : Int,val denominator : Int) {

  def *(fraction : Fraction): Fraction = {
    println("##### Call to operator overloading *")
    Fraction(this.numerator*fraction.numerator, this.denominator*fraction.denominator)
  }

  override def toString: String = s"Fraction($numerator,$denominator) ===== ${numerator/denominator}"

}

object Fraction {
  def apply(numerator: Int, denominator: Int): Fraction = new Fraction(numerator, denominator)

  implicit def intToFraction(num : Int) = {
    println("##### intToFraction")
    Fraction(num,1)
  }

  implicit def fractionToInt(fr : Fraction) = {
    println("##### fractionToInt")
    fr.numerator/fr.denominator
  }

}

any suggestion to fix this problem appreciated!


Solution

  • The problem was in the declaration of the implicit methods without explicitly mentioned return types. I did not mention the return type in the code snippet above. Implicits are working fine after adding the function return type. So, There is no need to explicitly import the implicit functions in ImplicitConversionTester class to fix the problem.

    Below is the updated fraction class to fix the error:

    class Fraction(val numerator : Int,val denominator : Int) {
    
      val test = 2
    
      def *(fraction : Fraction): Fraction = {
        println("##### Call to operator overloading *")
        Fraction(this.numerator*fraction.numerator, this.denominator*fraction.denominator)
      }
    
      override def toString: String = s"Fraction($numerator,$denominator) ===== ${numerator/denominator}"
    }
    
    object Fraction {
      def apply(numerator: Int, denominator: Int): Fraction = new Fraction(numerator, denominator)
    
      implicit def intToFraction(num : Int) : Fraction = { // RETURN TYPE FRACTION
        println("##### intToFraction")
        Fraction(num,1)
      }
      implicit def fractionToInt(fr : Fraction) : Int  = { // ADDED RETURN TYPE INT
        println("##### fractionToInt")
        fr.numerator/fr.denominator
      }
    
    }
    

    Sharing below more details about how Scala compiler looksup the implicit conversion. Below image is taken from the book "Scala for the Impatient".

    enter image description here

    As mentioned in the book, Scala compiler gives first preference to companion object of the source or the target type.

    Here, What is source and target type that depends on the order of expression. For example, if the expression is "2*Fraction(2,4)", the expression is evaluated from left to right. Int "2" has already implementation present for multiplication method (*). So, compiler tries to find implicit conversion from "Fraction to Int" in "Int" type first. As "Int" type does not have any implementation for "Fraction to Int", compiler will lookup companion object of Fraction class where it finds a method "fractionToInt". In this example, target type is Int and Source type is Fraction.

    Consider the example of expression "Fraction(2,4)*2", Here as the Fraction class has implementation available for multiplication "*", compiler tries to find implicit for converting Int to Fraction. So target type is Fraction and Source type is Int. As there is no such implicit method/class present in Int class, compiler looksup Fraction companion object and finds "IntToFraction" implicit.