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

Constructor argument gets compiled into a field (should it?)

5 replies
Alexey Goncharuk
Joined: 2012-02-20,
User offline. Last seen 42 years 45 weeks ago.

Hello everybody, I'm quite new to scala and recently faced behavior
which I cannot explain.

Consider the following examlpe:

object Convert {
def asHumanStringFunc(f: => Boolean): String = {
if (f) "on" else "off"
}

def asHumanString(f: Boolean): String = {
asHumanStringFunc(f)
}
}

class B {
var someProperty: Boolean = false
}

trait A {
def prop: String
}

class AImpl(b: B) {
// Here is the point
val prop = Convert.asHumanStringFunc(b.someProperty)
}

object Test{
def main(args:Array[String]){
val b: B = new B

val a = new AImpl(b)

println(a.prop)

b.someProperty = true

println(a.prop)

println(Arrays.toString(a.getClass.getDeclaredFields.asInstanceOf[Array[Object]]))
}
}

In AImpl constructor field prop is initialized with some value that
does not depend on any external changes. Since constructor argument b
is not used in any function, I expected that it would be excluded from
the object fields, however, it becomes the public field of AImpl. If I
change asHumanStringFunc to asHumanString in AImpl constructor, this
field disappears. Can anyone explain why does scala embed constructor
argument to the object field list in this case?

Thanks in advance.

Guillaume Yziquel 2
Joined: 2011-12-13,
User offline. Last seen 42 years 45 weeks ago.
Re: Constructor argument gets compiled into a field (should it?

Le Monday 20 Feb 2012 à 05:43:54 (-0800), Alexey Goncharuk a écrit :
>
> object Convert {
> def asHumanStringFunc(f: => Boolean): String = {
> if (f) "on" else "off"
> }
>
> def asHumanString(f: Boolean): String = {
> asHumanStringFunc(f)
> }
> }
>
> class B {
> var someProperty: Boolean = false
> }
>
> class AImpl(b: B) {
> // Here is the point
> val prop = Convert.asHumanStringFunc(b.someProperty)
> }
>
> In AImpl constructor field prop is initialized with some value that
> does not depend on any external changes. Since constructor argument b
> is not used in any function, I expected that it would be excluded from
> the object fields, however, it becomes the public field of AImpl. If I
> change asHumanStringFunc to asHumanString in AImpl constructor, this
> field disappears. Can anyone explain why does scala embed constructor
> argument to the object field list in this case?
>
> Thanks in advance.

Using f: => Boolean instead of f: Boolean likely implies operational
semantics which requires the argument to be managed by the GC. The
workings of the JVM GC likely require the value to be stored in some
field somewhere.

This is just a wild guess, though.

Guillaume Yziquel 2
Joined: 2011-12-13,
User offline. Last seen 42 years 45 weeks ago.
Re: Constructor argument gets compiled into a field (should it?

Le Monday 20 Feb 2012 à 17:50:26 (+0100), Guillaume Yziquel a écrit :
> Le Monday 20 Feb 2012 à 05:43:54 (-0800), Alexey Goncharuk a écrit :
> >
> > In AImpl constructor field prop is initialized with some value that
> > does not depend on any external changes. Since constructor argument b
> > is not used in any function, I expected that it would be excluded from
> > the object fields, however, it becomes the public field of AImpl. If I
> > change asHumanStringFunc to asHumanString in AImpl constructor, this
> > field disappears. Can anyone explain why does scala embed constructor
> > argument to the object field list in this case?
> >
> > Thanks in advance.
>
> Using f: => Boolean instead of f: Boolean likely implies operational
> semantics which requires the argument to be managed by the GC. The
> workings of the JVM GC likely require the value to be stored in some
> field somewhere.
>
> This is just a wild guess, though.

You should perhaps try replacing

> > val prop = Convert.asHumanStringFunc(b.someProperty)

by the following

val prop = {
val bb = b
Convert.asHumanStringFunc(bb.someProperty)
}

and check whether or not the field is still present. With a little luck,
it should disappear.

As to the public qualifier, I suspect that it's because you haven't
specified b as being private[this] that the field ends up being public
as soon as his presence is deemed necessary for runtime reasons (i.e.
lazyness of f: => Boolean). After all, public is the default.

Bernd Johannes
Joined: 2011-01-28,
User offline. Last seen 42 years 45 weeks ago.
Re: Constructor argument gets compiled into a field (should it?
p, li { white-space: pre-wrap; }

Am Montag, 20. Februar 2012, 14:43:54 schrieb Alexey Goncharuk:


Hello Alexey - I am not sure what you want to achieve exactly - maybe something like this:


Welcome to Scala version 2.9.0.1 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_29).

Type in expressions to have them evaluated.

Type :help for more information.


scala> :paste

// Entering paste mode (ctrl-D to finish)


object Test {

def render(source: =>Boolean): String = source.toString

def test() {

var bool = false

val renderme = () => render(bool)


println(renderme())

bool = true

println(renderme())


}

}


// Exiting paste mode, now interpreting.


defined module Test


scala> Test.test

false

true


I guess you expect "val prop" to be a function object which it isn't. It gets evaluated at once and contains an immutable String value.


Greetings

Bernd


> Hello everybody, I'm quite new to scala and recently faced behavior

> which I cannot explain.

>

> Consider the following examlpe:

>

> object Convert {

> def asHumanStringFunc(f: => Boolean): String = {

> if (f) "on" else "off"

> }

>

> def asHumanString(f: Boolean): String = {

> asHumanStringFunc(f)

> }

> }

>

> class B {

> var someProperty: Boolean = false

> }

>

> trait A {

> def prop: String

> }

>

> class AImpl(b: B) {

> // Here is the point

> val prop = Convert.asHumanStringFunc(b.someProperty)

> }

>

> object Test{

> def main(args:Array[String]){

> val b: B = new B

>

> val a = new AImpl(b)

>

> println(a.prop)

>

> b.someProperty = true

>

> println(a.prop)

>

>

> println(Arrays.toString(a.getClass.getDeclaredFields.asInstanceOf[Array[Obj

> ect]])) }

> }

>

> In AImpl constructor field prop is initialized with some value that

> does not depend on any external changes. Since constructor argument b

> is not used in any function, I expected that it would be excluded from

> the object fields, however, it becomes the public field of AImpl. If I

> change asHumanStringFunc to asHumanString in AImpl constructor, this

> field disappears. Can anyone explain why does scala embed constructor

> argument to the object field list in this case?

>

> Thanks in advance.



ichoran
Joined: 2009-08-14,
User offline. Last seen 2 years 3 weeks ago.
Re: Constructor argument gets compiled into a field (should it?
This is as it should (must?) be, but it's a little tricky to work out why.

First, look at

  f: => Boolean

Although this is called a "by-name parameter", it is actually implemented as a Function0,

  f: () => Boolean

just with different syntax used on both ends.  Now, this function has to be able to have access to

  b.someProperty

and since it evaluates lazily (that's the whole point of by-name calling!) it needs to get access to b to do it.  That is, the function is going to look like

  new Function0[Boolean] {
    def apply() = b.someProperty
  }

But, wait...how does it get b?  What I've written clearly isn't going to work since b isn't in scope.  You really need something like one of

  class ByName(b: B) extends Function0[Boolean] { ... }
  class ByName(a: AImpl) extends Function0[Boolean] { ... }

where the latter is preferred if b might change (since then it would delay being evaluated until it was actually used, which is again the whole point of by-name parameters).  So it does in fact do it the latter way.

But now we have a problem: even though b only exists as a constructor argument of AImpl, this new class needs access to it, and it needs access as late as possible.  So we have to store that argument as a field in AImpl, and, since it's an entirely separate class, make that field public.

And that's exactly what the compiler does behind the scenes.

  --Rex


On Mon, Feb 20, 2012 at 8:43 AM, Alexey Goncharuk <agoncharuk [at] gridgain [dot] com> wrote:
Hello everybody, I'm quite new to scala and recently faced behavior
which I cannot explain.

Consider the following examlpe:

object Convert {
   def asHumanStringFunc(f: => Boolean): String = {
       if (f) "on" else "off"
   }

   def asHumanString(f: Boolean): String = {
       asHumanStringFunc(f)
   }
}

class B {
   var someProperty: Boolean = false
}

trait A {
   def prop: String
}

class AImpl(b: B) {
   // Here is the point
   val prop = Convert.asHumanStringFunc(b.someProperty)
}

object Test{
   def main(args:Array[String]){
       val b: B = new B

       val a = new AImpl(b)

       println(a.prop)

       b.someProperty = true

       println(a.prop)


println(Arrays.toString(a.getClass.getDeclaredFields.asInstanceOf[Array[Object]]))
   }
}

In AImpl constructor field prop is initialized with some value that
does not depend on any external changes. Since constructor argument b
is not used in any function, I expected that it would be excluded from
the object fields, however, it becomes the public field of AImpl. If I
change asHumanStringFunc to asHumanString in AImpl constructor, this
field disappears. Can anyone explain why does scala embed constructor
argument to the object field list in this case?

Thanks in advance.

Alexey Goncharuk
Joined: 2012-02-20,
User offline. Last seen 42 years 45 weeks ago.
Re: Constructor argument gets compiled into a field (should it?)

Rex, thank you for the detailed explanation. Now I see that compiler
does not know (and can not) that the function is evaluated only once
during constructor call. And now I see why example suggested by
Guillaume Yziquel works. This was very helpful!

On 21 фев, 01:34, Rex Kerr wrote:
> This is as it should (must?) be, but it's a little tricky to work out why.
>
> First, look at
>
>   f: => Boolean
>
> Although this is called a "by-name parameter", it is actually implemented
> as a Function0,
>
>   f: () => Boolean
>
> just with different syntax used on both ends.  Now, this function has to be
> able to have access to
>
>   b.someProperty
>
> and since it evaluates lazily (that's the whole point of by-name calling!)
> it needs to get access to b to do it.  That is, the function is going to
> look like
>
>   new Function0[Boolean] {
>     def apply() = b.someProperty
>   }
>
> But, wait...how does it get b?  What I've written clearly isn't going to
> work since b isn't in scope.  You really need something like one of
>
>   class ByName(b: B) extends Function0[Boolean] { ... }
>   class ByName(a: AImpl) extends Function0[Boolean] { ... }
>
> where the latter is preferred if b might change (since then it would delay
> being evaluated until it was actually used, which is again the whole point
> of by-name parameters).  So it does in fact do it the latter way.
>
> But now we have a problem: even though b only exists as a constructor
> argument of AImpl, this new class needs access to it, and it needs access
> as late as possible.  So we have to store that argument as a field in
> AImpl, and, since it's an entirely separate class, make that field public.
>
> And that's exactly what the compiler does behind the scenes.
>
>   --Rex
>
> On Mon, Feb 20, 2012 at 8:43 AM, Alexey Goncharuk
> wrote:
>
>
>
>
>
>
>
> > Hello everybody, I'm quite new to scala and recently faced behavior
> > which I cannot explain.
>
> > Consider the following examlpe:
>
> > object Convert {
> >    def asHumanStringFunc(f: => Boolean): String = {
> >        if (f) "on" else "off"
> >    }
>
> >    def asHumanString(f: Boolean): String = {
> >        asHumanStringFunc(f)
> >    }
> > }
>
> > class B {
> >    var someProperty: Boolean = false
> > }
>
> > trait A {
> >    def prop: String
> > }
>
> > class AImpl(b: B) {
> >    // Here is the point
> >    val prop = Convert.asHumanStringFunc(b.someProperty)
> > }
>
> > object Test{
> >    def main(args:Array[String]){
> >        val b: B = new B
>
> >        val a = new AImpl(b)
>
> >        println(a.prop)
>
> >        b.someProperty = true
>
> >        println(a.prop)
>
> > println(Arrays.toString(a.getClass.getDeclaredFields.asInstanceOf[Array[Obj ect]]))
> >    }
> > }
>
> > In AImpl constructor field prop is initialized with some value that
> > does not depend on any external changes. Since constructor argument b
> > is not used in any function, I expected that it would be excluded from
> > the object fields, however, it becomes the public field of AImpl. If I
> > change asHumanStringFunc to asHumanString in AImpl constructor, this
> > field disappears. Can anyone explain why does scala embed constructor
> > argument to the object field list in this case?
>
> > Thanks in advance.

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