This page is no longer maintained — Please continue to the home page at www.scala-lang.org

Case classes and optimisation

3 replies
paulbutcher
Joined: 2010-03-08,
User offline. Last seen 10 weeks 5 days ago.

I'm currently looking at getting Borachio (the mocking library I'm working on) to support case classes. In the process, I've seen some surprising behaviour that I think I can attribute to calls to the implicitly defined methods being optimised away?

So, for example, given the following class definition:

> case class CaseClass(x: Int, y: String)

The following (hopefully self-explanatory?) test fails because CaseClass.apply is never called:

> test("case class") {
> val m = mock[CaseClass]
> val mo = mockObject(CaseClass)
>
> mo.expects.apply(42, "foo") returning m
>
> CaseClass(42, "foo")
> }

This, by contrast, does pass ("newInstance" is Borachio's way of mocking constructor invocation):

> test("case class") {
> val m = mock[CaseClass]
>
> m.expects.newInstance(42, "foo")
>
> CaseClass(42, "foo")
> }

Sure enough, when I look at the bytecode, I can't find any evidence of apply being called.

Along similar lines, the following also fails:

> test("case class") {
> val m = mock[CaseClass]
> val mo = mockObject(CaseClass)
>
> mo.expects.unapply(CaseClass(3, "bar")) returning Some(3, "bar")
> CaseClass(3, "bar") match {
> case CaseClass(x, y) => assert(x == 3 && y == "bar")
> }
> }

And when I look at the bytecode, I can't find any evidence of unapply being called.

Firstly - have I got this right. Are these method invocations optimised away? Or am I missing something?

Secondly - is there any way to switch these optimisations off?

Thanks,

--
paul.butcher->msgCount++

Snetterton, Castle Combe, Cadwell Park...
Who says I have a one track mind?

http://www.paulbutcher.com/
LinkedIn: http://www.linkedin.com/in/paulbutcher
MSN: paul@paulbutcher.com
AIM: paulrabutcher
Skype: paulrabutcher

sjrd
Joined: 2011-04-09,
User offline. Last seen 51 weeks 2 days ago.
Re: Case classes and optimisation
Hi,

You don't need to look at the bytecode to verify this. Just use the -Xprint:cleanup option of the compiler:
$ scalac -Xprint:cleanup Test.scala

You'll see that, indeed, apply is optimized away:
  final object Test extends java.lang.Object with ScalaObject {
    def main(args: Array[java.lang.String]): Unit = {
      val obj: CaseClass = new CaseClass(1, "ABC");
      scala.this.Predef.println(scala.Int.box(obj.x()))
    };
    def this(): object Test = {
      Test.super.this();
      ()
    }
  }

Now I do not know whether it's possible to deactivate this.

Cheers,
Sébastien

On Tue, Aug 30, 2011 at 11:33, Paul Butcher <paul@paulbutcher.com> wrote:
I'm currently looking at getting Borachio (the mocking library I'm working on) to support case classes. In the process, I've seen some surprising behaviour that I think I can attribute to calls to the implicitly defined methods being optimised away?

So, for example, given the following class definition:

> case class CaseClass(x: Int, y: String)

The following (hopefully self-explanatory?) test fails because CaseClass.apply is never called:

>   test("case class") {
>     val m = mock[CaseClass]
>     val mo = mockObject(CaseClass)
>
>     mo.expects.apply(42, "foo") returning m
>
>     CaseClass(42, "foo")
>   }

This, by contrast, does pass ("newInstance" is Borachio's way of mocking constructor invocation):

>   test("case class") {
>     val m = mock[CaseClass]
>
>     m.expects.newInstance(42, "foo")
>
>     CaseClass(42, "foo")
>   }


Sure enough, when I look at the bytecode, I can't find any evidence of apply being called.

Along similar lines, the following also fails:

>   test("case class") {
>     val m = mock[CaseClass]
>     val mo = mockObject(CaseClass)
>
>     mo.expects.unapply(CaseClass(3, "bar")) returning Some(3, "bar")
>     CaseClass(3, "bar") match {
>       case CaseClass(x, y) => assert(x == 3 && y == "bar")
>     }
>   }


And when I look at the bytecode, I can't find any evidence of unapply being called.

Firstly - have I got this right. Are these method invocations optimised away? Or am I missing something?

Secondly - is there any way to switch these optimisations off?

Thanks,

--
paul.butcher->msgCount++

Snetterton, Castle Combe, Cadwell Park...
Who says I have a one track mind?

http://www.paulbutcher.com/
LinkedIn: http://www.linkedin.com/in/paulbutcher
MSN: paul@paulbutcher.com
AIM: paulrabutcher
Skype: paulrabutcher


odersky
Joined: 2008-07-29,
User offline. Last seen 45 weeks 6 days ago.
Re: Case classes and optimisation


On Tue, Aug 30, 2011 at 11:55 AM, Sébastien Doeraene <sjrdoeraene@gmail.com> wrote:
Hi,

You don't need to look at the bytecode to verify this. Just use the -Xprint:cleanup option of the compiler:
$ scalac -Xprint:cleanup Test.scala

You'll see that, indeed, apply is optimized away:
  final object Test extends java.lang.Object with ScalaObject {
    def main(args: Array[java.lang.String]): Unit = {
      val obj: CaseClass = new CaseClass(1, "ABC");
      scala.this.Predef.println(scala.Int.box(obj.x()))
    };
    def this(): object Test = {
      Test.super.this();
      ()
    }
  }

Now I do not know whether it's possible to deactivate this.

Cheers,
Sébastien

On Tue, Aug 30, 2011 at 11:33, Paul Butcher <paul@paulbutcher.com> wrote:
I'm currently looking at getting Borachio (the mocking library I'm working on) to support case classes. In the process, I've seen some surprising behaviour that I think I can attribute to calls to the implicitly defined methods being optimised away?

So, for example, given the following class definition:

> case class CaseClass(x: Int, y: String)

The following (hopefully self-explanatory?) test fails because CaseClass.apply is never called:

>   test("case class") {
>     val m = mock[CaseClass]
>     val mo = mockObject(CaseClass)
>
>     mo.expects.apply(42, "foo") returning m
>
>     CaseClass(42, "foo")
>   }

This, by contrast, does pass ("newInstance" is Borachio's way of mocking constructor invocation):

>   test("case class") {
>     val m = mock[CaseClass]
>
>     m.expects.newInstance(42, "foo")
>
>     CaseClass(42, "foo")
>   }


Sure enough, when I look at the bytecode, I can't find any evidence of apply being called.

Along similar lines, the following also fails:

>   test("case class") {
>     val m = mock[CaseClass]
>     val mo = mockObject(CaseClass)
>
>     mo.expects.unapply(CaseClass(3, "bar")) returning Some(3, "bar")
>     CaseClass(3, "bar") match {
>       case CaseClass(x, y) => assert(x == 3 && y == "bar")
>     }
>   }


And when I look at the bytecode, I can't find any evidence of unapply being called.

Firstly - have I got this right. Are these method invocations optimised away? Or am I missing something?

Secondly - is there any way to switch these optimisations off?

Thanks,

These operations are indeed optimized away. AFAIK there is not way to deactivate this.

 -- Martin
 

paulbutcher
Joined: 2010-03-08,
User offline. Last seen 10 weeks 5 days ago.
Re: Case classes and optimisation

Thanks Sébastien, Martin,

At least I don't need to worry that it's not working because of anything I'm doing wrong :-)

On 30 Aug 2011, at 11:20, martin odersky wrote:
> These operations are indeed optimized away. AFAIK there is not way to deactivate this.

--
paul.butcher->msgCount++

Snetterton, Castle Combe, Cadwell Park...
Who says I have a one track mind?

http://www.paulbutcher.com/
LinkedIn: http://www.linkedin.com/in/paulbutcher
MSN: paul@paulbutcher.com
AIM: paulrabutcher
Skype: paulrabutcher

Copyright © 2012 École Polytechnique Fédérale de Lausanne (EPFL), Lausanne, Switzerland