14

The following code does not compile:

import scala.language.implicitConversions

trait Base {
  class Wrp[+T](val v: T) // wrapper / internal representation
}

trait BooleanOps extends Base {
  // implicit conversion
  implicit def lift2BooleanOpsCls(x: Boolean): BooleanOpsCls =
    new BooleanOpsCls(new Wrp[Boolean](x))
  class BooleanOpsCls(wx: Wrp[Boolean]) {
    def ||(wy: =>Wrp[Boolean]): Wrp[Boolean] = new Wrp[Boolean](wx.v || wy.v)
  }
}

trait MyExample extends BooleanOps {
  // test method
  def foo(): Wrp[Boolean] = {
    val ret: Wrp[Boolean] = false || new Wrp[Boolean](true)
    ret
  }
}

Output:

MyExample.scala:18: error: type mismatch;
 found   : MyExample.this.Wrp[Boolean]
 required: Boolean
        val ret: Wrp[Boolean] = false || new Wrp[Boolean](true)
                                         ^

But if I:

1) put the class Wrp outside of Base

or

2) move the body of BooleanOps to MyExample

everything compiles.

Why does not the original example work? If you have some insight in this behavior, help would be appreciated. Thank you.

perovic
  • 266
  • 1
  • 7

3 Answers3

5

One issue is the call-by-name nature of the argument in the def ||(wy: =>Wrp[Boolean])
if you rewite it to def ||(wy: Wrp[Boolean]) it works

but I agree that it is weird that it works if you move around Wrp or BooleanOpsCls! Intended or bug of implicit resolution??

cedric
  • 351
  • 4
  • 13
  • Great, I can confirm that this works. Thanks :) The question remains what is exactly happening in the original example, also relating to the two workarounds proposed. – perovic Jan 23 '16 at 03:34
  • 1
    @perovic Just the idea in context of @cbastin answer: I assume that because of the nature of call-by-name argument implicit search is done differently, because `=>Wrp[Boolean]` is a block of code rather than type. Because of that compiler doesn't consider this method definition is applicable during implicit search. Unfortunately I cannot confirm this with any reliable source, so it is a completely wild guess. But given that the behavior is weird and depends on `Wrp` positioning, it looks like a bug, and I guess it's worth posting it as a jira ticket for compiler team? – Archeg Jan 23 '16 at 07:39
  • @Archeg Thank you for the advice. It seems that many elements and rules are interacting. I am still unsure this is a bug, so I will wait a bit more in hope someone will be able to give a full explanation. – perovic Jan 23 '16 at 12:14
1

The original example will work if you rename the || method. The compiler finds the false.||() method and doesn't bother to look for an implicit that might also work there.

jwvh
  • 50,871
  • 7
  • 38
  • 64
  • Indeed, this works, but I am trying to overload `||` operator behavior. The compiler finds the method with the same name (`||`), but the argument type is different (`Wrp[Boolean]` instead of `Boolean`), so it should perform implicit search. This indeed happens if I put the implicit conversion in the body of `MyExample` or if I put the `class Wrp` definition outside of `Base`, as specified in the question. I do not understand why these solutions work and the original example does not. – perovic Jan 23 '16 at 01:34
1

The problem is that there is no single class called Wrp (ignore the T for a moment) -- Base does not define Wrp but rather defines a named subclass of each and every concrete class that extends Base. The implicits are a red herring, too. The error that's the giveaway is the mention of MyExample.this.Wrp -- remember that there is no such class even as MyExample -- val x = new MyExample would have type Object with MyExample.

Rob Starling
  • 3,868
  • 3
  • 23
  • 40
  • Thank you for the answer Rob. In the actual code `Wrp` is an abstract type, I have simplified it here. But still the example works if the `Wrp[Boolean]` argument of the `||` operator is call-by-value or if I put the implicit conversion (both the `implicit def` and the `class`) inside MyExample trait. Do you know how does this relate to your answer? – perovic Jan 23 '16 at 05:59
  • I'm not sure... maybe you could add an additional, alternative minimal example? – Rob Starling Jan 23 '16 at 21:35