11

I was working through the code examples from the chapter on Traits in Programming in Scala Edition1 https://www.artima.com/pins1ed/traits.html

and came across a weird behavior because of my typo. While overriding method of a trait below code snippet doesn't give any compile error although the return types of the overridden method is different Unit vs String. But upon calling the method on an object it returns Unit but doesn't print anything.

trait Philosophical {
    def philosophize = println("I consume memory, therefore I am!")
}

class Frog extends Philosophical {
  override def toString = "green"
  override def philosophize = "It aint easy to be " + toString + "!"
}

val frog = new Frog
//frog: Frog = green

frog.philosophize
// no message printed on console

val f = frog.philosophize
//f: Unit = ()

But when I give the explicit return type in the overridden method , it gives a compile error:

class Frog extends Philosophical {
  override def toString = "green"
  override def philosophize: String = "It aint easy to be " + toString + "!"
}
         override def philosophize: String = "It aint easy to be " + toString +
                      ^
On line 3: error: incompatible type in overriding
       def philosophize: Unit (defined in trait Philosophical);
        found   : => String
        required: => Unit

Can anyone help explain why no compile error in the first case.

Mario Galic
  • 47,285
  • 6
  • 56
  • 98
Shanil
  • 388
  • 1
  • 4
  • 16

2 Answers2

8

When the expected type is Unit, any value can be accepted:

Value Discarding

If e has some value type and the expected type is Unit, e is converted to the expected type by embedding it in the term { e; () }.

Community
  • 1
  • 1
Alexey Romanov
  • 167,066
  • 35
  • 309
  • 487
6

my question is why it got through the compiler in the 1st case

When you did not specify the return type explicitly it was inferred by the type it needs to have for the override to work.

That turned out to be Unit.

Since String values (the value of the expression making up the function body) can be assigned to Unit, the compiler is happy.

Thilo
  • 257,207
  • 101
  • 511
  • 656
  • 1
    What I want to know now, though, is why the explicit return type `String` was rejected. In Java (and I think in Scala, too), you are allowed to *narrow* the return type when overriding. For example, when the parent method returns `Number`, you can return `Integer`. Maybe `void`/`Unit` is special. – Thilo Dec 02 '19 at 13:25
  • 1
    This compiles, for example: `trait Philosophical { def philosophize : Number = 1 } class Frog extends Philosophical { override def philosophize : Integer = 2 }` – Thilo Dec 02 '19 at 13:28
  • 2
    You can narrow the return type to a subtype; but you can't narrow it to a type which can only be implicitly converted to the overridden method's return type. `String` to `Unit` is more like the second one, even if it isn't _exactly_ that. – Alexey Romanov Dec 02 '19 at 13:32
  • I suppose `void` returns on the JVM have to be special, because the bytecode generated will not provide for returning any result value. So there is no way to change that and still have a compatible call signature. – Thilo Dec 02 '19 at 13:32
  • 2
    On bytecode level there's no narrowing return type either, there are actually two methods in `Frog`: `def philosophize : Integer` and `def philosophize : Number`. The second one actually overrides the `Philosophical`'s method (and calls the first one). The same could certainly be done for `void`/anything else, the designers just decided not to do it. – Alexey Romanov Dec 02 '19 at 13:36
  • @AlexeyRomanov Is that additional synthetic method a Scala-thing? Or does Java also do that for narrowed return types? – Thilo Dec 02 '19 at 13:40
  • @AlexeyRomanov Actually, are you sure about that? Can you have two methods of the same name and argument signature (`def philosophize`) in the same (JVM) class? – Thilo Dec 02 '19 at 13:42
  • 2
    1. Java does the same. 2. Yes, in bytecode you can. See e.g. https://stackoverflow.com/questions/18655541/how-covariant-method-overriding-is-implemented-using-bridging-technique-in-java and https://stackoverflow.com/questions/58065680/java-interface-synthetic-method-generation-while-narrowing-return-type. – Alexey Romanov Dec 02 '19 at 13:50