1

I started playing with Scala few days ago. What I wanted to do is write a very small class that would represent natural numbers and I'd like it to be implicitly convertible from/to Int. Honestly I haven't done a lot so far. Here is the code:

object main  {
  class Natural(n: Int) {
    def isDividerOf(m: Natural): Boolean =  m % n == 0
  }

  implicit def Int(m: Natural): Int = m
  implicit def Natural(n: Int): Natural = new Natural(n)

  def main(args: Array[String]) ={
    println("test")
    println(60 isDividerOf 600)
  }
}

The code is able to compile, but when I run it (no matter the numbers I use as arguments to isDividerOf) the program execution is paused/hung forever, in other words after printing test it doesn't output anything no does it exit properly.

What am I doing wrong?

OlivierBlanvillain
  • 7,701
  • 4
  • 32
  • 51
Gonzalez
  • 681
  • 1
  • 11
  • 21

1 Answers1

3

Your definition for the implicit Natural => Int is responsible for the infinite recursion; what you wrote is equivalent to the following (expliciting the implicit conversions in your code):

implicit def Int2Nat(m: Natural): Int = Int2Nat(m)

What you want instead is:

implicit def Int2Nat(m: Natural): Int = m.n // Also change n to a val 

I got this information by compiling your code with scalac -Xprint:typer, which shows the internal compiler representation of your code after it's been typechecked (and all implicits have been resolved):

$ scalac -Xprint:typer so.scala
[[syntax trees at end of                     typer]] // s.scala
package <empty> {
  object main extends scala.AnyRef {
    def <init>(): main.type = {
      main.super.<init>();
      ()
    };
    class Natural extends scala.AnyRef {
      <paramaccessor> private[this] val n: Int = _;
      <stable> <accessor> <paramaccessor> def n: Int = Natural.this.n;
      def <init>(n: Int): main.Natural = {
        Natural.super.<init>();
        ()
      };
      def isDividerOf(m: main.Natural): Boolean = main.this.Int(m).%(Natural.this.n).==(0)
    };
    implicit def Int(m: main.Natural): Int = main.this.Int(m);
    implicit def Natural(n: Int): main.Natural = new main.this.Natural(n);
    def main(args: Array[String]): Unit = {
      scala.Predef.println("test");
      scala.Predef.println(main.this.Natural(60).isDividerOf(main.this.Natural(600)))
    }
  }
}

But please don't use implicit conversions. Ever. They are the devil.
You can achieve the same result with an extension method on Int:

implicit class isDividerOfOp(i: Int) {
  def isDividerOf(other: Int): Boolean =  other % i == 0
}
OlivierBlanvillain
  • 7,701
  • 4
  • 32
  • 51
  • Thanks @OlivierBlanvillain. The ```-Xprint:typer``` bit was really helpful, my bad I didn't know about it. Can you explain why you don't suggest using implicit conversions? Because they are source of confusions or there's some other reason? I honestly had an impression it is very common in scala. – Gonzalez May 07 '17 at 13:18
  • 1
    The fact that you, as a beginner, got confused by implicit conversions to the point where you took the time to go on SO and ask this question is IMO a pretty good indicator that implicit conversions are a big source of confusion. There is a lot to say on the subject, please open another question "Why I shouldn't use implicit conversions in my code?" :) – OlivierBlanvillain May 07 '17 at 14:32