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

Temporary Values in Constructors Retained As Fields

25 replies
Randall R Schulz
Joined: 2008-12-16,
User offline. Last seen 1 year 29 weeks ago.

Hi,

I'm working through Programming in Scala and when I came to the Rational
number example, I found myself wondering about the "private val g"
defined to hold the result of the greatest common divisor calculation
used to reduce the arguments to the normal form. This value is used
twice, so is computed once and stored in a private val so both the
numerator and the denominator can be divided by it. It is never needed
after the reduced numerator and denominator are computed:

-==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==-
/*
* Copyright (C) 2007-2008 Artima, Inc. All rights reserved.
*/
class Rational(n: Int, d: Int) {

require(d != 0)

private val g = gcd(n.abs, d.abs)
val numer = n / g
val denom = d / g

// ...

private def gcd(a: Int, b: Int): Int =
if (b == 0) a else gcd(b, a % b)
}
-==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==-

When I compile this and dump it with javap, I find that the private val
g is part of the class:

-==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==-
% javap -private !-2:$
javap -private Rational
Compiled from "Rational.scala"
public class Rational extends java.lang.Object implements
scala.ScalaObject{
private final int denom;
private final int numer;
private final int g;
...
private int g();
...
}
-==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==-

Now if Rational is going to be used in a way that produces large
populations, this wasted field is undesirable. So the question is: How
does one avoid it? I puzzled over this for quite a while and couldn't
figure out how, but I'm a Scala neophyte, at best, so this failure may
just reflect the insufficiency of my knowledge.

What's the answer? Is the only way out calling gcd(...) twice?

Randall Schulz

Jorge Ortiz
Joined: 2008-12-16,
User offline. Last seen 29 weeks 4 days ago.
Re: Temporary Values in Constructors Retained As Fields
Try:

  val (numer, denom) = {
    val g = gcd(n.abs, d.abs)
    (n / g, d / g)
  }

--j

On Sun, Mar 15, 2009 at 10:23 AM, Randall R Schulz <rschulz [at] sonic [dot] net> wrote:
Hi,

I'm working through Programming in Scala and when I came to the Rational
number example, I found myself wondering about the "private val g"
defined to hold the result of the greatest common divisor calculation
used to reduce the arguments to the normal form. This value is used
twice, so is computed once and stored in a private val so both the
numerator and the denominator can be divided by it. It is never needed
after the reduced numerator and denominator are computed:

-==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==-
/*
 * Copyright (C) 2007-2008 Artima, Inc. All rights reserved.
 */
class Rational(n: Int, d: Int) {

 require(d != 0)

 private val g = gcd(n.abs, d.abs)
 val numer = n / g
 val denom = d / g

 // ...

 private def gcd(a: Int, b: Int): Int =
   if (b == 0) a else gcd(b, a % b)
}
-==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==-


When I compile this and dump it with javap, I find that the private val
g is part of the class:

-==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==-
% javap -private !-2:$
javap -private Rational
Compiled from "Rational.scala"
public class Rational extends java.lang.Object implements
scala.ScalaObject{
   private final int denom;
   private final int numer;
   private final int g;
   ...
   private int g();
   ...
}
-==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==-


Now if Rational is going to be used in a way that produces large
populations, this wasted field is undesirable. So the question is: How
does one avoid it? I puzzled over this for quite a while and couldn't
figure out how, but I'm a Scala neophyte, at best, so this failure may
just reflect the insufficiency of my knowledge.

What's the answer? Is the only way out calling gcd(...) twice?


Randall Schulz

Jan Lohre
Joined: 2008-12-17,
User offline. Last seen 42 years 45 weeks ago.
Re: Temporary Values in Constructors Retained As Fields
The following should work:

val (numer,denom) = {
  val g = gcd(n.abs, d.abs)
  (n/g,d/g)
}

I did not validate it though.

Kind regards,
Jan

2009/3/15 Randall R Schulz <rschulz [at] sonic [dot] net>
Hi,

I'm working through Programming in Scala and when I came to the Rational
number example, I found myself wondering about the "private val g"
defined to hold the result of the greatest common divisor calculation
used to reduce the arguments to the normal form. This value is used
twice, so is computed once and stored in a private val so both the
numerator and the denominator can be divided by it. It is never needed
after the reduced numerator and denominator are computed:

-==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==-
/*
 * Copyright (C) 2007-2008 Artima, Inc. All rights reserved.
 */
class Rational(n: Int, d: Int) {

 require(d != 0)

 private val g = gcd(n.abs, d.abs)
 val numer = n / g
 val denom = d / g

 // ...

 private def gcd(a: Int, b: Int): Int =
   if (b == 0) a else gcd(b, a % b)
}
-==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==-


When I compile this and dump it with javap, I find that the private val
g is part of the class:

-==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==-
% javap -private !-2:$
javap -private Rational
Compiled from "Rational.scala"
public class Rational extends java.lang.Object implements
scala.ScalaObject{
   private final int denom;
   private final int numer;
   private final int g;
   ...
   private int g();
   ...
}
-==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==-


Now if Rational is going to be used in a way that produces large
populations, this wasted field is undesirable. So the question is: How
does one avoid it? I puzzled over this for quite a while and couldn't
figure out how, but I'm a Scala neophyte, at best, so this failure may
just reflect the insufficiency of my knowledge.

What's the answer? Is the only way out calling gcd(...) twice?


Randall Schulz

Jorge Ortiz
Joined: 2008-12-16,
User offline. Last seen 29 weeks 4 days ago.
Re: Temporary Values in Constructors Retained As Fields
Neverming, it seems that doesn't actually help.

Stick with factories and assert gcd == 1.

--j

On Sun, Mar 15, 2009 at 11:38 AM, Jorge Ortiz <jorge [dot] ortiz [at] gmail [dot] com> wrote:
Try:

  val (numer, denom) = {
    val g = gcd(n.abs, d.abs)
    (n / g, d / g)
  }

--j

On Sun, Mar 15, 2009 at 10:23 AM, Randall R Schulz <rschulz [at] sonic [dot] net> wrote:
Hi,

I'm working through Programming in Scala and when I came to the Rational
number example, I found myself wondering about the "private val g"
defined to hold the result of the greatest common divisor calculation
used to reduce the arguments to the normal form. This value is used
twice, so is computed once and stored in a private val so both the
numerator and the denominator can be divided by it. It is never needed
after the reduced numerator and denominator are computed:

-==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==-
/*
 * Copyright (C) 2007-2008 Artima, Inc. All rights reserved.
 */
class Rational(n: Int, d: Int) {

 require(d != 0)

 private val g = gcd(n.abs, d.abs)
 val numer = n / g
 val denom = d / g

 // ...

 private def gcd(a: Int, b: Int): Int =
   if (b == 0) a else gcd(b, a % b)
}
-==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==-


When I compile this and dump it with javap, I find that the private val
g is part of the class:

-==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==-
% javap -private !-2:$
javap -private Rational
Compiled from "Rational.scala"
public class Rational extends java.lang.Object implements
scala.ScalaObject{
   private final int denom;
   private final int numer;
   private final int g;
   ...
   private int g();
   ...
}
-==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==-


Now if Rational is going to be used in a way that produces large
populations, this wasted field is undesirable. So the question is: How
does one avoid it? I puzzled over this for quite a while and couldn't
figure out how, but I'm a Scala neophyte, at best, so this failure may
just reflect the insufficiency of my knowledge.

What's the answer? Is the only way out calling gcd(...) twice?


Randall Schulz


DRMacIver
Joined: 2008-09-02,
User offline. Last seen 42 years 45 weeks ago.
Re: Temporary Values in Constructors Retained As Fields

2009/3/15 Randall R Schulz :
> Now if Rational is going to be used in a way that produces large
> populations, this wasted field is undesirable. So the question is: How
> does one avoid it? I puzzled over this for quite a while and couldn't
> figure out how, but I'm a Scala neophyte, at best, so this failure may
> just reflect the insufficiency of my knowledge.
>
> What's the answer? Is the only way out calling gcd(...) twice?

private[this] val stuff = ...

private variables can in theory escape the scope of the current object
as they're accessible to other instances of the same class. Therefore
they get made fields (in principle you should be able to tell if this
is neccessary and the compiler should be able to avoid it. But this
doesn't happen). private[this] ones are guaranteed to never escape so
will be made fields only if it's needed.

Jorge Ortiz
Joined: 2008-12-16,
User offline. Last seen 29 weeks 4 days ago.
Re: Temporary Values in Constructors Retained As Fields
I tried that. Reality does not agree with you:

$ cat Rational.scala
class Rational(n: Int, d: Int) {
  require(d != 0)

  private[this] val g = gcd(n.abs, d.abs)
  val numer = n / g
  val denom = d / g

  private def gcd(a: Int, b: Int): Int =
   if (b == 0) a else gcd(b, a % b)
}
$ scalac Rational.scala
$ javap -private Rational
Compiled from "Rational.scala"
public class Rational extends java.lang.Object implements scala.ScalaObject{
    private final int denom;
    private final int numer;
    private final int g;
    public Rational(int, int);
    private int gcd(int, int);
    public int denom();
    public int numer();
    public int $tag()       throws java.rmi.RemoteException;
}

--j

On Sun, Mar 15, 2009 at 12:03 PM, David MacIver <david [dot] maciver [at] gmail [dot] com> wrote:
2009/3/15 Randall R Schulz <rschulz [at] sonic [dot] net>:
> Now if Rational is going to be used in a way that produces large
> populations, this wasted field is undesirable. So the question is: How
> does one avoid it? I puzzled over this for quite a while and couldn't
> figure out how, but I'm a Scala neophyte, at best, so this failure may
> just reflect the insufficiency of my knowledge.
>
> What's the answer? Is the only way out calling gcd(...) twice?

private[this] val stuff = ...

private variables can in theory escape the scope of the current object
as they're accessible to other instances of the same class. Therefore
they get made fields (in principle you should be able to tell if this
is neccessary and the compiler should be able to avoid it. But this
doesn't happen). private[this] ones are guaranteed to never escape so
will be made fields only if it's needed.

ounos
Joined: 2008-12-29,
User offline. Last seen 3 years 44 weeks ago.
Re: Temporary Values in Constructors Retained As Fields

You mean that there is no way to use local (temporary) vars in primary
constructors?? This sounds bad.

O/H Jorge Ortiz έγραψε:
> Neverming, it seems that doesn't actually help.
>
> Stick with factories and assert gcd == 1.
>
> --j
>
> On Sun, Mar 15, 2009 at 11:38 AM, Jorge Ortiz > wrote:
>
> Try:
>
> val (numer, denom) = {
>
> val g = gcd(n.abs, d.abs)
> (n / g, d / g)
> }
>
> --j
>
>
> On Sun, Mar 15, 2009 at 10:23 AM, Randall R Schulz
> > wrote:
>
> Hi,
>
> I'm working through Programming in Scala and when I came to
> the Rational
> number example, I found myself wondering about the "private val g"
> defined to hold the result of the greatest common divisor
> calculation
> used to reduce the arguments to the normal form. This value is
> used
> twice, so is computed once and stored in a private val so both the
> numerator and the denominator can be divided by it. It is
> never needed
> after the reduced numerator and denominator are computed:
>
> -==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==-
> /*
> * Copyright (C) 2007-2008 Artima, Inc. All rights reserved.
> */
> class Rational(n: Int, d: Int) {
>
> require(d != 0)
>
> private val g = gcd(n.abs, d.abs)
> val numer = n / g
> val denom = d / g
>
> // ...
>
> private def gcd(a: Int, b: Int): Int =
> if (b == 0) a else gcd(b, a % b)
> }
> -==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==-
>
>
> When I compile this and dump it with javap, I find that the
> private val
> g is part of the class:
>
> -==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==-
> % javap -private !-2:$
> javap -private Rational
> Compiled from "Rational.scala"
> public class Rational extends java.lang.Object implements
> scala.ScalaObject{
> private final int denom;
> private final int numer;
> private final int g;
> ...
> private int g();
> ...
> }
> -==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==-
>
>
> Now if Rational is going to be used in a way that produces large
> populations, this wasted field is undesirable. So the question
> is: How
> does one avoid it? I puzzled over this for quite a while and
> couldn't
> figure out how, but I'm a Scala neophyte, at best, so this
> failure may
> just reflect the insufficiency of my knowledge.
>
> What's the answer? Is the only way out calling gcd(...) twice?
>
>
> Randall Schulz
>
>
>

Randall R Schulz
Joined: 2008-12-16,
User offline. Last seen 1 year 29 weeks ago.
Re: Temporary Values in Constructors Retained As Fields

On Sunday March 15 2009, Jan Lohre wrote:
> The following should work:
>
> val (numer,denom) = {
> val g = gcd(n.abs, d.abs)
> (n/g,d/g)
> }
>
> I did not validate it though.
>
> Kind regards,
> Jan

Keeping in mind that the point of the exercise is to minimize
unnecessary storage within the representation, this is not an
improvement, since now you're dragging a Tuple around!

To wit:

-==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==-
class Rational(n: Int, d: Int) {

require(d != 0)

val (numerator, denomator) = {
val g = gcd(n.abs, d.abs)
(n/g,d/g)
}

private def gcd(a: Int, b: Int): Int =
if (b == 0) a else gcd(b, a % b)
}
-==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==-

% javap -private Rational
Compiled from "Rational.scala"
public class Rational extends java.lang.Object implements
scala.ScalaObject{
private final int denomator;
private final int numerator;
private final scala.Tuple2 x$1;
public Rational(int, int);
private int gcd(int, int);
public int denomator();
public int numerator();
public int $tag() throws java.rmi.RemoteException;
}

Definite step backwards!

Randall Schulz

Randall R Schulz
Joined: 2008-12-16,
User offline. Last seen 1 year 29 weeks ago.
Re: Temporary Values in Constructors Retained As Fields

On Sunday March 15 2009, David MacIver wrote:
> 2009/3/15 Randall R Schulz :
> > ...
> >
> > What's the answer? Is the only way out calling gcd(...) twice?
>
> private[this] val stuff = ...
>
> private variables can in theory escape the scope of the current
> object as they're accessible to other instances of the same class.
> Therefore they get made fields (in principle you should be able to
> tell if this is neccessary and the compiler should be able to avoid
> it. But this doesn't happen). private[this] ones are guaranteed to
> never escape so will be made fields only if it's needed.

That doesn't change the generated code, other than in the absence of the
private method for accessing the val g:

-==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==-
class Rational(n: Int, d: Int) {

require(d != 0)

private[this] val g = gcd(n.abs, d.abs)
val numer = n / g
val denom = d / g

private def gcd(a: Int, b: Int): Int =
if (b == 0) a else gcd(b, a % b)
}
-==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==-

-==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==-
% javap -private Rational
Compiled from "Rational.scala"
public class Rational extends java.lang.Object implements
scala.ScalaObject{
private final int denom;
private final int numer;
private final int g;
public Rational(int, int);
private int gcd(int, int);
public int denom();
public int numer();
public int $tag() throws java.rmi.RemoteException;
}
-==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==-

Randall Schulz

extempore
Joined: 2008-12-17,
User offline. Last seen 35 weeks 3 days ago.
Re: Temporary Values in Constructors Retained As Fields

On Sun, Mar 15, 2009 at 12:13:01PM -0700, Randall R Schulz wrote:
> Keeping in mind that the point of the exercise is to minimize
> unnecessary storage within the representation

No problem.

class Rational(n: Int, d: Int) {
require(d != 0)

val numerator = n/ gcd(n.abs, d.abs)
val denominator = numerator * d / n

private def gcd(a: Int, b: Int): Int =
if (b == 0) a else gcd(b, a % b)
}

(Is there a top prize for missing the point?) Until now I hadn't
realized the multiple assignment approach drags in a permanent tuple. I
would agree this seems like a real problem, but as already pointed out
factory methods are the way for now.

ijuma
Joined: 2008-08-20,
User offline. Last seen 22 weeks 2 days ago.
Re: Temporary Values in Constructors Retained As Fields

On Sun, 2009-03-15 at 12:36 -0700, Paul Phillips wrote:
> (Is there a top prize for missing the point?) Until now I hadn't
> realized the multiple assignment approach drags in a permanent tuple. I
> would agree this seems like a real problem, but as already pointed out
> factory methods are the way for now.

Since everyone seemed to be surprised by this in the #scala IRC channel
and there doesn't seem to be a good reason for it, shall a bug be filed?

Ismael

odersky
Joined: 2008-07-29,
User offline. Last seen 45 weeks 6 days ago.
Re: Temporary Values in Constructors Retained As Fields

There should be an optimization that turns private[this] variables
that are only used for class initialization into constructor-local
variables. It would be relatively easy to do that, I think. But it has
not yet been done.

Cheers

DRMacIver
Joined: 2008-09-02,
User offline. Last seen 42 years 45 weeks ago.
Re: Temporary Values in Constructors Retained As Fields

2009/3/15 martin odersky :
> There should be an optimization that turns private[this] variables
> that are only used for class initialization into constructor-local
> variables. It would be relatively easy to do that, I think. But it has
> not yet been done.

I was really sure that I'd seen such behaviour in the past, but I
can't seem to find any evidence of it. It's very strange.

Russ P.
Joined: 2009-01-31,
User offline. Last seen 1 year 26 weeks ago.
Re: Temporary Values in Constructors Retained As Fields
Maybe another private function could be used to compute the numerator and denominator, or maybe even a nested function. I didn't try it though.

However, it does seem that you should be able to get what you want without having to call another function. Maybe you could use a nested block, but then I think you may need to make numer and denom vars rather than vals (so they can be declared before the block and changed in the block).

--Russ

DRMacIver
Joined: 2008-09-02,
User offline. Last seen 42 years 45 weeks ago.
Re: Temporary Values in Constructors Retained As Fields

2009/3/15 Jorge Ortiz :
> I tried that. Reality does not agree with you:

Weird. Reality sure used to agree with me on that one. That probably
constitutes a bug.

Randall R Schulz
Joined: 2008-12-16,
User offline. Last seen 1 year 29 weeks ago.
Re: Temporary Values in Constructors Retained As Fields

On Sunday March 15 2009, Jorge Ortiz wrote:
> Nevermind, it seems that doesn't actually help.
>
> Stick with factories and assert gcd == 1.
>
> --j

Just to clarify, the idea is to do all the computation requiring
intermediate values in a factory and simply require that the numerator
and denominator are in reduced form in the primary constructor? Of
course, in this case, that would imply two calls to gcd(...), which
would leave you in the same situation as simply calling gcd twice in
the primary constructor (in this particular example). Naturally that's
an incidental matter that would manifest itself differently in
different classes, but any counterpart of gcd that was computationally
very expensive would still pose a problem, if it had to be verified in
the primary constructor. Can primary constuctors be made private?

Alternately could auxiliary constructors work for this, or do vals
defined within auxiliary constructors also contribute state to the
resulting class?

Randall Schulz

Randall R Schulz
Joined: 2008-12-16,
User offline. Last seen 1 year 29 weeks ago.
Re: Temporary Values in Constructors Retained As Fields

On Sunday March 15 2009, Randall R Schulz wrote:
> Hi,
>
> I'm working through Programming in Scala and when I came to the
> Rational number example, I found myself wondering about the "private
> val g" defined to hold the result of the greatest common divisor
> calculation used to reduce the arguments to the normal form. ...
>
> ... I find that the private val g is part of the class ...
>
> What's the answer? Is the only way out calling gcd(...) twice?

Thanks for the analysis and ideas, folks. I learned some new stuff and I
think I know how to handle this general issue now.

Randall Schulz

Jorge Ortiz
Joined: 2008-12-16,
User offline. Last seen 29 weeks 4 days ago.
Re: Temporary Values in Constructors Retained As Fields
Randall,

I'd do something like:

  package foo

  class Rational private[foo](val numer: Int, val denom: Int) {
    require(denom != 0)
  }

  object Rational {
    def apply(n: Int, d: Int): Rational = {
      val g = gcd(n.abs, d.abs)
      new Rational(n / g, d / g)
    }

    def gcd(a: Int, b: Int): Int =
     if (b == 0) a else gcd(b, a % b)
  }

--j

On Sun, Mar 15, 2009 at 12:15 PM, Randall R Schulz <rschulz [at] sonic [dot] net> wrote:
On Sunday March 15 2009, David MacIver wrote:
> 2009/3/15 Randall R Schulz <rschulz [at] sonic [dot] net>:
> > ...
> >
> > What's the answer? Is the only way out calling gcd(...) twice?
>
> private[this] val stuff = ...
>
> private variables can in theory escape the scope of the current
> object as they're accessible to other instances of the same class.
> Therefore they get made fields (in principle you should be able to
> tell if this is neccessary and the compiler should be able to avoid
> it. But this doesn't happen). private[this] ones are guaranteed to
> never escape so will be made fields only if it's needed.


That doesn't change the generated code, other than in the absence of the
private method for accessing the val g:

-==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==-
class Rational(n: Int, d: Int) {

 require(d != 0)

 private[this] val g = gcd(n.abs, d.abs)
 val numer = n / g
 val denom = d / g

 private def gcd(a: Int, b: Int): Int =
   if (b == 0) a else gcd(b, a % b)
}
-==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==-


-==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==-
% javap -private Rational
Compiled from "Rational.scala"
public class Rational extends java.lang.Object implements
scala.ScalaObject{
   private final int denom;
   private final int numer;
   private final int g;
   public Rational(int, int);
   private int gcd(int, int);
   public int denom();
   public int numer();
   public int $tag()       throws java.rmi.RemoteException;
}
-==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==-


Randall Schulz

ijuma
Joined: 2008-08-20,
User offline. Last seen 22 weeks 2 days ago.
Re: Temporary Values in Constructors Retained As Fields

On Sun, 2009-03-15 at 11:42 -0700, Jorge Ortiz wrote:
> Neverming, it seems that doesn't actually help.

For the ones who haven't checked, the reason why it doesn't help is that
the tuple returned from the block is stored as a synthetic field. It
does help in cases where one needs to use some locals in the process of
computing a single result. e.g.:

val numer = {
val g = gcd(n.abs, d.abs)
n / g
}

In this case, g is not stored as a field.

Ismael

> Stick with factories and assert gcd == 1.
>
> --j
>
> On Sun, Mar 15, 2009 at 11:38 AM, Jorge Ortiz
> wrote:
> Try:
>
> val (numer, denom) = {
>
> val g = gcd(n.abs, d.abs)
>
> (n / g, d / g)
> }
>
> --j

Randall R Schulz
Joined: 2008-12-16,
User offline. Last seen 1 year 29 weeks ago.
Re: Temporary Values in Constructors Retained As Fields

On Sunday March 15 2009, Ismael Juma wrote:
> On Sun, 2009-03-15 at 12:36 -0700, Paul Phillips wrote:
> > (Is there a top prize for missing the point?) Until now I hadn't
> > realized the multiple assignment approach drags in a permanent
> > tuple. I would agree this seems like a real problem, but as
> > already pointed out factory methods are the way for now.
>
> Since everyone seemed to be surprised by this in the #scala IRC
> channel and there doesn't seem to be a good reason for it, shall a
> bug be filed?

If that's the consensus, I'll file the bug report.

Bugs are reported via ?

By the way, Firefox tells me that this site has an "invalid" security
certificate when I visited that URL.

> Ismael

Randall Schulz

extempore
Joined: 2008-12-17,
User offline. Last seen 35 weeks 3 days ago.
Re: Temporary Values in Constructors Retained As Fields

Aha, I thought it could be done. This is probably what DRMacIver was
remembering working, it just has to be a constructor parameter.

// the sickest local variable ever!
class Rational(n: Int, d: Int, private[this] var g: Int) {
def this(n: Int, d: Int) = this(n, d, -1)

g = gcd(n.abs, d.abs)
val numer = n / g
val denom = d / g

private def gcd(a: Int, b: Int): Int =
if (b == 0) a else gcd(b, a % b)
}

$ javap -private Rational
Compiled from "a.scala"
public class Rational extends java.lang.Object implements scala.ScalaObject{
private final int denom;
private final int numer;
public Rational(int, int, int);
private int gcd(int, int);
public int denom();
public int numer();
public Rational(int, int);
}

odersky
Joined: 2008-07-29,
User offline. Last seen 45 weeks 6 days ago.
Re: Temporary Values in Constructors Retained As Fields

On Sun, Mar 15, 2009 at 11:14 PM, Randall R Schulz wrote:
> On Sunday March 15 2009, Ismael Juma wrote:
>> On Sun, 2009-03-15 at 12:36 -0700, Paul Phillips wrote:
>> > (Is there a top prize for missing the point?) Until now I hadn't
>> > realized the multiple assignment approach drags in a permanent
>> > tuple.  I would agree this seems like a real problem, but as
>> > already pointed out factory methods are the way for now.
>>
>> Since everyone seemed to be surprised by this in the #scala IRC
>> channel and there doesn't seem to be a good reason for it, shall a
>> bug be filed?
>
> If that's the consensus, I'll file the bug report.
>
> Bugs are reported via ?
>
> By the way, Firefox tells me that this site has an "invalid" security
> certificate when I visited that URL.
>
>
>> Ismael
>
>
> Randall Schulz
>
Please don't file a bug without proposing an alternative. The spec
explains how tuple assignemnts are expanded. If you know a better way,
please suggest a patch.

Thanks

extempore
Joined: 2008-12-17,
User offline. Last seen 35 weeks 3 days ago.
Re: Temporary Values in Constructors Retained As Fields

On Sun, Mar 15, 2009 at 10:23:31AM -0700, Randall R Schulz wrote:
> I'm working through Programming in Scala and when I came to the
> Rational number example, I found myself wondering about the "private
> val g" defined to hold the result of the greatest common divisor
> calculation used to reduce the arguments to the normal form. This
> value is used twice, so is computed once and stored in a private val
> so both the numerator and the denominator can be divided by it. It is
> never needed after the reduced numerator and denominator are computed:

[See http://tinyurl.com/cwgadg for the whole thread.]

I totally forgot while that conversation was taking place that there is
a way to do this, if you can handle a little perversion.

class Rational private (x: Tuple2[Int, Int]) {
val numer = x._1
val denom = x._2

def this(n: Int, d: Int) = this( {
def gcd(a: Int, b: Int): Int = if (b == 0) a else gcd(b, a % b)
val g = gcd(n.abs, d.abs)

(n / g, d / g)
} )
}

See, scala requires the first call in an auxiliary constructor to be to
another constructor, but it can't leave without the parameters and
nobody said we couldn't take our time creating those.

% jp Rational
// ...
private final int denom;
private final int numer;
// and that's it.

Now let's fire her up:
scala> new Rational(5,10)
java.lang.VerifyError: (class: Rational, method: signature: (II)V) Expecting to find object/array on stack
at .(:5)
at .()
at RequestResult$.(:4)
at RequestResult$.()
at RequestResult$result()
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invo...

Oops, a bug - looks like it's from defining a function inside a not yet
constructed object. We can work around it though:

object Rational {
def gcd(a: Int, b: Int): Int = if (b == 0) a else gcd(b, a % b)
}
class Rational private (x: Tuple2[Int, Int]) {
val numer = x._1
val denom = x._2

def this(n: Int, d: Int) = this( {
val g = Rational.gcd(n.abs, d.abs)
(n / g, d / g)
} )
}

scala> new Rational(50,125)
res0: Rational = Rational@94f2e8
scala> println(res0.numer + " / " + res0.denom)
2 / 5

It's a bit less thrilling that way since we've had to bring in outside
help, but still.

Mark Thornton
Joined: 2009-04-19,
User offline. Last seen 42 years 45 weeks ago.
Re: Temporary Values in Constructors Retained As Fields

Paul Phillips wrote:
> On Sun, Mar 15, 2009 at 10:23:31AM -0700, Randall R Schulz wrote:
>
>> I'm working through Programming in Scala and when I came to the
>> Rational number example, I found myself wondering about the "private
>> val g" defined to hold the result of the greatest common divisor
>> calculation used to reduce the arguments to the normal form. This
>> value is used twice, so is computed once and stored in a private val
>> so both the numerator and the denominator can be divided by it. It is
>> never needed after the reduced numerator and denominator are computed:
>>
> object Rational {
> def gcd(a: Int, b: Int): Int = if (b == 0) a else gcd(b, a % b)
> }
> class Rational private (x: Tuple2[Int, Int]) {
> val numer = x._1
> val denom = x._2
>
> def this(n: Int, d: Int) = this( {
> val g = Rational.gcd(n.abs, d.abs)
> (n / g, d / g)
> } )
> }
>
object Rational {
def gcd(a: Int, b: Int): Int = if (b == 0) a else gcd(b, a % b)
def apply(n: Int, d: Int) = {
val g = gcd(n.abs, g.abs)
new Rational(n/g, d/g)
}
}

class Rational private (val numer: Int, val denom:Int) {}

Is much simpler

Regards,
Mark Thornton

extempore
Joined: 2008-12-17,
User offline. Last seen 35 weeks 3 days ago.
Re: Temporary Values in Constructors Retained As Fields

On Tue, Apr 21, 2009 at 08:57:08PM +0100, Mark Thornton wrote:
> class Rational private (val numer: Int, val denom:Int) {}
>
> Is much simpler

It is! It also fails to solve the problem. What are you going to do
when you want to mix in a trait?

extempore
Joined: 2008-12-17,
User offline. Last seen 35 weeks 3 days ago.
Re: Temporary Values in Constructors Retained As Fields

Incidentally if you take a look at the thread I linked to, you may
notice that many people (including me) mentioned factories. I didn't
bring it back up again for nothing.

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