Skip to content

Pattern match in constructor results in extra class member #1913

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
scabug opened this issue Apr 22, 2009 · 6 comments
Open

Pattern match in constructor results in extra class member #1913

scabug opened this issue Apr 22, 2009 · 6 comments
Labels
bytecode fixed in Scala 3 This issue does not exist in the Scala 3 compiler (https://github.com/lampepfl/dotty/)
Milestone

Comments

@scabug
Copy link

scabug commented Apr 22, 2009

The compiler generates an extra member (a tuple) for the following class as can be seen from the generated byte code below.
The extra field is:

private final scala.Tuple3 x$$2;

Here is the class definition:

final class C(i: Int, j: Int, k: Int)
{
  val (m_i, m_j, m_k) = (i + 3, j + 7, k - j)

  override def toString: String =
  {
    "(" + m_i + ", " + m_j + ", " + m_k + ")"
  }
}

And here is the generated bytecode:

public final class math.C extends java.lang.Object implements scala.ScalaObject{
private final int m_k;

private final int m_j;

private final int m_i;

private final scala.Tuple3 x$$2;

public math.C(int, int, int);
  Code:
   0:   aload_0
   1:   invokespecial   SI-17; //Method java/lang/Object."<init>":()V
   4:   aload_0
   5:   iload_1
   6:   iconst_3
   7:   iadd
   8:   istore  4
   10:  iload_2
   11:  ldc     SI-18; //int 7
   13:  iadd
   14:  istore  5
   16:  iload_3
   17:  iload_2
   18:  isub
   19:  istore  6
   21:  new     SI-20; //class scala/Tuple3
   24:  dup
   25:  iload   4
   27:  invokestatic    SI-26; //Method scala/runtime/BoxesRunTime.boxToInteger:(I)Ljava/lang/Integer;
   30:  iload   5
   32:  invokestatic    SI-26; //Method scala/runtime/BoxesRunTime.boxToInteger:(I)Ljava/lang/Integer;
   35:  iload   6
   37:  invokestatic    SI-26; //Method scala/runtime/BoxesRunTime.boxToInteger:(I)Ljava/lang/Integer;
   40:  invokespecial   SI-29; //Method scala/Tuple3."<init>":(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V
   43:  putfield        SI-33; //Field x$$2:Lscala/Tuple3;
   46:  aload_0
   47:  aload_0
   48:  getfield        SI-33; //Field x$$2:Lscala/Tuple3;
   51:  invokevirtual   SI-37; //Method scala/Tuple3._1:()Ljava/lang/Object;
   54:  invokestatic    SI-41; //Method scala/runtime/BoxesRunTime.unboxToInt:(Ljava/lang/Object;)I
   57:  putfield        SI-43; //Field m_i:I
   60:  aload_0
   61:  aload_0
   62:  getfield        SI-33; //Field x$$2:Lscala/Tuple3;
   65:  invokevirtual   SI-46; //Method scala/Tuple3._2:()Ljava/lang/Object;
   68:  invokestatic    SI-41; //Method scala/runtime/BoxesRunTime.unboxToInt:(Ljava/lang/Object;)I
   71:  putfield        SI-48; //Field m_j:I
   74:  aload_0
   75:  aload_0
   76:  getfield        SI-33; //Field x$$2:Lscala/Tuple3;
   79:  invokevirtual   SI-51; //Method scala/Tuple3._3:()Ljava/lang/Object;
   82:  invokestatic    SI-41; //Method scala/runtime/BoxesRunTime.unboxToInt:(Ljava/lang/Object;)I
   85:  putfield        SI-53; //Field m_k:I
   88:  return

As a general comment on the language itself, I find the feature of class constructors as one of the least thought out parts of the Scala language. Maybe it's because I am new to the language but it's never clear what becomes a field and what doesn't. Also the inability to use local variables in the class constructor (because it will/may become a member variable) is very troubling and restrictive. Basically one is forced to always use the factory pattern (in a companion object) so as to be sure what is going on and so any savings (in conciseness) from the class-constructor feature is nullified.

@scabug
Copy link
Author

scabug commented Apr 22, 2009

Imported From: https://issues.scala-lang.org/browse/SI-1913?orig=1
Reporter: johnconnor
See #37, #17, #18, #26, #29, #41, #43, #48, #51, #53, #20, #33, #46

@scabug
Copy link
Author

scabug commented Apr 27, 2009

@dragos said:
Local variables in blocks won't become fields, and there are explicit constructors that behave like Java constructors.

Since vals declared in the class body (or primary constructor) are lexically scoped, they are visible inside methods... therefore they are turned into fields (not talking about the tuple here). What would be a better rule?

@scabug
Copy link
Author

scabug commented Apr 27, 2009

@ijuma said:
I am not the original reporter, but the main problem in my view is the tuple case. As you say, local variables in blocks won't become fields so that's great.

The problem occurs when you need to compute more than one value inside the block. Most people are surprised when they find out that using pattern matching to assign the values computed in the block (or def) will result in an extra tuple being stored in the class.

If the implementation was improved to store the tuple as a local variable in the constructor there would be a simple and predictable way to deal with the described need.

@scabug
Copy link
Author

scabug commented Apr 27, 2009

@dragos said:
Yes, the extra tuple should be removed, that's why the ticket is still open.

@scabug scabug added this to the Backlog milestone Apr 6, 2017
@SethTisue SethTisue changed the title Bug in Object Construction -- extra member variables Pattern match in constructor results in extra class member May 10, 2017
@Atry Atry added the bytecode label May 10, 2017
Atry added a commit to ThoughtWorksInc/Constructor.scala that referenced this issue May 10, 2017
@SethTisue
Copy link
Member

curious if Scala 3 is the same

@som-snytt
Copy link

Scala 3 does the obvious and deconstructs in the constructor. In this example, C has members x, a, b as expected.

class X {
  def x = 42
}
class C {
  val x = new X
  val _ = x.x

  val (a, b) = (42, 17)
}

Scala 2 warns

extra.scala:7: warning: Pattern definition introduces Unit-valued member of C; consider wrapping it in `locally { ... }`.
  val _ = x.x
      ^

for

  private final X x;
    descriptor: LX;
    flags: (0x0012) ACC_PRIVATE, ACC_FINAL

  private final scala.runtime.BoxedUnit x$1;
    descriptor: Lscala/runtime/BoxedUnit;
    flags: (0x1012) ACC_PRIVATE, ACC_FINAL, ACC_SYNTHETIC

  private final scala.Tuple2 x$2;
    descriptor: Lscala/Tuple2;
    flags: (0x1012) ACC_PRIVATE, ACC_FINAL, ACC_SYNTHETIC

  private final int a;
    descriptor: I
    flags: (0x0012) ACC_PRIVATE, ACC_FINAL

  private final int b;
    descriptor: I
    flags: (0x0012) ACC_PRIVATE, ACC_FINAL

but actually there are 2 extra members! Update the ticket! I guess that is why there is another ticket about the tuple member.

@SethTisue SethTisue added the fixed in Scala 3 This issue does not exist in the Scala 3 compiler (https://github.com/lampepfl/dotty/) label Nov 15, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bytecode fixed in Scala 3 This issue does not exist in the Scala 3 compiler (https://github.com/lampepfl/dotty/)
Projects
None yet
Development

No branches or pull requests

5 participants