Search code examples
scalastack-overflowimplicit-conversion

How to prevent stack overflow when using Scala's Predef.implicitly


Given this simple code snippet I am astounded to provoke a stack overflow this easy:

implicit def foobar: Unit = implicitly[Unit]

In my little more complex use case I have the following situtation:

abstract class Foo {
  type Repr_Tpe
  protected implicit def repr2Ordered: Repr_Tpe => Ordered[Repr_Tpe]
}

class Bar extends Foo {
  type Repr_Tpe = Long
  protected implicit def repr2Ordered = implicitly[Repr_Tpe => Ordered[Repr_Tpe]]
}

Defining method repr2Ordered in class Foo does not work because type Repr_Tpe is abstract. So I decided to copy & paste the declaration and make a definition out of it; apparently leading to the stack overflow from above. Only by removing the modifier implicit from the definition in class Bar solves this problem.

Isn't there an elegant solution circumventing this pitfall?


Solution

  • You've defined foobar to be the implicit value of type Unit. Then you've defined it as the implicit value of type Unit. Thinking of it this way:

    implicit def foobar: Unit = implicitly[Unit]
    // you've defined foobar as the implicit value for Unit.
    // so implicitly[Unit] is the same as calling foobar
    // which is the same as:
    implicit def foobar: Unit = foobar
    

    You should be no more surprised that this causes a stack overflow than you would be by this statement:

    def tharSheBlows: Unit = tharSheBlows
    

    For something with a bit more elegance, I would use a view bound to ensure that the type paramater is Ordered instead.

    scala> abstract class Foo[Repr_Tpe <% Ordered[Repr_Tpe]] {}
    defined class Foo
    
    scala> class Bar extends Foo[Long] {}
    defined class Bar
    
    scala> case class Unordered(data: String)
    defined class Unordered
    
    scala> class Bam extends Foo[Unordered] {}
    <console>:10: error: No implicit view available from Unordered => Ordered[Unordered].
           class Bam extends Foo[Unordered] {}
                     ^
    
    scala> implicit def bringOrder(u: Unordered) = new Ordered[Unordered] { def compare(that: Unordered) = u.data.compareTo(that.data) }
    bringOrder: (u: Unordered)java.lang.Object with Ordered[Unordered]
    
    scala> class Bam extends Foo[Unordered] {}
    defined class Bam