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

surprised by class initialization

57 replies
Michael Schmitz
Joined: 2011-11-01,
User offline. Last seen 42 years 45 weeks ago.

Maybe Java has the same issue, but the way Scala constructors work
encourages me to put initialization code at the top of the body of
classes. Maybe this is a bad approach and I should put more
information into secondary constructors (def this = ).

scala> class Foo { val x = y + 2; val y = 3 }
defined class Foo

scala> (new Foo).x
res9: Int = 2

Whereas, when the assignment to y is first.

scala> class Bar { val y = 3; val x = y + 2 }
defined class Bar

scala> (new Bar).x
res2: Int = 5

The first example silently uses the default value for an int (0).
This is confusing to me because y is a val that takes on two different
values (0 and 3) during the initialization of the class. I remember a
similar problem when using initialization code in traits.

At the very least, should we be giving a compiler warning when
referring to a variable that has not yet been initialized?

Peace. Michael

DaveScala
Joined: 2011-03-18,
User offline. Last seen 1 year 21 weeks ago.
Re: surprised by class initialization

In java you get "illegal forward reference"

It is a bug in 2.9
see: https://issues.scala-lang.org/browse/SI-4419

On 30 nov, 18:22, Michael Schmitz wrote:
> Maybe Java has the same issue, but the way Scala constructors work
> encourages me to put initialization code at the top of the body of
> classes.  Maybe this is a bad approach and I should put more
> information into secondary constructors (def this = ).
>
> scala> class Foo { val x = y + 2; val y = 3 }
> defined class Foo
>
> scala> (new Foo).x
> res9: Int = 2
>
> Whereas, when the assignment to y is first.
>
> scala> class Bar { val y = 3; val x = y + 2 }
> defined class Bar
>
> scala> (new Bar).x
> res2: Int = 5
>
> The first example silently uses the default value for an int (0).
> This is confusing to me because y is a val that takes on two different
> values (0 and 3) during the initialization of the class.  I remember a
> similar problem when using initialization code in traits.
>
> At the very least, should we be giving a compiler warning when
> referring to a variable that has not yet been initialized?
>
> Peace.  Michael

dcsobral
Joined: 2009-04-23,
User offline. Last seen 38 weeks 5 days ago.
Re: Re: surprised by class initialization

On Wed, Nov 30, 2011 at 16:18, Dave wrote:
> In java you get "illegal forward reference"
>
> It is a bug in 2.9
> see: https://issues.scala-lang.org/browse/SI-4419

No, that is a different issue. In this case, x and y are members of a
class, so this code is valid. Java has the same problem, but the way
this code would appear in Java is less obvious. Here's an equivalent
Java class:

class Foo {
private int x;
private int y;

Foo() {
x = y + 2;
y = 3;
}
}

Java won't complain, and it has the very same bug. I think all IDEs do
complain about it, but Java initialization is much simpler than
Scala's. But the point is that people find the bug in that obvious,
while they don't find the bug in Scala version obvious. The reason is
that Scala mixes declaration and initialization *in the syntax*, but
separates them at run-time, while Java keeps them separated.

>
> On 30 nov, 18:22, Michael Schmitz wrote:
>> Maybe Java has the same issue, but the way Scala constructors work
>> encourages me to put initialization code at the top of the body of
>> classes.  Maybe this is a bad approach and I should put more
>> information into secondary constructors (def this = ).
>>
>> scala> class Foo { val x = y + 2; val y = 3 }
>> defined class Foo
>>
>> scala> (new Foo).x
>> res9: Int = 2
>>
>> Whereas, when the assignment to y is first.
>>
>> scala> class Bar { val y = 3; val x = y + 2 }
>> defined class Bar
>>
>> scala> (new Bar).x
>> res2: Int = 5
>>
>> The first example silently uses the default value for an int (0).
>> This is confusing to me because y is a val that takes on two different
>> values (0 and 3) during the initialization of the class.  I remember a
>> similar problem when using initialization code in traits.
>>
>> At the very least, should we be giving a compiler warning when
>> referring to a variable that has not yet been initialized?
>>
>> Peace.  Michael
>

dcsobral
Joined: 2009-04-23,
User offline. Last seen 38 weeks 5 days ago.
Re: surprised by class initialization

On Wed, Nov 30, 2011 at 15:22, Michael Schmitz wrote:
> Maybe Java has the same issue, but the way Scala constructors work
> encourages me to put initialization code at the top of the body of
> classes.  Maybe this is a bad approach and I should put more
> information into secondary constructors (def this = ).
>
> scala> class Foo { val x = y + 2; val y = 3 }
> defined class Foo
>
> scala> (new Foo).x
> res9: Int = 2
>
> Whereas, when the assignment to y is first.
>
> scala> class Bar { val y = 3; val x = y + 2 }
> defined class Bar
>
> scala> (new Bar).x
> res2: Int = 5
>
> The first example silently uses the default value for an int (0).
> This is confusing to me because y is a val that takes on two different
> values (0 and 3) during the initialization of the class.  I remember a
> similar problem when using initialization code in traits.
>
> At the very least, should we be giving a compiler warning when
> referring to a variable that has not yet been initialized?

This is a hard problem to solve in practice, simple as it is for your
particular example. What can be done is to throw an exception at
run-time if the initialization does not happen correctly, which can be
done with the -Xcheckinit flag.

Michael Schmitz
Joined: 2011-11-01,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: surprised by class initialization

I don't think your example is quite right.

class Foo {
private final int x;
private final int y;

Foo() {
x = y + 2;
y = 3;
}
}

> javac Foo.java
Foo.java:6: variable y might not have been initialized
x = y + 2;

Peace. Michael

On Wed, Nov 30, 2011 at 10:27 AM, Daniel Sobral wrote:
> On Wed, Nov 30, 2011 at 16:18, Dave wrote:
>> In java you get "illegal forward reference"
>>
>> It is a bug in 2.9
>> see: https://issues.scala-lang.org/browse/SI-4419
>
> No, that is a different issue. In this case, x and y are members of a
> class, so this code is valid. Java has the same problem, but the way
> this code would appear in Java is less obvious. Here's an equivalent
> Java class:
>
> class Foo {
>  private int x;
>  private int y;
>
>  Foo() {
>    x = y + 2;
>    y = 3;
>  }
> }
>
> Java won't complain, and it has the very same bug. I think all IDEs do
> complain about it, but Java initialization is much simpler than
> Scala's. But the point is that people find the bug in that obvious,
> while they don't find the bug in Scala version obvious. The reason is
> that Scala mixes declaration and initialization *in the syntax*,  but
> separates them at run-time, while Java keeps them separated.
>
>>
>> On 30 nov, 18:22, Michael Schmitz wrote:
>>> Maybe Java has the same issue, but the way Scala constructors work
>>> encourages me to put initialization code at the top of the body of
>>> classes.  Maybe this is a bad approach and I should put more
>>> information into secondary constructors (def this = ).
>>>
>>> scala> class Foo { val x = y + 2; val y = 3 }
>>> defined class Foo
>>>
>>> scala> (new Foo).x
>>> res9: Int = 2
>>>
>>> Whereas, when the assignment to y is first.
>>>
>>> scala> class Bar { val y = 3; val x = y + 2 }
>>> defined class Bar
>>>
>>> scala> (new Bar).x
>>> res2: Int = 5
>>>
>>> The first example silently uses the default value for an int (0).
>>> This is confusing to me because y is a val that takes on two different
>>> values (0 and 3) during the initialization of the class.  I remember a
>>> similar problem when using initialization code in traits.
>>>
>>> At the very least, should we be giving a compiler warning when
>>> referring to a variable that has not yet been initialized?
>>>
>>> Peace.  Michael
>>
>
>
>
> --
> Daniel C. Sobral
>
> I travel to the future all the time.
>

dcsobral
Joined: 2009-04-23,
User offline. Last seen 38 weeks 5 days ago.
Re: Re: surprised by class initialization

On Wed, Nov 30, 2011 at 16:41, Michael Schmitz wrote:
> I don't think your example is quite right.
>
> class Foo {
>  private final int x;
>  private final int y;
>
>  Foo() {
>   x = y + 2;
>   y = 3;
>  }
> }
>
>> javac Foo.java
> Foo.java:6: variable y might not have been initialized
>   x = y + 2;

dcs@ayanami:~/tmp$ cat Foo.java
class Foo {
private int x;
private int y;

Foo() {
x = y + 2;
y = 3;
}
}

dcs@ayanami:~/tmp$ javac Foo.java

Are you using Java 7, by any chance?

odersky
Joined: 2008-07-29,
User offline. Last seen 45 weeks 6 days ago.
Re: Re: surprised by class initialization


On Wed, Nov 30, 2011 at 7:51 PM, Daniel Sobral <dcsobral [at] gmail [dot] com> wrote:
On Wed, Nov 30, 2011 at 16:41, Michael Schmitz <michael [at] schmitztech [dot] com> wrote:
> I don't think your example is quite right.
>
> class Foo {
>  private final int x;
>  private final int y;
>
>  Foo() {
>   x = y + 2;
>   y = 3;
>  }
> }
>
>> javac Foo.java
> Foo.java:6: variable y might not have been initialized
>   x = y + 2;

I think you need to write
  Foo() {    x = this.y + 2    y = 3  }
That's pretty much what Scala's version translates to.
Cheers
 -- Martin 
Michael Schmitz
Joined: 2011-11-01,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: surprised by class initialization

> javac -version
javac 1.6.0_22

I just stuck in the finals. Sorry I mean to make my change clearer
before sending the email.

Peace. Michael

On Wed, Nov 30, 2011 at 10:51 AM, Daniel Sobral wrote:
> On Wed, Nov 30, 2011 at 16:41, Michael Schmitz wrote:
>> I don't think your example is quite right.
>>
>> class Foo {
>>  private final int x;
>>  private final int y;
>>
>>  Foo() {
>>   x = y + 2;
>>   y = 3;
>>  }
>> }
>>
>>> javac Foo.java
>> Foo.java:6: variable y might not have been initialized
>>   x = y + 2;
>
> dcs@ayanami:~/tmp$ cat Foo.java
> class Foo {
>  private int x;
>  private int y;
>
>  Foo() {
>    x = y + 2;
>    y = 3;
>  }
> }
>
>
> dcs@ayanami:~/tmp$ javac Foo.java
>
> Are you using Java 7, by any chance?
>
>
> --
> Daniel C. Sobral
>
> I travel to the future all the time.
>

dcsobral
Joined: 2009-04-23,
User offline. Last seen 38 weeks 5 days ago.
Re: Re: surprised by class initialization

Yeah, just noticed that now.

On Wed, Nov 30, 2011 at 17:03, Michael Schmitz wrote:
>> javac -version
> javac 1.6.0_22
>
> I just stuck in the finals.  Sorry I mean to make my change clearer
> before sending the email.
>
> Peace.  Michael
>
>
> On Wed, Nov 30, 2011 at 10:51 AM, Daniel Sobral wrote:
>> On Wed, Nov 30, 2011 at 16:41, Michael Schmitz wrote:
>>> I don't think your example is quite right.
>>>
>>> class Foo {
>>>  private final int x;
>>>  private final int y;
>>>
>>>  Foo() {
>>>   x = y + 2;
>>>   y = 3;
>>>  }
>>> }
>>>
>>>> javac Foo.java
>>> Foo.java:6: variable y might not have been initialized
>>>   x = y + 2;
>>
>> dcs@ayanami:~/tmp$ cat Foo.java
>> class Foo {
>>  private int x;
>>  private int y;
>>
>>  Foo() {
>>    x = y + 2;
>>    y = 3;
>>  }
>> }
>>
>>
>> dcs@ayanami:~/tmp$ javac Foo.java
>>
>> Are you using Java 7, by any chance?
>>
>>
>> --
>> Daniel C. Sobral
>>
>> I travel to the future all the time.
>>
>

DaveScala
Joined: 2011-03-18,
User offline. Last seen 1 year 21 weeks ago.
Re: surprised by class initialization

If I translate it to:

Init.java
=========
package init;

class Init {
class Foo { private final int x = y + 2;
private final int y = 3;
public int x() { return x; }
}
class Bar { private final int y = 3;
private final int x = y + 2;
public int x() { return x; }
}
public static void main(String[] args) {
new Init();
}

Init() {
System.out.println(new Foo().x());
System.out.println(new Bar().x());
}
}

I get:

C:\scala-2.9.1.final\examples>javac -d . Init.java
Init.java:4: illegal forward reference
class Foo { private final int x = y + 2;
^
1 error

If you out comment Foo and the call site I get:

C:\scala-2.9.1.final\examples>javac -d . Init.java

C:\scala-2.9.1.final\examples>java init.Init
5

On 30 nov, 19:27, Daniel Sobral wrote:
> On Wed, Nov 30, 2011 at 16:18, Dave wrote:
> > In java you get "illegal forward reference"
>
> > It is a bug in 2.9
> > see:https://issues.scala-lang.org/browse/SI-4419
>
> No, that is a different issue. In this case, x and y are members of a
> class, so this code is valid. Java has the same problem, but the way
> this code would appear in Java is less obvious. Here's an equivalent
> Java class:
>
> class Foo {
>   private int x;
>   private int y;
>
>   Foo() {
>     x = y + 2;
>     y = 3;
>   }
>
> }
>
> Java won't complain, and it has the very same bug. I think all IDEs do
> complain about it, but Java initialization is much simpler than
> Scala's. But the point is that people find the bug in that obvious,
> while they don't find the bug in Scala version obvious. The reason is
> that Scala mixes declaration and initialization *in the syntax*,  but
> separates them at run-time, while Java keeps them separated.
>
>
>
>
>
>
>
> > On 30 nov, 18:22, Michael Schmitz wrote:
> >> Maybe Java has the same issue, but the way Scala constructors work
> >> encourages me to put initialization code at the top of the body of
> >> classes.  Maybe this is a bad approach and I should put more
> >> information into secondary constructors (def this = ).
>
> >> scala> class Foo { val x = y + 2; val y = 3 }
> >> defined class Foo
>
> >> scala> (new Foo).x
> >> res9: Int = 2
>
> >> Whereas, when the assignment to y is first.
>
> >> scala> class Bar { val y = 3; val x = y + 2 }
> >> defined class Bar
>
> >> scala> (new Bar).x
> >> res2: Int = 5
>
> >> The first example silently uses the default value for an int (0).
> >> This is confusing to me because y is a val that takes on two different
> >> values (0 and 3) during the initialization of the class.  I remember a
> >> similar problem when using initialization code in traits.
>
> >> At the very least, should we be giving a compiler warning when
> >> referring to a variable that has not yet been initialized?
>
> >> Peace.  Michael
>
> --
> Daniel C. Sobral
>
> I travel to the future all the time.- Tekst uit oorspronkelijk bericht niet weergeven -
>
> - Tekst uit oorspronkelijk bericht weergeven -

dcsobral
Joined: 2009-04-23,
User offline. Last seen 38 weeks 5 days ago.
Re: Re: surprised by class initialization

What's the point of what happens when you do not translate it correctly?

So, Java does not support abstract overrides, it does not support
traits, it does not support early initialization, it does not support
overrides of fields. In other words: it has a simpler model to deal
with.

On Wed, Nov 30, 2011 at 18:19, Dave wrote:
> If I translate it to:
>
> Init.java
> =========
> package init;
>
> class Init {
>    class Foo { private final int x = y + 2;
>                private final int y = 3;
>                public int x() { return x; }
>              }
>    class Bar { private final int y = 3;
>                private final int x = y + 2;
>                public int x() { return x; }
>              }
>    public static void main(String[] args) {
>        new Init();
>    }
>
>    Init() {
>        System.out.println(new Foo().x());
>        System.out.println(new Bar().x());
>    }
> }
>
> I get:
>
>
> C:\scala-2.9.1.final\examples>javac -d . Init.java
> Init.java:4: illegal forward reference
>    class Foo { private final int x = y + 2;
>                                      ^
> 1 error
>
> If you out comment Foo and the call site I get:
>
> C:\scala-2.9.1.final\examples>javac -d . Init.java
>
> C:\scala-2.9.1.final\examples>java init.Init
> 5
>
>
>
> On 30 nov, 19:27, Daniel Sobral wrote:
>> On Wed, Nov 30, 2011 at 16:18, Dave wrote:
>> > In java you get "illegal forward reference"
>>
>> > It is a bug in 2.9
>> > see:https://issues.scala-lang.org/browse/SI-4419
>>
>> No, that is a different issue. In this case, x and y are members of a
>> class, so this code is valid. Java has the same problem, but the way
>> this code would appear in Java is less obvious. Here's an equivalent
>> Java class:
>>
>> class Foo {
>>   private int x;
>>   private int y;
>>
>>   Foo() {
>>     x = y + 2;
>>     y = 3;
>>   }
>>
>> }
>>
>> Java won't complain, and it has the very same bug. I think all IDEs do
>> complain about it, but Java initialization is much simpler than
>> Scala's. But the point is that people find the bug in that obvious,
>> while they don't find the bug in Scala version obvious. The reason is
>> that Scala mixes declaration and initialization *in the syntax*,  but
>> separates them at run-time, while Java keeps them separated.
>>
>>
>>
>>
>>
>>
>>
>> > On 30 nov, 18:22, Michael Schmitz wrote:
>> >> Maybe Java has the same issue, but the way Scala constructors work
>> >> encourages me to put initialization code at the top of the body of
>> >> classes.  Maybe this is a bad approach and I should put more
>> >> information into secondary constructors (def this = ).
>>
>> >> scala> class Foo { val x = y + 2; val y = 3 }
>> >> defined class Foo
>>
>> >> scala> (new Foo).x
>> >> res9: Int = 2
>>
>> >> Whereas, when the assignment to y is first.
>>
>> >> scala> class Bar { val y = 3; val x = y + 2 }
>> >> defined class Bar
>>
>> >> scala> (new Bar).x
>> >> res2: Int = 5
>>
>> >> The first example silently uses the default value for an int (0).
>> >> This is confusing to me because y is a val that takes on two different
>> >> values (0 and 3) during the initialization of the class.  I remember a
>> >> similar problem when using initialization code in traits.
>>
>> >> At the very least, should we be giving a compiler warning when
>> >> referring to a variable that has not yet been initialized?
>>
>> >> Peace.  Michael
>>
>> --
>> Daniel C. Sobral
>>
>> I travel to the future all the time.- Tekst uit oorspronkelijk bericht niet weergeven -
>>
>> - Tekst uit oorspronkelijk bericht weergeven -
>

DaveScala
Joined: 2011-03-18,
User offline. Last seen 1 year 21 weeks ago.
Re: surprised by class initialization

I know that Scala has different semantics than Java and Java different
semantics than c# and Nemerle is also different and so on.
But that is between programming languages and this addresses a
semantic difference within Scala between Scala surface code and its
underlying semantic model.

val guarantees immutability so y gets a value only once and upon
initializing.
Its semantic translation however is a mutable that is initialized with
0 upon first usage and later assigned 3.
So during semantic translation a semantic side-effect is introduced
which weakens the ability for the compiler to check for typos.

For instance:
t and y keys are next to each other on the keyboard
if you want to type
class Foo { val t = 1; val x = t + 2; val y = 3 }
and you type
class Foo { val t = 1; val x = y + 2; val y = 3 }
the compiler will not raise an error with the current translation.

This is simple but when the algorithm is more complex and the
application is bigger you won't see it.
Incorrectness (due to typos etcetera) is even more worse than runtime
errors and can be very expensive.

Compiler time errors are a big reason to program in a strong
statically typed language (next to performance).

One note to the title btw: it is instance initialization and x and y
are instance members. In java class initialization and class members
are static.

On 30 nov, 21:38, Daniel Sobral wrote:
> What's the point of what happens when you do not translate it correctly?
>
> So, Java does not support abstract overrides, it does not support
> traits, it does not support early initialization, it does not support
> overrides of fields. In other words: it has a simpler model to deal
> with.
>
>
>
>
>
> On Wed, Nov 30, 2011 at 18:19, Dave wrote:
> > If I translate it to:
>
> > Init.java
> > =========
> > package init;
>
> > class Init {
> >    class Foo { private final int x = y + 2;
> >                private final int y = 3;
> >                public int x() { return x; }
> >              }
> >    class Bar { private final int y = 3;
> >                private final int x = y + 2;
> >                public int x() { return x; }
> >              }
> >    public static void main(String[] args) {
> >        new Init();
> >    }
>
> >    Init() {
> >        System.out.println(new Foo().x());
> >        System.out.println(new Bar().x());
> >    }
> > }
>
> > I get:
>
> > C:\scala-2.9.1.final\examples>javac -d . Init.java
> > Init.java:4: illegal forward reference
> >    class Foo { private final int x = y + 2;
> >                                      ^
> > 1 error
>
> > If you out comment Foo and the call site I get:
>
> > C:\scala-2.9.1.final\examples>javac -d . Init.java
>
> > C:\scala-2.9.1.final\examples>java init.Init
> > 5
>
> > On 30 nov, 19:27, Daniel Sobral wrote:
> >> On Wed, Nov 30, 2011 at 16:18, Dave wrote:
> >> > In java you get "illegal forward reference"
>
> >> > It is a bug in 2.9
> >> > see:https://issues.scala-lang.org/browse/SI-4419
>
> >> No, that is a different issue. In this case, x and y are members of a
> >> class, so this code is valid. Java has the same problem, but the way
> >> this code would appear in Java is less obvious. Here's an equivalent
> >> Java class:
>
> >> class Foo {
> >>   private int x;
> >>   private int y;
>
> >>   Foo() {
> >>     x = y + 2;
> >>     y = 3;
> >>   }
>
> >> }
>
> >> Java won't complain, and it has the very same bug. I think all IDEs do
> >> complain about it, but Java initialization is much simpler than
> >> Scala's. But the point is that people find the bug in that obvious,
> >> while they don't find the bug in Scala version obvious. The reason is
> >> that Scala mixes declaration and initialization *in the syntax*,  but
> >> separates them at run-time, while Java keeps them separated.
>
> >> > On 30 nov, 18:22, Michael Schmitz wrote:
> >> >> Maybe Java has the same issue, but the way Scala constructors work
> >> >> encourages me to put initialization code at the top of the body of
> >> >> classes.  Maybe this is a bad approach and I should put more
> >> >> information into secondary constructors (def this = ).
>
> >> >> scala> class Foo { val x = y + 2; val y = 3 }
> >> >> defined class Foo
>
> >> >> scala> (new Foo).x
> >> >> res9: Int = 2
>
> >> >> Whereas, when the assignment to y is first.
>
> >> >> scala> class Bar { val y = 3; val x = y + 2 }
> >> >> defined class Bar
>
> >> >> scala> (new Bar).x
> >> >> res2: Int = 5
>
> >> >> The first example silently uses the default value for an int (0).
> >> >> This is confusing to me because y is a val that takes on two different
> >> >> values (0 and 3) during the initialization of the class.  I remember a
> >> >> similar problem when using initialization code in traits.
>
> >> >> At the very least, should we be giving a compiler warning when
> >> >> referring to a variable that has not yet been initialized?
>
> >> >> Peace.  Michael
>
> >> --
> >> Daniel C. Sobral
>
> >> I travel to the future all the time.- Tekst uit oorspronkelijk bericht niet weergeven -
>
> >> - Tekst uit oorspronkelijk bericht weergeven -
>
> --
> Daniel C. Sobral
>
> I travel to the future all the time.- Tekst uit oorspronkelijk bericht niet weergeven -
>
> - Tekst uit oorspronkelijk bericht weergeven -

odersky
Joined: 2008-07-29,
User offline. Last seen 45 weeks 6 days ago.
Fwd: surprised by class initialization
This has come up many times before. The fact that it does not go away means probably that we should do something about it. Maybe we should we emit an "dubious forward reference" warning in a situation like this:

  class C { val x = y + 1; val y = 0 }  ??

My previous position was that this is dangerous because then people would expect handholding even in more difficult situations, which we cannot check statically anymore. For instance:

  class C { val other = this; val x = other.y + 1; val y = 0 }   // no way we can trace this!

But on the other hand, if this is a warning as opposed to an error we might be OK. People generally expect to warnings to be less ad-hoc and exhaustive than errors.

What do people think?

Cheers

 -- Martin






---------- Forwarded message ----------
From: Michael Schmitz <michael [at] schmitztech [dot] com>
Date: Wed, Nov 30, 2011 at 6:22 PM
Subject: [scala-user] surprised by class initialization
To: scala-user <scala-user [at] googlegroups [dot] com>


Maybe Java has the same issue, but the way Scala constructors work
encourages me to put initialization code at the top of the body of
classes.  Maybe this is a bad approach and I should put more
information into secondary constructors (def this = ).

scala> class Foo { val x = y + 2; val y = 3 }
defined class Foo

scala> (new Foo).x
res9: Int = 2

Whereas, when the assignment to y is first.

scala> class Bar { val y = 3; val x = y + 2 }
defined class Bar

scala> (new Bar).x
res2: Int = 5

The first example silently uses the default value for an int (0).
This is confusing to me because y is a val that takes on two different
values (0 and 3) during the initialization of the class.  I remember a
similar problem when using initialization code in traits.

At the very least, should we be giving a compiler warning when
referring to a variable that has not yet been initialized?

Peace.  Michael



--
Martin Odersky
Prof., EPFL and Chairman, Typesafe
PSED, 1015 Lausanne, Switzerland
Tel. EPFL: +41 21 693 6863
Tel. Typesafe: +41 21 691 4967

extempore
Joined: 2008-12-17,
User offline. Last seen 35 weeks 3 days ago.
Re: Fwd: surprised by class initialization

On Fri, Dec 2, 2011 at 5:23 AM, martin odersky wrote:
> This has come up many times before. The fact that it does not go away means
> probably that we should do something about it. Maybe we should we emit an
> "dubious forward reference" warning in a situation like this:
>
>   class C { val x = y + 1; val y = 0 }  ??

I tried to implement that warning a while ago and was surprised at how
complicated it got, and I still wasn't warning where I wanted. I
definitely think it should be warned. The ticket I left open (chosen
from a cast of thousands of duplicates) is
https://issues.scala-lang.org/browse/SI-4856 .

odersky
Joined: 2008-07-29,
User offline. Last seen 45 weeks 6 days ago.
Re: Fwd: surprised by class initialization


On Fri, Dec 2, 2011 at 2:40 PM, Paul Phillips <paulp [at] improving [dot] org> wrote:
On Fri, Dec 2, 2011 at 5:23 AM, martin odersky <martin [dot] odersky [at] epfl [dot] ch> wrote:
> This has come up many times before. The fact that it does not go away means
> probably that we should do something about it. Maybe we should we emit an
> "dubious forward reference" warning in a situation like this:
>
>   class C { val x = y + 1; val y = 0 }  ??

I tried to implement that warning a while ago and was surprised at how
complicated it got, and I still wasn't warning where I wanted.  I
definitely think it should be warned.  The ticket I left open (chosen
from a cast of thousands of duplicates) is
https://issues.scala-lang.org/browse/SI-4856 .

I agree it's no simple.

Could we re-use the logic in RefChecks that disallows about forward references, apply it to template initialization sequences (with this.x instead of x as the reference idiom), and issue a warning instead of an error? The self reference in SI-4856 case 4 is tricky. We should suppress the warning for references to fields appearing under a lambda or passed to a by-name parameter.

Cheers

 -- Martin

dcsobral
Joined: 2009-04-23,
User offline. Last seen 38 weeks 5 days ago.
Re: Re: surprised by class initialization

On Thu, Dec 1, 2011 at 10:41, Dave wrote:
>
> val guarantees immutability so y gets a value only once and upon
> initializing.

Yes, but val can be overridden, so you cannot predict the point of
initialization.

> Its semantic translation however is a mutable that is initialized with
> 0 upon first usage and later assigned 3.
> So during semantic translation a semantic side-effect is introduced
> which weakens the ability for the compiler to check for typos.
>
> For instance:
> t and y keys are next to each other on the keyboard
> if you want to type
> class Foo { val t = 1; val x = t + 2; val y = 3 }
> and you type
> class Foo { val t = 1; val x = y + 2; val y = 3 }
> the compiler will not raise an error with the current translation.

scala> class Foo { val t = 1; val x = y + 2; val y = 3 }
defined class Foo

scala> class Bar extends { override val y = 1 } with Foo
defined class Bar

scala> new Bar
res0: Bar = Bar@7810a519

scala> res0.t
res1: Int = 1

scala> res0.x
res2: Int = 3

scala> res0.y
res3: Int = 1

Works as expected.

DaveScala
Joined: 2011-03-18,
User offline. Last seen 1 year 21 weeks ago.
Re: surprised by class initialization

Yes but it works because there is not really a forward reference for
y.
override val y = 1 is executed first (and then val t = 1; val x = y +
2;) and val y = 3 is not executed.
So basically the sequence is:
val y = 1; val t = 1; val x = y + 2;

On 2 dec, 15:59, Daniel Sobral wrote:
> On Thu, Dec 1, 2011 at 10:41, Dave wrote:
>
> > val guarantees immutability so y gets a value only once and upon
> > initializing.
>
> Yes, but val can be overridden, so you cannot predict the point of
> initialization.
>
> > Its semantic translation however is a mutable that is initialized with
> > 0 upon first usage and later assigned 3.
> > So during semantic translation a semantic side-effect is introduced
> > which weakens the ability for the compiler to check for typos.
>
> > For instance:
> > t and y keys are next to each other on the keyboard
> > if you want to type
> > class Foo { val t = 1; val x = t + 2; val y = 3 }
> > and you type
> > class Foo { val t = 1; val x = y + 2; val y = 3 }
> > the compiler will not raise an error with the current translation.
>
> scala> class Foo { val t = 1; val x = y + 2; val y = 3 }
> defined class Foo
>
> scala> class Bar extends { override val y = 1 } with Foo
> defined class Bar
>
> scala> new Bar
> res0: Bar = Bar@7810a519
>
> scala> res0.t
> res1: Int = 1
>
> scala> res0.x
> res2: Int = 3
>
> scala> res0.y
> res3: Int = 1
>
> Works as expected.
>
> --
> Daniel C. Sobral
>
> I travel to the future all the time.

roland.kuhn
Joined: 2011-02-21,
User offline. Last seen 35 weeks 3 days ago.
Re: surprised by class initialization
WANT! I recently spent some unnecessary edit/compile/test cycles just to find some of these bugs in the initialization of our ActorSystemImpl, which is just much too large to fit on one screen and hence invites “trivial” errors in this department. And it doesn’t need to be perfect, plus it should not be an error, unless you can prove that the compiler is smarter than your average brilliant genius programmer.

Regards,

Roland

Am Freitag, 2. Dezember 2011 14:23:24 UTC+1 schrieb martin odersky:
This has come up many times before. The fact that it does not go away means probably that we should do something about it. Maybe we should we emit an "dubious forward reference" warning in a situation like this:

  class C { val x = y + 1; val y = 0 }  ??

My previous position was that this is dangerous because then people would expect handholding even in more difficult situations, which we cannot check statically anymore. For instance:

  class C { val other = this; val x = other.y + 1; val y = 0 }   // no way we can trace this!

But on the other hand, if this is a warning as opposed to an error we might be OK. People generally expect to warnings to be less ad-hoc and exhaustive than errors.

What do people think?

Cheers

 -- Martin






---------- Forwarded message ----------
From: Michael Schmitz <mic [dot] [dot] [dot] [at] schmitztech [dot] com>
Date: Wed, Nov 30, 2011 at 6:22 PM
Subject: [scala-user] surprised by class initialization
To: scala-user <scala [dot] [dot] [dot] [at] googlegroups [dot] com>


Maybe Java has the same issue, but the way Scala constructors work
encourages me to put initialization code at the top of the body of
classes.  Maybe this is a bad approach and I should put more
information into secondary constructors (def this = ).

scala> class Foo { val x = y + 2; val y = 3 }
defined class Foo

scala> (new Foo).x
res9: Int = 2

Whereas, when the assignment to y is first.

scala> class Bar { val y = 3; val x = y + 2 }
defined class Bar

scala> (new Bar).x
res2: Int = 5

The first example silently uses the default value for an int (0).
This is confusing to me because y is a val that takes on two different
values (0 and 3) during the initialization of the class.  I remember a
similar problem when using initialization code in traits.

At the very least, should we be giving a compiler warning when
referring to a variable that has not yet been initialized?

Peace.  Michael



--
Martin Odersky
Prof., EPFL and Chairman, Typesafe
PSED, 1015 Lausanne, Switzerland
Tel. EPFL: +41 21 693 6863
Tel. Typesafe: +41 21 691 4967

DaveScala
Joined: 2011-03-18,
User offline. Last seen 1 year 21 weeks ago.
Re: Fwd: surprised by class initialization

> My previous position was that this is dangerous because then people would
> expect handholding even in more difficult situations, which we cannot check
> statically anymore. For instance:
>
>   class C { val other = this; val x = other.y + 1; val y = 0 }   // no way
> we can trace this!
>

I would like to see that it is solved for once and for all (basically
it is either a compile-time error or correct output) and not by
putting a plaster-strip on the wound.
If Scala wants to beat Java out of the enterprise arena then it must
be at least as good as Java otherwise it is hard to convince an it-
manager to adopt it.

In Java if I translate the above pattern (with other.y + 2 and y = 3)
this way it outputs the correct number.

Init.java
=========
package init;

class Init {
class Foo { private final Foo other = this;
private final int x = other.y() + 2;
private final int y = 3;
public int x() { return x; }
public int y() { return y; }
}
class Bar { private final Bar other = this;
private final int y = 3;
private final int x = other.y() + 2;
public int x() { return x; }
public int y() { return y; }
}
public static void main(String[] args) {
new Init();
}

Init() {
System.out.println(new Foo().x());
System.out.println(new Bar().x());
}
}

C:\scala-2.9.1.final\examples>javac -d . Init.java

C:\scala-2.9.1.final\examples>javac -d . Init.java

C:\scala-2.9.1.final\examples>java init.Init
5
5

In Scala there is a difference (because of mutables and
initialization, assignment order dependency under water):
init.scala
==========
package initscala
class Foo { val other = this; val x = other.y + 2; val y = 3 }
class Bar { val other = this; val y = 3; val x = other.y + 2 }

object Main extends App {

println((new Foo).x)
println((new Bar).x)

}

C:\scala-2.9.1.final\examples>scalac init.scala

C:\scala-2.9.1.final\examples>scala initscala.Main
2
5

dcsobral
Joined: 2009-04-23,
User offline. Last seen 38 weeks 5 days ago.
Re: Re: surprised by class initialization

On Fri, Dec 2, 2011 at 13:21, Dave wrote:
> Yes but it works because there is not really a forward reference for
> y.
> override val y = 1 is executed first (and then val t = 1; val x = y +
> 2;) and val y = 3 is not executed.
> So basically the sequence is:
> val y = 1; val t = 1; val x = y + 2;

The point is that y is accessed through open recursion, so one cannot
tell for sure whether it was initialized or not. Whether there's an
error or not depends on how it is used. While that case seemed silly
because "val y = 3" was lost, here are two variations that are not so
silly:

class Foo { val t = 1; val x = y + 2; val y = _ }
abstract class Foo { val t = 1; val x = y + 2; val y: Int }

>
> On 2 dec, 15:59, Daniel Sobral wrote:
>> On Thu, Dec 1, 2011 at 10:41, Dave wrote:
>>
>> > val guarantees immutability so y gets a value only once and upon
>> > initializing.
>>
>> Yes, but val can be overridden, so you cannot predict the point of
>> initialization.
>>
>> > Its semantic translation however is a mutable that is initialized with
>> > 0 upon first usage and later assigned 3.
>> > So during semantic translation a semantic side-effect is introduced
>> > which weakens the ability for the compiler to check for typos.
>>
>> > For instance:
>> > t and y keys are next to each other on the keyboard
>> > if you want to type
>> > class Foo { val t = 1; val x = t + 2; val y = 3 }
>> > and you type
>> > class Foo { val t = 1; val x = y + 2; val y = 3 }
>> > the compiler will not raise an error with the current translation.
>>
>> scala> class Foo { val t = 1; val x = y + 2; val y = 3 }
>> defined class Foo
>>
>> scala> class Bar extends { override val y = 1 } with Foo
>> defined class Bar
>>
>> scala> new Bar
>> res0: Bar = Bar@7810a519
>>
>> scala> res0.t
>> res1: Int = 1
>>
>> scala> res0.x
>> res2: Int = 3
>>
>> scala> res0.y
>> res3: Int = 1
>>
>> Works as expected.
>>
>> --
>> Daniel C. Sobral
>>
>> I travel to the future all the time.

odersky
Joined: 2008-07-29,
User offline. Last seen 45 weeks 6 days ago.
Re: Re: surprised by class initialization


On Fri, Dec 2, 2011 at 9:13 PM, Daniel Sobral <dcsobral [at] gmail [dot] com> wrote:
On Fri, Dec 2, 2011 at 13:21, Dave <dave [dot] mahabiersing [at] hotmail [dot] com> wrote:
> Yes but it works because there is not really a forward reference for
> y.
> override val y = 1 is executed first (and then val t = 1; val x = y +
> 2;) and val y = 3 is not executed.
> So basically the sequence is:
> val y = 1; val t = 1; val x = y + 2;

The point is that y is accessed through open recursion, so one cannot
tell for sure whether it was initialized or not. Whether there's an
error or not depends on how it is used. While that case seemed silly
because "val y = 3" was lost, here are two variations that are not so
silly:

class Foo { val t = 1; val x = y + 2; val y = _ }
abstract class Foo { val t = 1; val x = y + 2; val y: Int }

Right. That's why the best we can do is a "dubious forward reference" warning. Errors would be out of place here.
 -- Martin 
odersky
Joined: 2008-07-29,
User offline. Last seen 45 weeks 6 days ago.
Re: Re: Fwd: surprised by class initialization


On Fri, Dec 2, 2011 at 9:09 PM, Dave <dave [dot] mahabiersing [at] hotmail [dot] com> wrote:
> My previous position was that this is dangerous because then people would
> expect handholding even in more difficult situations, which we cannot check
> statically anymore. For instance:
>
>   class C { val other = this; val x = other.y + 1; val y = 0 }   // no way
> we can trace this!
>


I would like to see that it is solved for once and for all (basically
it is either a compile-time error or correct output) and not by
putting a plaster-strip on the wound.
If Scala wants to beat Java out of the enterprise arena then it must
be at least as good as Java otherwise it is hard to convince an it-
manager to adopt it.


In Java if I translate the above pattern (with other.y + 2 and y = 3)
this way it outputs the correct number.

Init.java
=========
package init;

class Init {
   class Foo { private final Foo other = this;
               private final int x = other.y() + 2;
               private final int y = 3;
               public int x() { return x; }
               public int y() { return y; }
             }
   class Bar { private final Bar other = this;
               private final int y = 3;
               private final int x = other.y() + 2;
               public int x() { return x; }
               public int y() { return y; }
             }
   public static void main(String[] args) {
       new Init();
   }

   Init() {
       System.out.println(new Foo().x());
       System.out.println(new Bar().x());
   }
}

C:\scala-2.9.1.final\examples>javac -d . Init.java

C:\scala-2.9.1.final\examples>javac -d . Init.java

C:\scala-2.9.1.final\examples>java init.Init
5
5


In Scala there is a difference (because of mutables and
initialization, assignment order dependency under water):
init.scala
==========
package initscala
class Foo { val other = this; val x = other.y + 2; val y = 3 }
class Bar { val other = this; val y = 3; val x = other.y + 2 }

object Main extends App {

  println((new Foo).x)
  println((new Bar).x)

}

C:\scala-2.9.1.final\examples>scalac init.scala

C:\scala-2.9.1.final\examples>scala initscala.Main
2
5

Having written both the javac and the scalac compiler I can pretty much vouch that their behavior wrt object initialization is exactly the same. That's why I could not believe your example, and had to try it out myself. Sure enough, I got the same result as you: Foo producdes 5 for Java, 2 for Scala. After having disassembled the bytecode, it dawned on me:
A final field int y = 3; in Java is a compile-time constant! So final means both "is immutable" and (if the right hand side is a constant), "is a constant". Try with either the final dropped or the initial value not a compile-time constant and you will get 2! 
The very same trick also works in Scala, but you have to add the final explicitly (just like you do in Java). Try with
 class Foo { val other = this; val x = other.y + 2; final val y = 3 }
and you will get 5. So, mystery solved, Java and Scala really behave the same here.
Cheers
 -- Martin 
DaveScala
Joined: 2011-03-18,
User offline. Last seen 1 year 21 weeks ago.
Re: surprised by class initialization

But that is an academic answer, but what should an it-manager in an
enterprise do with this information?

He thinks Java works this way: either a correct answer or an error
maybe not the succinct implicitly higher kinded way, but it works and
Scala we are not sure which way it works and he doesn't want that his
programmers who have different levels in programming are looking for
the needle in the haystack and spend time on how it works so he will
not adopt Scala.

On 2 dec, 21:13, Daniel Sobral wrote:
> On Fri, Dec 2, 2011 at 13:21, Dave wrote:
> > Yes but it works because there is not really a forward reference for
> > y.
> > override val y = 1 is executed first (and then val t = 1; val x = y +
> > 2;) and val y = 3 is not executed.
> > So basically the sequence is:
> > val y = 1; val t = 1; val x = y + 2;
>
> The point is that y is accessed through open recursion, so one cannot
> tell for sure whether it was initialized or not. Whether there's an
> error or not depends on how it is used. While that case seemed silly
> because "val y = 3" was lost, here are two variations that are not so
> silly:
>
> class Foo { val t = 1; val x = y + 2; val y = _ }
> abstract class Foo { val t = 1; val x = y + 2; val y: Int }
>
>
>
>
>
>
>
> > On 2 dec, 15:59, Daniel Sobral wrote:
> >> On Thu, Dec 1, 2011 at 10:41, Dave wrote:
>
> >> > val guarantees immutability so y gets a value only once and upon
> >> > initializing.
>
> >> Yes, but val can be overridden, so you cannot predict the point of
> >> initialization.
>
> >> > Its semantic translation however is a mutable that is initialized with
> >> > 0 upon first usage and later assigned 3.
> >> > So during semantic translation a semantic side-effect is introduced
> >> > which weakens the ability for the compiler to check for typos.
>
> >> > For instance:
> >> > t and y keys are next to each other on the keyboard
> >> > if you want to type
> >> > class Foo { val t = 1; val x = t + 2; val y = 3 }
> >> > and you type
> >> > class Foo { val t = 1; val x = y + 2; val y = 3 }
> >> > the compiler will not raise an error with the current translation.
>
> >> scala> class Foo { val t = 1; val x = y + 2; val y = 3 }
> >> defined class Foo
>
> >> scala> class Bar extends { override val y = 1 } with Foo
> >> defined class Bar
>
> >> scala> new Bar
> >> res0: Bar = Bar@7810a519
>
> >> scala> res0.t
> >> res1: Int = 1
>
> >> scala> res0.x
> >> res2: Int = 3
>
> >> scala> res0.y
> >> res3: Int = 1
>
> >> Works as expected.
>
> >> --
> >> Daniel C. Sobral
>
> >> I travel to the future all the time.
>
> --
> Daniel C. Sobral
>
> I travel to the future all the time.- Tekst uit oorspronkelijk bericht niet weergeven -
>
> - Tekst uit oorspronkelijk bericht weergeven -

Michael Schmitz
Joined: 2011-11-01,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: surprised by class initialization

I'll still use scala, but a warning message would be great!

On Fri, Dec 2, 2011 at 12:42 PM, Dave wrote:
> But that is an academic answer, but what should an it-manager in an
> enterprise do with this information?
>
> He thinks Java works this way: either a correct answer or an error
> maybe not the succinct implicitly higher kinded way, but it works and
> Scala we are not sure which way it works and he doesn't want that his
> programmers who have different levels in programming are looking for
> the needle in the haystack and spend time on how it works so he will
> not adopt Scala.
>
>
> On 2 dec, 21:13, Daniel Sobral wrote:
>> On Fri, Dec 2, 2011 at 13:21, Dave wrote:
>> > Yes but it works because there is not really a forward reference for
>> > y.
>> > override val y = 1 is executed first (and then val t = 1; val x = y +
>> > 2;) and val y = 3 is not executed.
>> > So basically the sequence is:
>> > val y = 1; val t = 1; val x = y + 2;
>>
>> The point is that y is accessed through open recursion, so one cannot
>> tell for sure whether it was initialized or not. Whether there's an
>> error or not depends on how it is used. While that case seemed silly
>> because "val y = 3" was lost, here are two variations that are not so
>> silly:
>>
>> class Foo { val t = 1; val x = y + 2; val y = _ }
>> abstract class Foo { val t = 1; val x = y + 2; val y: Int }
>>
>>
>>
>>
>>
>>
>>
>> > On 2 dec, 15:59, Daniel Sobral wrote:
>> >> On Thu, Dec 1, 2011 at 10:41, Dave wrote:
>>
>> >> > val guarantees immutability so y gets a value only once and upon
>> >> > initializing.
>>
>> >> Yes, but val can be overridden, so you cannot predict the point of
>> >> initialization.
>>
>> >> > Its semantic translation however is a mutable that is initialized with
>> >> > 0 upon first usage and later assigned 3.
>> >> > So during semantic translation a semantic side-effect is introduced
>> >> > which weakens the ability for the compiler to check for typos.
>>
>> >> > For instance:
>> >> > t and y keys are next to each other on the keyboard
>> >> > if you want to type
>> >> > class Foo { val t = 1; val x = t + 2; val y = 3 }
>> >> > and you type
>> >> > class Foo { val t = 1; val x = y + 2; val y = 3 }
>> >> > the compiler will not raise an error with the current translation.
>>
>> >> scala> class Foo { val t = 1; val x = y + 2; val y = 3 }
>> >> defined class Foo
>>
>> >> scala> class Bar extends { override val y = 1 } with Foo
>> >> defined class Bar
>>
>> >> scala> new Bar
>> >> res0: Bar = Bar@7810a519
>>
>> >> scala> res0.t
>> >> res1: Int = 1
>>
>> >> scala> res0.x
>> >> res2: Int = 3
>>
>> >> scala> res0.y
>> >> res3: Int = 1
>>
>> >> Works as expected.
>>
>> >> --
>> >> Daniel C. Sobral
>>
>> >> I travel to the future all the time.
>>
>> --
>> Daniel C. Sobral
>>
>> I travel to the future all the time.- Tekst uit oorspronkelijk bericht niet weergeven -
>>
>> - Tekst uit oorspronkelijk bericht weergeven -

dcsobral
Joined: 2009-04-23,
User offline. Last seen 38 weeks 5 days ago.
Re: Re: surprised by class initialization

On Fri, Dec 2, 2011 at 18:42, Dave wrote:
> But that is an academic answer, but what should an it-manager in an
> enterprise do with this information?
>
> He thinks Java works this way: either a correct answer or an error
> maybe not the succinct implicitly higher kinded way, but it works and
> Scala we are not sure which way it works and he doesn't want that his
> programmers who have different levels in programming are looking for
> the needle in the haystack and spend time on how it works so he will
> not adopt Scala.

Are you seriously claiming that an it-manager would even entertain
such low level discussion, much less base Scala adoption on its
answer? I find the very idea completely absurd.

But, anyway, here's the non-academic answer: it is NOT POSSIBLE to
correctly assert if a forward reference is used incorrect at compile
time in Scala.

If you want to avoid problems, just compile with -Xcheckinit. The
error will show up at run-time, but it should show up readily enough.

DaveScala
Joined: 2011-03-18,
User offline. Last seen 1 year 21 weeks ago.
Re: Fwd: surprised by class initialization

Yes, indeed that works.
So using an immutable val before initialization is only possible in a
save way if it is made non-overridable (thus final like in FooFinaly)
or overridable but with a pre-initialization (like in Foo3)
Warnings would be helpful though.

init.scala
==========
package initscala
class Foo { val other = this; val x = other.y + 2; val y = 0 }
class FooFinaly { val other = this; val x = other.y + 2; final val y =
3 }
class Foo2 extends Foo { override val y = 3 }
class Foo3 extends { override val y = 3 } with Foo
class Bar { val other = this; val y = 3; val x = other.y + 2 }

object Main extends App {
println((new Foo).x)
println((new FooFinaly).x)
println((new Foo2).x)
println((new Foo3).x)
println((new Bar).x)

}

C:\scala-2.9.1.final\examples>scala initscala.Main
2
5
2
5
5

On 2 dec, 21:28, martin odersky wrote:
> On Fri, Dec 2, 2011 at 9:09 PM, Dave wrote:
> > > My previous position was that this is dangerous because then people would
> > > expect handholding even in more difficult situations, which we cannot
> > check
> > > statically anymore. For instance:
>
> > >   class C { val other = this; val x = other.y + 1; val y = 0 }   // no
> > way
> > > we can trace this!
>
> > I would like to see that it is solved for once and for all (basically
> > it is either a compile-time error or correct output) and not by
> > putting a plaster-strip on the wound.
> > If Scala wants to beat Java out of the enterprise arena then it must
> > be at least as good as Java otherwise it is hard to convince an it-
> > manager to adopt it.
>
> > In Java if I translate the above pattern (with other.y + 2 and y = 3)
> > this way it outputs the correct number.
>
> > Init.java
> > =========
> > package init;
>
> > class Init {
> >     class Foo { private final Foo other = this;
> >                private final int x = other.y() + 2;
> >                 private final int y = 3;
> >                public int x() { return x; }
> >                 public int y() { return y; }
> >              }
> >    class Bar { private final Bar other = this;
> >                 private final int y = 3;
> >                 private final int x = other.y() + 2;
> >                 public int x() { return x; }
> >                public int y() { return y; }
> >              }
> >    public static void main(String[] args) {
> >        new Init();
> >    }
>
> >    Init() {
> >        System.out.println(new Foo().x());
> >        System.out.println(new Bar().x());
> >    }
> > }
>
> > C:\scala-2.9.1.final\examples>javac -d . Init.java
>
> > C:\scala-2.9.1.final\examples>javac -d . Init.java
>
> > C:\scala-2.9.1.final\examples>java init.Init
> > 5
> > 5
>
> > In Scala there is a difference (because of mutables and
> > initialization, assignment order dependency under water):
> > init.scala
> > ==========
> > package initscala
> > class Foo { val other = this; val x = other.y + 2; val y = 3 }
> > class Bar { val other = this; val y = 3; val x = other.y + 2 }
>
> > object Main extends App {
>
> >   println((new Foo).x)
> >   println((new Bar).x)
>
> > }
>
> > C:\scala-2.9.1.final\examples>scalac init.scala
>
> > C:\scala-2.9.1.final\examples>scala initscala.Main
> > 2
> > 5
>
> > Having written both the javac and the scalac compiler I can pretty much
>
> vouch that their behavior wrt object initialization is exactly the same.
> That's why I could not believe your example, and had to try it out myself.
> Sure enough, I got the same result as you: Foo producdes 5 for Java, 2 for
> Scala. After having disassembled the bytecode, it dawned on me:
>
> A final field int y = 3; in Java is a compile-time constant! So final means
> both "is immutable" and (if the right hand side is a constant), "is a
> constant". Try with either the final dropped or the initial value not a
> compile-time constant and you will get 2!
>
> The very same trick also works in Scala, but you have to add the final
> explicitly (just like you do in Java). Try with
>
>  class Foo { val other = this; val x = other.y + 2; final val y = 3 }
>
> and you will get 5. So, mystery solved, Java and Scala really behave the
> same here.
>
> Cheers
>
>  -- Martin- Tekst uit oorspronkelijk bericht niet weergeven -
>
> - Tekst uit oorspronkelijk bericht weergeven -

E. Labun
Joined: 2010-06-20,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: surprised by class initialization

On 2011-12-02 21:15, martin odersky wrote:
> On Fri, Dec 2, 2011 at 9:13 PM, Daniel Sobral > wrote:
>
> On Fri, Dec 2, 2011 at 13:21, Dave > wrote:
> > Yes but it works because there is not really a forward reference for
> > y.
> > override val y = 1 is executed first (and then val t = 1; val x = y +
> > 2;) and val y = 3 is not executed.
> > So basically the sequence is:
> > val y = 1; val t = 1; val x = y + 2;
>
> The point is that y is accessed through open recursion, so one cannot
> tell for sure whether it was initialized or not. Whether there's an
> error or not depends on how it is used. While that case seemed silly
> because "val y = 3" was lost, here are two variations that are not so
> silly:
>
> class Foo { val t = 1; val x = y + 2; val y = _ }
> abstract class Foo { val t = 1; val x = y + 2; val y: Int }
>
> Right. That's why the best we can do is a "dubious forward reference" warning.
> Errors would be out of place here.

Daniel, Martin,
I'm realy grateful to you for your patience in explaining Scala concepts.

But it's still unclear to me, why not to forbid forward references generally by generating a compile
*error*? Like it's done for sequences of expressions in methods.

Why errors "would be out of place here"?

(Perhaps, the last example should show an important usecase, but I can't comprehend, sorry.)

--
Thank you,
Eugen

nilskp
Joined: 2009-01-30,
User offline. Last seen 1 year 27 weeks ago.
Re: Re: Fwd: surprised by class initialization
On Fri, Dec 2, 2011 at 2:28 PM, martin odersky <martin [dot] odersky [at] epfl [dot] ch> wrote:
A final field int y = 3; in Java is a compile-time constant! So final means both "is immutable" and (if the right hand side is a constant), "is a constant". Try with either the final dropped or the initial value not a compile-time constant and you will get 2! 
The very same trick also works in Scala, but you have to add the final explicitly (just like you do in Java). Try with
 class Foo { val other = this; val x = other.y + 2; final val y = 3 }
and you will get 5. So, mystery solved, Java and Scala really behave the same here.

In Java, final means both immutable (on the field) and non-overridable (on the method). In Scala it only has meaning for the latter, no? Whereas val is supposed to mean immutable (the reference, not the method call). So, to me, it doesn't make sense that adding final, which should mean that I can't override the method accessor, should change the underlying field semantics.  

Cheers
 -- Martin 

odersky
Joined: 2008-07-29,
User offline. Last seen 45 weeks 6 days ago.
Re: Re: Fwd: surprised by class initialization


On Sat, Dec 3, 2011 at 2:25 AM, Nils Kilden-Pedersen <nilskp [at] gmail [dot] com> wrote:
On Fri, Dec 2, 2011 at 2:28 PM, martin odersky <martin [dot] odersky [at] epfl [dot] ch> wrote:
A final field int y = 3; in Java is a compile-time constant! So final means both "is immutable" and (if the right hand side is a constant), "is a constant". Try with either the final dropped or the initial value not a compile-time constant and you will get 2! 
The very same trick also works in Scala, but you have to add the final explicitly (just like you do in Java). Try with
 class Foo { val other = this; val x = other.y + 2; final val y = 3 }
and you will get 5. So, mystery solved, Java and Scala really behave the same here.

In Java, final means both immutable (on the field) and non-overridable (on the method). In Scala it only has meaning for the latter, no? Whereas val is supposed to mean immutable (the reference, not the method call). So, to me, it doesn't make sense that adding final, which should mean that I can't override the method accessor, should change the underlying field semantics.  
But it does! If a val can be overridden, the compiler does not know its value, so it cannot be treated as a constant! It's really Java that conflates the concepts here, not Scala.  Cheers
 -- Martin
odersky
Joined: 2008-07-29,
User offline. Last seen 45 weeks 6 days ago.
Re: Re: surprised by class initialization


On Sat, Dec 3, 2011 at 12:32 AM, Eugen Labun <labun [at] gmx [dot] net> wrote:
On 2011-12-02 21:15, martin odersky wrote:
On Fri, Dec 2, 2011 at 9:13 PM, Daniel Sobral <dcsobral [at] gmail [dot] com <mailto:dcsobral [at] gmail [dot] com>> wrote:

   On Fri, Dec 2, 2011 at 13:21, Dave <dave [dot] mahabiersing [at] hotmail [dot] com
   <mailto:dave [dot] mahabiersing [at] hotmail [dot] com (dave.mahabiersing@hotmail.com)>> wrote:
    > Yes but it works because there is not really a forward reference for
    > y.
    > override val y = 1 is executed first (and then val t = 1; val x = y +
    > 2;) and val y = 3 is not executed.
    > So basically the sequence is:
    > val y = 1; val t = 1; val x = y + 2;

   The point is that y is accessed through open recursion, so one cannot
   tell for sure whether it was initialized or not. Whether there's an
   error or not depends on how it is used. While that case seemed silly
   because "val y = 3" was lost, here are two variations that are not so
   silly:

   class Foo { val t = 1; val x = y + 2; val y = _ }
   abstract class Foo { val t = 1; val x = y + 2; val y: Int }

Right. That's why the best we can do is a "dubious forward reference" warning.
Errors would be out of place here.

Daniel, Martin,
I'm realy grateful to you for your patience in explaining Scala concepts.

But it's still unclear to me, why not to forbid forward references generally by generating a compile *error*? Like it's done for sequences of expressions in methods.

Why errors "would be out of place here"?

At the end of 
https://issues.scala-lang.org/browse/SI-4856 .
there's an example that shows that forward referencing vals can be useful.I quote:

scala> val fib: Stream[Int] = 1 #:: ((0 #:: fib zip fib) map { case (x, y) => x + y })
fib: Stream[Int] = Stream(1, ?)

scala> fib take 10 toList
res0: List[Int] = List(1, 1, 2, 3, 5, 8, 13, 21, 34, 55)

 
(Perhaps, the last example should show an important usecase, but I can't comprehend, sorry.)

Cheers
 -- Martin 
Peter Walkley
Joined: 2011-10-23,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: surprised by class initialization

In java, this is the sort of thing I would expect to be flagged by checkstyle and/or findbugs. The "IT manager" seriously is not going to care unless they are a micromanaging frustrated ex-developer !
 
With my team-leader hat on, I would want to see a comment in the code explaining why the unusual pratise was there for the benefit of the developers who come along after - but that would be the end of the matter.

DaveScala
Joined: 2011-03-18,
User offline. Last seen 1 year 21 weeks ago.
Re: surprised by class initialization

>
> there's an example that shows that forward referencing vals can be useful.
> I quote:
>
> scala> val fib: Stream[Int] = 1 #:: ((0 #:: fib zip fib) map { case (x, y)
> => x + y })
> fib: Stream[Int] = Stream(1, ?)
>
> scala> fib take 10 toList
> res0: List[Int] = List(1, 1, 2, 3, 5, 8, 13, 21, 34, 55)
>
> > (Perhaps, the last example should show an important usecase, but I can't
> > comprehend, sorry.)
>
> Cheers
>
>  -- Martin- Tekst uit oorspronkelijk bericht niet weergeven -
>
> - Tekst uit oorspronkelijk bericht weergeven -

But this is a recursive function and fib is lazy initialized (but
still initialized). The initialization is within the statement and
after that it is used.

Here:
class Foo { val t = 1; val x = y + 2; val y = _ }
there is a eager forward reference of y across the statement boundary
(the semicolon). Or am I misreading something? Are those semicolons
not really statement separators?

I see it as an important difference.
But nevertheless I understand that it is a difficult problem to solve
otherwise Paul would have fixed it long time ago.

nilskp
Joined: 2009-01-30,
User offline. Last seen 1 year 27 weeks ago.
Re: Re: surprised by class initialization
On Wed, Nov 30, 2011 at 12:59 PM, martin odersky <martin [dot] odersky [at] epfl [dot] ch> wrote:
On Wed, Nov 30, 2011 at 7:51 PM, Daniel Sobral <dcsobral [at] gmail [dot] com> wrote:
On Wed, Nov 30, 2011 at 16:41, Michael Schmitz <michael [at] schmitztech [dot] com> wrote:
> I don't think your example is quite right.
>
> class Foo {
>  private final int x;
>  private final int y;
>
>  Foo() {
>   x = y + 2;
>   y = 3;
>  }
> }
>
>> javac Foo.java
> Foo.java:6: variable y might not have been initialized
>   x = y + 2;

I think you need to write
  Foo() {    x = this.y + 2    y = 3  }
That's pretty much what Scala's version translates to.

Is there a reason why the Java compiler allows the forward reference, when prefixing with "this"? It seems the compile error, which occurs without "this", is valid, and I'm struggling to think of a use case, where the above behavior is desirable.
E. Labun
Joined: 2010-06-20,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: surprised by class initialization

On 2011-12-03 10:02, martin odersky wrote:
> At the end of https://issues.scala-lang.org/browse/SI-4856 .
> there's an example that shows that forward referencing vals can be useful.
> I quote:
>
> scala> val fib: Stream[Int] = 1 #:: ((0 #:: fib zip fib) map { case (x, y) => x + y })
> fib: Stream[Int] = Stream(1, ?)

Thank you, Martin, but I should agree with Dave that the usage of 'fib' here (on the
right-hand-side) is not really a forward reference. The parameter of the '#::'-method will be taken
by name, and therefore is in fact a function object that is fully initialized *without accessing
'fib'* (the desugared version of this expression is attached, the class of the function is
'Fib$$anonfun$1').

It should also be noted that this code doesn't compile if 'fib' is a local val (instead of a member
val), since the compiler sees it as a (formal) forward reference: "error: forward reference extends
over definition of value fib".

This behavior (member val: OK, local val: compile error) could be seen as inconsistency.

A *lazy* val does work in both cases (member / local).

But that were rather side notes. What is much more important in my opinion:

Java's automatic control over initialization, which ensures that fields and variables are
initialized before their usage, was an important improvement as compared to C/C++.

Scala may not be a step back.

At the beginning I was delighted with Scala features such as the uniform access principle or the
ability to override member fields. But if it comes at the costs of losing automatic control over the
initialization state, I would rather give up some of these new features than vice versa.

Assuming, you would agree, which options do we have?

I see the following:

1) We could disallow overriding vals¹, if it will make possible (does it?) to get automatic
initialization control.

2) Another option would be to handle all member-'val's as 'lazy vals'².
As an addition, perhaps, it would make sense to introduce another keywords for local variables
(which are simple old variables) as opposed to member fields (which are field/accessor[/mutator]
pairs/triples)?

3) Something better?

What do you think about?

Thanks,
Eugen

¹ Which imo isn't properly working anyway, e.g. there is no access to the overriden value via
'super', there is a lot of nuances how to avoid 'null's, etc., etc.

² Theoretically, such change (eager evaluation -> lazy evaluation) should not break existing
programs (i.e. their correctness), but the point of initialization might be important for the
run-time behavior, e.g. for the responsiveness of the user interface. I think also that the slightly
reduced performance due to initialization check on each access will not be an issue in practice,
since the resulting target reference is often cached by the caller. (Remember, I'm still speaking
about member vals, not about local vals.)

odersky
Joined: 2008-07-29,
User offline. Last seen 45 weeks 6 days ago.
Re: Re: surprised by class initialization

On Sun, Dec 4, 2011 at 7:29 AM, Eugen Labun wrote:
> On 2011-12-03 10:02, martin odersky wrote:
>>
>> At the end of https://issues.scala-lang.org/browse/SI-4856 .
>> there's an example that shows that forward referencing vals can be useful.
>> I quote:
>>
>> scala> val fib: Stream[Int] = 1 #:: ((0 #:: fib zip fib) map { case (x, y)
>> => x + y })
>> fib: Stream[Int] = Stream(1, ?)
>
>
> Thank you, Martin, but I should agree with Dave that the usage of 'fib' here
> (on the right-hand-side) is not really a forward reference. The parameter of
> the '#::'-method will be taken by name, and therefore is in fact a function
> object that is fully initialized *without accessing 'fib'* (the desugared
> version of this expression is attached, the class of the function is
> 'Fib$$anonfun$1').

Of course. That's one half of my point! In general you will not be
able to detect forward references statically. Subcases, yes, but in
general no. That's why the best we can do is a warning.

>
> It should also be noted that this code doesn't compile if 'fib' is a local
> val (instead of a member val), since the compiler sees it as a (formal)
> forward reference: "error: forward reference extends over definition of
> value fib".
>
Excact. Same as in Java. Java prescribes that local variables are
guaranteed to be explicitly initialized but object fields need not be.
The problem is considerably simpler for local variables because there
are no aliases. But some things (e.g. fib) fall through the cracks,
and are rejected by the stricter warning even though they are
technically OK.

>
> But that were rather side notes. What is much more important in my opinion:
>
>
> Java's automatic control over initialization, which ensures that fields and
> variables are initialized before their usage, was an important improvement
> as compared to C/C++.
>
> Scala may not be a step back.
>
I have argued two times now on this thread Java and Scala behave
_exactly the same_. Before we go on, please show an example where they
do not.

> At the beginning I was delighted with Scala features such as the uniform
> access principle or the ability to override member fields. But if it comes
> at the costs of losing automatic control over the initialization state, I
> would rather give up some of these new features than vice versa.
>
Neither Java nor Scala give you automatic control over initialization state.

>
> Assuming, you would agree, which options do we have?
>
> I see the following:
>
> 1) We could disallow overriding valsน, if it will make possible (does it?)
> to get automatic initialization control.
>
It won't. To find out more, here's a reference:

Manuel Fähndrich, K. Rustan M. Leino: Declaring and checking non-null
types in an object-oriented language. OOPSLA 2003: 302-312.

The paper shows among others what you need to do to get static
initialization checking. The type system is very complicated and still
very restrictive. It's also strictly research. It was implemented in
Spec# (I believe), but never in a mainstream programming language.

> 2) Another option would be to handle all member-'val's as 'lazy vals'ฒ.
>   As an addition, perhaps, it would make sense to introduce another keywords
> for local variables (which are simple old variables) as opposed to member
> fields (which are field/accessor[/mutator] pairs/triples)?
>
We considered that. But lazy vals interact badly with effects. If you
need effects to happen during object initialization, lazy vals are
problematic.

Cheers

nilskp
Joined: 2009-01-30,
User offline. Last seen 1 year 27 weeks ago.
Re: Re: surprised by class initialization
On Mon, Dec 5, 2011 at 1:57 PM, martin odersky <martin [dot] odersky [at] epfl [dot] ch> wrote:
I have argued two times now on this thread Java and Scala behave _exactly the same_. Before we go on, please show an example where they
do not.

Martin, I asked a couple of days ago, if anyone knows why Java allows forward referencing when prefixing with "this". I asked because I can't think of a use case where that is useful, and therefore it seems questionable that Scala should allow it. (I can understand why there are technical reasons why this can be difficult to detect, but that's another argument).
roland.kuhn
Joined: 2011-02-21,
User offline. Last seen 35 weeks 3 days ago.
Re: Re: surprised by class initialization
Doesn’t the fib example from three posts up fit the bill:

val fib: Stream[Int] = 1 #:: ((0 #:: fib zip fib) map { case (x, y) => x + y })

That’s a forward reference, AFAICT (“left” is the new “forward” ;-) ).

Regards,

Roland
DaveScala
Joined: 2011-03-18,
User offline. Last seen 1 year 21 weeks ago.
Re: surprised by class initialization

On 5 dec, 23:21, rkuhn wrote:
> Doesn’t the fib example from three posts up fit the bill:
>
> val fib: Stream[Int] = 1 #:: ((0 #:: fib zip fib) map { case (x, y) => x +
> y })
>
> That’s a forward reference, AFAICT (“left” is the new “forward” ;-) ).
>
> Regards,
>
> Roland

Maybe it is also called a "forward reference", but that is a recursive
function within a statement which is something different than this
issue. Simply said: all fibs are behind val fib.

Here it is about imperative forward references across statement bounds
(semicolons). There is no recursive function here.
In the example below the correct answer is 5 but for the eager forward
reference is 2.
Only lazy and final forward references return 5 as correct answer (and
"eager forward reference with early override")
So an eager forward reference is a potentially threat for incorrect
answer.
Therefore a warning would be helpful

package initscala
class Foo { val other = this; val x = other.y + 2; val y = 3 } //
eager forward reference
class FooLazy { val other = this; val x = other.y + 2; lazy val y =
3 } // lazy forward reference
class FooFinal { val other = this; val x = other.y + 2; final val y =
3 } // final forward reference
class Foo2 extends Foo { override val y = 3 } // eager forward
reference with late override
class Foo3 extends FooLazy { override lazy val y = 3 } // lazy forward
reference with late override
class Foo4 extends { override val y = 3 } with Foo // eager forward
reference with early override
class Bar { val other = this; val y = 3; val x = other.y + 2 } // no
forward reference

object Main extends App {
println("Foo (eager forward reference): " + (new Foo).x)
println("FooLazy (lazy forward reference): " + (new FooLazy).x)
println("FooFinal (final forward reference): " + (new FooFinal).x)
println("Foo2 (eager forward reference with late override): " +
(new Foo2).x)
println("Foo3 (lazy forward reference with late override): " + (new
Foo3).x)
println("Foo4 (eager forward reference with early override): " +
(new Foo4).x)
println("Bar (no forward reference): " + (new Bar).x)

}
/*
C:\scala-2.9.1.final\examples>scala initscala.Main
Foo (eager forward reference): 2
FooLazy (lazy forward reference): 5
FooFinal (final forward reference): 5
Foo2 (eager forward reference with late override): 2
Foo3 (lazy forward reference with late override): 5
Foo4 (eager forward reference with early override): 5
Bar (no forward reference): 5
*/

roland.kuhn
Joined: 2011-02-21,
User offline. Last seen 35 weeks 3 days ago.
Re: surprised by class initialization
Actually, the “fib” example is not a recursive function, it is really a forward reference. What the compiler must do is conceptually this:

val temp = 1 #:: ((0 #:: fib zip fib) map { case (x, y) => x + y })
val fib = temp

and since it is not possible (halting problem) to determine whether the first line actually evaluates “fib” (in the general case), it is not possible to provide an error message for uninitialized forward references. This also shows that just outlawing forward references is really a restriction. (I get the impression that initialization semantics must not be Turing complete in order to be able to definitively discriminate legitimate from illegitimate forward references, right? Wouldn’t that spread over the whole language, then?)

So, to return to the context of your message: as I said earlier, I very much agree that the compiler should warn about this on a best-effort basis. Just know that you cannot absolutely rely on the correctness of the warning.

Regards,

Roland
E. Labun
Joined: 2010-06-20,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: surprised by class initialization

Martin, thank you for your detailed answer. I can only imagine how busy you are, and still finding
time to answer in forums to us, simple users! (A big plus for Scala, btw!)

On 2011-12-05 20:57, martin odersky wrote:
> On Sun, Dec 4, 2011 at 7:29 AM, Eugen Labun wrote:
>> On 2011-12-03 10:02, martin odersky wrote:
>>>
>>> At the end of https://issues.scala-lang.org/browse/SI-4856 .
>>> there's an example that shows that forward referencing vals can be useful.
>>> I quote:
>>>
>>> scala> val fib: Stream[Int] = 1 #:: ((0 #:: fib zip fib) map { case (x, y)
>>> => x + y })
>>> fib: Stream[Int] = Stream(1, ?)
>>
>>
>> Thank you, Martin, but I should agree with Dave that the usage of 'fib' here
>> (on the right-hand-side) is not really a forward reference. The parameter of
>> the '#::'-method will be taken by name, and therefore is in fact a function
>> object that is fully initialized *without accessing 'fib'* (the desugared
>> version of this expression is attached, the class of the function is
>> 'Fib$$anonfun$1').
>
> Of course. That's one half of my point! In general you will not be
> able to detect forward references statically. Subcases, yes, but in
> general no. That's why the best we can do is a warning.

I trust you, but still hoping (sorry!) that that might be possible, perhaps when some additional
conditions hold (such as, for example, a prohibition to override vals). In the fib-example it seems
to be possible. I would like to see an example where it is definitely not.

Issuing a warning would be an improvement anyway.

>> It should also be noted that this code doesn't compile if 'fib' is a local
>> val (instead of a member val), since the compiler sees it as a (formal)
>> forward reference: "error: forward reference extends over definition of
>> value fib".
>>
> Excact. Same as in Java. Java prescribes that local variables are
> guaranteed to be explicitly initialized but object fields need not be.
> The problem is considerably simpler for local variables because there
> are no aliases.

"aliases": you mean accessors generated for member vals or something else?

> But some things (e.g. fib) fall through the cracks,
> and are rejected by the stricter warning even though they are
> technically OK.

Yes. But it would be nice if the compiler could analyze the desugared version and see that it would
be working in the case of a local val, too. So that the same expression would work (or cause a
compile error) equally in both cases: if declared as a member val or as a local val.

The point seems to be to differentiate, where it is a simple forward reference, and where it gets
implicitly replaced by a function object, which is initialized without accessing the variable (i. e.
the 'val' in question).

The implicit replacement by the function object is triggered by the by-name parameters or by
implicit conversions. In either case the replacement become explicit in the desugared version of
expression and could be analyzed by compiler.

Or am I missing something?

>> But that were rather side notes. What is much more important in my opinion:
>>
>>
>> Java's automatic control over initialization, which ensures that fields and
>> variables are initialized before their usage, was an important improvement
>> as compared to C/C++.
>>
>> Scala may not be a step back.
>>
> I have argued two times now on this thread Java and Scala behave
> _exactly the same_. Before we go on, please show an example where they
> do not.

Here are two pieses of code that seem to be semantically equivalent to me:

Java: causes a compile error
--------------------------------------------------------------------
class Test {
final int x = y; // compile error: illegal forward reference (y)
final int y = 5;

public static void main(String[] args) {
Test t = new Test();
System.out.println("x = " + t.x);
System.out.println("y = " + t.y);
}
}
--------------------------------------------------------------------

Scala: compiles, 'x' is initialized with 0
(the expectation was: a compile error or initialization with 5)
--------------------------------------------------------------------
object Test {
val x: Int = y // compiles OK
val y: Int = 5

def main(args: Array[String]) {
println("x = " + x) // 0
println("y = " + y) // 5
}
}
--------------------------------------------------------------------

Yet another example, borrowed from the ticket 399 https://issues.scala-lang.org/browse/SI-399:

First, without using 'this' in the reference to 'foo':

Java: compile error
--------------------------------------------------------------------
class Foo {
{
System.out.println ( foo ); // compile error: illegal forward reference (foo)
}
final int foo = 1;
}

public class Test {
public static void main(String[] args) {
new Foo();
}
}
--------------------------------------------------------------------

Scala: compiles, output: 0
(the expectation was: a compile error or output 1)
--------------------------------------------------------------------
class Foo { // compiles OK
println ( foo ) // prints 0
val foo = 1
}

object Test {
def main(args: Array[String]) {
new Foo
}
}
--------------------------------------------------------------------

Now with 'this' in the reference to 'foo' (i.e. 'this.foo'):

Scala's behavior remains unchanged.

Java version surprisingly compiles now, but the output is 1, which is OK.
--------------------------------------------------------------------
class Foo { // compiles OK
{
System.out.println ( this.foo ); // prints 1
}
final int foo = 1;
}

public class Test {
public static void main(String[] args) {
new Foo();
}
}
--------------------------------------------------------------------

Environment:

Java JDK: 1.6.0_26
Scala: 2.9.1.final
OS: WinXP SP3

>> At the beginning I was delighted with Scala features such as the uniform
>> access principle or the ability to override member fields. But if it comes
>> at the costs of losing automatic control over the initialization state, I
>> would rather give up some of these new features than vice versa.
>>
> Neither Java nor Scala give you automatic control over initialization state.
>
>>
>> Assuming, you would agree, which options do we have?
>>
>> I see the following:
>>
>> 1) We could disallow overriding vals¹, if it will make possible (does it?)
>> to get automatic initialization control.
>>
> It won't. To find out more, here's a reference:
>
> Manuel Fähndrich, K. Rustan M. Leino: Declaring and checking non-null
> types in an object-oriented language. OOPSLA 2003: 302-312.
>
> The paper shows among others what you need to do to get static
> initialization checking. The type system is very complicated and still
> very restrictive. It's also strictly research. It was implemented in
> Spec# (I believe), but never in a mainstream programming language.

Thank you for the reference (I used [1]), but IIUC the article handles a slightly different set of
problems. Namely, detecting null-values *after* the corresponding fields already been initialized,
but not yet reassigned. See e.g. the code on p.2: the non-final string 'name' has been already
initialized with the default value (since it has no initializer), and the whole discussion is about
preventing access to the 'name' before it get a new non-null value in the constructor. The keyword
'final' is nowhere used in code.

That is different from our discussion about initializing 'final' values.

In my understanding, the goal in Java is to make this initialization (which might be technically a
two-step process from the default to the specified value), working as atomic for the user.

In Scala, there are additional complications due to field+accessor implementation of vals.
Nevertheless, the goal is the same.

The authors say though that their "partially-initialized types" may help also with problems that
targeting 'final' values. (That is briefly mentioned in the last two paragraphs of the last section:
see p.10 before 'REFERENCES'.) They say further: "for any readonly (in C#) or final (in Java) field
f, after the allocation of an object x and before the assignment to x.f, reading x.f will return a
zero-equivalent value". That is what I mean with "technically, a two-step process".
And is also the point of our discussion: make sure that the value is fully initialized before its
reading or prohibit the reading by generating a compile error.

And I'm hoping that this is feasible, at least for the most situations (like the Java/Scala examples
above).

>> 2) Another option would be to handle all member-'val's as 'lazy vals'².
>> As an addition, perhaps, it would make sense to introduce another keywords
>> for local variables (which are simple old variables) as opposed to member
>> fields (which are field/accessor[/mutator] pairs/triples)?
>>
> We considered that. But lazy vals interact badly with effects. If you
> need effects to happen during object initialization, lazy vals are
> problematic.

What do you mean with "interact badly with effects"?
I'm asking because this approach seems to be an interesting and robust solution!
Also any references will be highly appreciated.

Thank you,
Eugen

[1] http://research.microsoft.com/en-us/um/people/leino/papers/krml109.pdf

E. Labun
Joined: 2010-06-20,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: surprised by class initialization

Hi Roland,

On 2011-12-06 14:02, rkuhn wrote:
> Actually, the “fib” example is not a recursive function, it is really a forward reference. What the
> compiler must do is conceptually this:
>
> val temp = 1 #:: ((0 #:: fib zip fib) map { case (x, y) => x + y })
> val fib = temp

no, the 'fib' (and the whole part after "1 #:: ") gets replaced by something like 'new
Anon$$Func$$()'. 'fib' is not read during the instance creation. So, no 'temp' variable is needed,
and so this is (technically) not a forward reference.

--
Eugen

roland.kuhn
Joined: 2011-02-21,
User offline. Last seen 35 weeks 3 days ago.
Re: Re: surprised by class initialization


Am Dienstag, 6. Dezember 2011 18:10:36 UTC+1 schrieb Eugen Labun:
Hi Roland,

On 2011-12-06 14:02, rkuhn wrote:
> Actually, the “fib” example is not a recursive function, it is really a forward reference. What the
> compiler must do is conceptually this:
>
> val temp = 1 #:: ((0 #:: fib zip fib) map { case (x, y) => x + y })
> val fib = temp

no, the 'fib' (and the whole part after "1 #:: ") gets replaced by something like 'new
Anon$$Func$$()'. 'fib' is not read during the instance creation. So, no 'temp' variable is needed,
and so this is (technically) not a forward reference.


The fact that “fib” is not read during the construction of the closure is not something that the compiler can statically prove: the closure receives “this” as constructor parameter precisely in order to be able to access “fib”, so how should the compiler know that #:: does NOT evaluate the closure? And the return of #:: is what gets stored in the “fib” field, so, yes, this really is a forward reference, and a clear example of why the compiler cannot reliably warn about this.

Replying to your other mail to Martin: desugaring does not help at all, since the behavior of #:: is not statically known (i.e. it is not part of the type signature that this method does not evaluate the by-name parameter).

Regards,

Roland
DaveScala
Joined: 2011-03-18,
User offline. Last seen 1 year 21 weeks ago.
Re: surprised by class initialization

On 6 dec, 14:02, rkuhn wrote:
> Actually, the “fib” example is not a recursive function, it is really a
> forward reference. What the compiler must do is conceptually this:
>
> val temp = 1 #:: ((0 #:: fib zip fib) map { case (x, y) => x + y })
> val fib = temp
>
> and since it is not possible (halting problem) to determine whether the
> first line actually evaluates “fib” (in the general case),

The left hand side expression and right hand side of the expression
are evaluated as one whole statement, not two separate statements.
To determine the return type therefore an explicit type annotation is
needed.

You could also rewrite it like:
scala> def temp : Stream[Int] = 1 #:: ((0 #:: temp zip temp) map
{ case (x, y) =
> x + y })
temp: Stream[Int]

scala> val fib = temp
fib: Stream[Int] = Stream(1, ?)

scala> fib take 10 toList
res0: List[Int] = List(1, 1, 2, 3, 5, 8, 13, 21, 34, 55)

then it is clearly visible that it is a recursive function.

What is the difference with:
scala> def fib(i: Int): Int = i match {
| case _ if i <= 1 => i
| case _ => fib(i-1) + fib(i-2)
| }
fib: (i: Int)Int

scala> fib(5)
res1: Int = 5

Except the stream is infinite and the other is finite

E. Labun
Joined: 2010-06-20,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: surprised by class initialization

On 2011-12-06 19:05, rkuhn wrote:
>
>
> Am Dienstag, 6. Dezember 2011 18:10:36 UTC+1 schrieb Eugen Labun:
>
> Hi Roland,
>
> On 2011-12-06 14:02, rkuhn wrote:
> > Actually, the “fib” example is not a recursive function, it is really a forward reference.
> What the
> > compiler must do is conceptually this:
> >
> > val temp = 1 #:: ((0 #:: fib zip fib) map { case (x, y) => x + y })
> > val fib = temp
>
> no, the 'fib' (and the whole part after "1 #:: ") gets replaced by something like 'new
> Anon$$Func$$()'. 'fib' is not read during the instance creation. So, no 'temp' variable is needed,
> and so this is (technically) not a forward reference.
>
>
> The fact that “fib” is not read during the construction of the closure is not something that the
> compiler can statically prove: the closure receives “this” as constructor parameter precisely in
> order to be able to access “fib”, so how should the compiler know that #:: does NOT evaluate the
> closure? And the return of #:: is what gets stored in the “fib” field, so, yes, this really is a
> forward reference, and a clear example of why the compiler cannot reliably warn about this.

You are right! I somehow overseen that the closure (i.e. the constructor of the anon. function)
takes 'this' as parameter, and that there is no info about what could happen in the method itself.
Sorry.

Then how about following:

forward_reference match {
case by-name-parameter => warning "reference might not be initialized"
case _ => error "illegal forward reference"
}

?

> Replying to your other mail to Martin: desugaring does not help at all, since the behavior of #:: is
> not statically known (i.e. it is not part of the type signature that this method does not evaluate
> the by-name parameter).

Yes, I see now. Thank you!

Regards,
Eugen

roland.kuhn
Joined: 2011-02-21,
User offline. Last seen 35 weeks 3 days ago.
Re: Re: surprised by class initialization


Am Dienstag, 6. Dezember 2011 21:04:58 UTC+1 schrieb Eugen Labun:
On 2011-12-06 19:05, rkuhn wrote:
>
>
> Am Dienstag, 6. Dezember 2011 18:10:36 UTC+1 schrieb Eugen Labun:
>
>     Hi Roland,
>
>     On 2011-12-06 14:02, rkuhn wrote:
>      > Actually, the “fib” example is not a recursive function, it is really a forward reference.
>     What the
>      > compiler must do is conceptually this:
>      >
>      > val temp = 1 #:: ((0 #:: fib zip fib) map { case (x, y) => x + y })
>      > val fib = temp
>
>     no, the 'fib' (and the whole part after "1 #:: ") gets replaced by something like 'new
>     Anon$$Func$$()'. 'fib' is not read during the instance creation. So, no 'temp' variable is needed,
>     and so this is (technically) not a forward reference.
>
>
> The fact that “fib” is not read during the construction of the closure is not something that the
> compiler can statically prove: the closure receives “this” as constructor parameter precisely in
> order to be able to access “fib”, so how should the compiler know that #:: does NOT evaluate the
> closure? And the return of #:: is what gets stored in the “fib” field, so, yes, this really is a
> forward reference, and a clear example of why the compiler cannot reliably warn about this.

You are right! I somehow overseen that the closure (i.e. the constructor of the anon. function)
takes 'this' as parameter, and that there is no info about what could happen in the method itself.
Sorry.

Then how about following:

forward_reference match {
   case by-name-parameter => warning "reference might not be initialized"
   case _ => error "illegal forward reference"
}


Yes, would look reasonable, but I sympathize with Martin on this one: if the rule were that if the compiler can prove you wrong it is an error, then people would probably be disappointed in cases where it fails to do so. Since the border of what can be proven and what not (and I mean “practically”, not “theoretically”) is a rather convoluted construct, that would be very difficult to explain, and since an error discriminates between legal and illegal programs this would make the language spec “indeterministic” in a sense (i.e. as seen from the user). Thus, the best we can hope for is a warning which is as reliable as it can get without exploding the build time.
 
Regards,

Roland
roland.kuhn
Joined: 2011-02-21,
User offline. Last seen 35 weeks 3 days ago.
Re: surprised by class initialization


Am Dienstag, 6. Dezember 2011 20:11:13 UTC+1 schrieb Dave:

On 6 dec, 14:02, rkuhn <goo [dot] [dot] [dot] [at] rkuhn [dot] info> wrote:
> Actually, the “fib” example is not a recursive function, it is really a
> forward reference. What the compiler must do is conceptually this:
>
> val temp = 1 #:: ((0 #:: fib zip fib) map { case (x, y) => x + y })
> val fib = temp
>
> and since it is not possible (halting problem) to determine whether the
> first line actually evaluates “fib” (in the general case),

The left hand side expression and right hand side of the expression
are evaluated as one whole statement, not two separate statements.
To determine the return type therefore an explicit type annotation is
needed.


Functionally or conceptually that may be true, but computers execute sequences of commands, and this sequence must compute the rvalue before assigning to the lvalue, which means that evaluating the rvalue must not actually read the lvalue. Which is, of course, the core of the problem (i.e. that a program cannot determine whether another program does a specific thing without actually running it, which may not ever terminate).
 

You could also rewrite it like:
scala> def temp : Stream[Int] = 1 #:: ((0 #:: temp zip temp) map
{ case (x, y) =
> x + y })
temp: Stream[Int]


Yes, you _could_ rewrite it, but then you change it from a clever forward reference to an eerily mind-boggling recursive method ;-)
 

scala> val fib = temp
fib: Stream[Int] = Stream(1, ?)

scala> fib take 10 toList
res0: List[Int] = List(1, 1, 2, 3, 5, 8, 13, 21, 34, 55)

then it is clearly visible that it is a recursive function.

What is the difference with:
scala>     def fib(i: Int): Int = i match {
     |       case _ if i <= 1 => i
     |       case _ => fib(i-1) + fib(i-2)
     |     }
fib: (i: Int)Int

scala> fib(5)
res1: Int = 5

Except the stream is infinite and the other is finite

E. Labun
Joined: 2010-06-20,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: surprised by class initialization

Any comments on the code examples that showing inconsistencies between Java and Scala?

http://groups.google.com/group/scala-user/msg/36376d8fae5fdf41

Thank you,
Eugen

dcsobral
Joined: 2009-04-23,
User offline. Last seen 38 weeks 5 days ago.
Re: Re: surprised by class initialization

On Fri, Dec 9, 2011 at 10:41, Eugen Labun wrote:
> Any comments on the code examples that showing inconsistencies between Java
> and Scala?
>
> http://groups.google.com/group/scala-user/msg/36376d8fae5fdf41

final int x != val x: Int
final int x == final val x: Int

E. Labun
Joined: 2010-06-20,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: surprised by class initialization

On 2011-12-09 15:04, Daniel Sobral wrote:
> On Fri, Dec 9, 2011 at 10:41, Eugen Labun wrote:
>> Any comments on the code examples that showing inconsistencies between Java
>> and Scala?
>>
>> http://groups.google.com/group/scala-user/msg/36376d8fae5fdf41
>
> final int x != val x: Int
> final int x == final val x: Int

I changed the Scala code in examples accordingly (i.e. to "final val x: Int = ...") and retested.
(Although I think that 'final' in Scala adds another semantics that are not present in Java).

The results are exactly the same!

DaveScala
Joined: 2011-03-18,
User offline. Last seen 1 year 21 weeks ago.
Re: surprised by class initialization

> I changed the Scala code in examples accordingly (i.e. to "final val x: Int
> = ...") and retested.
> (Although I think that 'final' in Scala adds another semantics that are not
> present in Java).
>
> The results are exactly the same!

(I'm not on scala-user.) Try this one to see 5/5. (I'm not suggesting
this is super intuitive.)

object Test {
final val x = y // compiles OK
final val y = 5

def main(args: Array[String]) {
println("x = " + x) // 5
println("y = " + y) // 5
}
}

C:\scala-2.9.1.final\examples>scalac testscala.scala

C:\scala-2.9.1.final\examples>scala testscala.Test
x = 0
y = 5

Are you testing with 2.10 ?

E. Labun
Joined: 2010-06-20,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: surprised by class initialization

> ... Try this one to see 5/5. ...
>
> object Test {
> final val x = y // compiles OK
> final val y = 5
>
> def main(args: Array[String]) {
> println("x = " + x) // 5
> println("y = " + y) // 5
> }
> }
>
>
> C:\scala-2.9.1.final\examples>scalac testscala.scala
>
> C:\scala-2.9.1.final\examples>scala testscala.Test
> x = 0
> y = 5

But you get 0/5, too (not 5/5). Or am I missing something?

>
> Are you testing with 2.10 ?
I'm testing with Scala 2.9.1.final on Windows XP SP3.

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