Please...

193 views
Skip to first unread message

Nils Kilden-Pedersen

unread,
Mar 7, 2013, 3:56:31 PM3/7/13
to scala-user
Please give me a way to save me from myself:

scala> class Foo {
     | val bar = baz
     | val baz = "Oh noes"
     | }
defined class Foo

scala> new Foo
res0: Foo = Foo@465e9361

scala> println(res0.bar)
null


I know this been discussed before, and Martin's position is that you can do the same in Java (although not as syntactically transparent). But could we at least get a compiler option for a warning? This has bitten me again today.

Alternatively, is there a use case for allowing this?

Michael Swierczek

unread,
Mar 7, 2013, 4:07:33 PM3/7/13
to Nils Kilden-Pedersen, scala-user
Yikes! Interesting. Maybe we should go the Haskell route and make
all of the vals lazy by default.
I assume there are good reasons for not doing that, but I can't think
of them off hand.
-Mike

Rex Kerr

unread,
Mar 7, 2013, 4:10:31 PM3/7/13
to Michael Swierczek, Nils Kilden-Pedersen, scala-user
On Thu, Mar 7, 2013 at 4:07 PM, Michael Swierczek <mike.sw...@gmail.com> wrote:

I assume there are good reasons for not doing that, but I can't think
of them off hand.

Performance?

  --Rex

Nils Kilden-Pedersen

unread,
Mar 7, 2013, 4:17:31 PM3/7/13
to Rex Kerr, Michael Swierczek, scala-user

Compiler performance?


 

  --Rex


Michael Swierczek

unread,
Mar 7, 2013, 4:45:01 PM3/7/13
to Nils Kilden-Pedersen, Rex Kerr, scala-user
That intuitively makes sense, but I couldn't guess how much of an
impact it would have.

So I think your original proposal makes sense - a compiler warning
when a val declaration will always yield null because of a non-lazy
forward reference.

-Mike

Josh Suereth

unread,
Mar 7, 2013, 5:00:57 PM3/7/13
to Nils Kilden-Pedersen, scala-user
The "easiest" and also very annoying way to ensure your safety is to compile twice:

(1) with -Xcheckinit
(2) with -optimise (or just not -Xcheckinit)

Checkinit will emit runtime errors if you attempt to access something which is not initialized yet.   I don't think any build tool gives you a workflow for compiling your program with different byte-code outputs though, but it's essentially what you want.

The runtime checks for checkinit are expensive...



--
You received this message because you are subscribed to the Google Groups "scala-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-user+...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Nils Kilden-Pedersen

unread,
Mar 7, 2013, 5:10:08 PM3/7/13
to Josh Suereth, scala-user
On Thu, Mar 7, 2013 at 4:00 PM, Josh Suereth <joshua....@gmail.com> wrote:
The "easiest" and also very annoying way to ensure your safety is to compile twice:

(1) with -Xcheckinit
(2) with -optimise (or just not -Xcheckinit)

Checkinit will emit runtime errors if you attempt to access something which is not initialized yet.

I already get runtime errors :-)
 
  I don't think any build tool gives you a workflow for compiling your program with different byte-code outputs though, but it's essentially what you want.

No, I'd just like the same as Java, which is a compile time error: "Cannot reference a field before it is defined".
 

Josh Suereth

unread,
Mar 7, 2013, 5:22:12 PM3/7/13
to Nils Kilden-Pedersen, scala-user
Yeah, the problem is it's possible to define it so that it works:

scala> class Foo {
     |   val bar = baz
     |   val baz = "O NOES"
     | }
defined class Foo


scala> new { override val baz = "O NOES" } with Foo
res1: Foo = $anon$1@133b32e5

scala> res1.bar
res2: String = O NOES



They're called "Early Initializers", and they help solve the startup issue of classes.   We allow traits to have vals, and also allow this kind of ambiguity because sometimes it's handy.

However, many times we really do want a more principled solution.  -Xcheckinit will at least give you a useful runtime warning telling you which vals were not initialized so you can use Early-Initializers.  It's not a perfect solution, but it's better than nothing.


- Josh

Nils Kilden-Pedersen

unread,
Mar 7, 2013, 5:49:43 PM3/7/13
to Josh Suereth, scala-user
On Thu, Mar 7, 2013 at 4:22 PM, Josh Suereth <joshua....@gmail.com> wrote:
Yeah, the problem is it's possible to define it so that it works:

scala> class Foo {
     |   val bar = baz
     |   val baz = "O NOES"
     | }
defined class Foo


scala> new { override val baz = "O NOES" } with Foo
res1: Foo = $anon$1@133b32e5

scala> res1.bar
res2: String = O NOES



They're called "Early Initializers", and they help solve the startup issue of classes.   We allow traits to have vals, and also allow this kind of ambiguity because sometimes it's handy.

Overriding is handy yes, but is the original (wrong) order necessary? Are there cases where, if I was required to switch the order of bar and baz in Foo, it would cause wrong behavior?
 

Jason Zaugg

unread,
Mar 7, 2013, 6:13:39 PM3/7/13
to Nils Kilden-Pedersen, scala-user
Until we get a similar warning into the compiler (there is already a ticket, but I can't locate it right now), you can run the code analysis in IntelliJ. Screenshot attached.

-jason
Screen Shot 2013-03-08 at 12.10.59 AM.png

Nils Kilden-Pedersen

unread,
Mar 7, 2013, 6:18:12 PM3/7/13
to Jason Zaugg, scala-user

Nice to see that it's not impossible.

Do they still use their own compiler or is it like the Eclipse plugin, leveraging the standard one?
 

-jason

Jason Zaugg

unread,
Mar 8, 2013, 2:06:25 AM3/8/13
to Nils Kilden-Pedersen, scala-user
IntelliJ has a custom presentation compiler for Scala, as it does for Java. That works best with its architecture, and despite being a huge amount of work, makes writing refactorings and code analysis much easier.

-jason 

Oliver Ruebenacker

unread,
Mar 11, 2013, 12:56:03 PM3/11/13
to Nils Kilden-Pedersen, scala-user
Hello,

This seems not be confined to fields and class declarations:

scala> println(x); val x = "Oops"
null
x: String = Oops

Take care
Oliver
> --
> You received this message because you are subscribed to the Google Groups
> "scala-user" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to scala-user+...@googlegroups.com.
> For more options, visit https://groups.google.com/groups/opt_out.
>
>



--
IT Project Lead at PanGenX (http://www.pangenx.com)
The purpose is always improvement

Jason Zaugg

unread,
Mar 11, 2013, 12:59:46 PM3/11/13
to Oliver Ruebenacker, Nils Kilden-Pedersen, scala-user
On Mon, Mar 11, 2013 at 5:56 PM, Oliver Ruebenacker <cur...@gmail.com> wrote:
     Hello,

  This seems not be confined to fields and class declarations:

scala> println(x); val x = "Oops"
null
x: String = Oops

Definitions in the REPL are packaged into objects behind the scenes. Forward reference checking is enabled for statements in a block:

scala> {  println(x); val x = "Oops" }
<console>:8: error: forward reference extends over definition of value x
              {  println(x); val x = "Oops" }
                         ^
-jason 

Rich Oliver

unread,
Mar 11, 2013, 5:49:39 PM3/11/13
to scala...@googlegroups.com, Nils Kilden-Pedersen
They're called "Early Initializers", and they help solve the startup issue of classes.   We allow traits to have vals, and also allow this kind of ambiguity because sometimes it's handy.

However, many times we really do want a more principled solution.  -Xcheckinit will at least give you a useful runtime warning telling you which vals were not initialized so you can use Early-Initializers.  It's not a perfect solution, but it's better than nothing.


- Josh
Why can they not be checked at compile time? It should be possible to check the construction initialisation order when ever a non abstract class is declared for the calling of un-initialised vars/ vals?

Josh Suereth

unread,
Mar 11, 2013, 6:25:05 PM3/11/13
to Rich Oliver, Nils Kilden-Pedersen, scala-user

I'm not saying it shouldn't, just ape-ing justification I've heard before.

The enemy of "this one little thing" is "universal applicability".

I'd be all for seeing a compiler warning, promotable to error when we see this.   Just stating that it is possible for this code to eventually work.  Unless the class final/sealed, we can't know for certain (but we can make a pretty darn good guess). 

--

Matthew Pocock

unread,
Mar 12, 2013, 10:02:27 AM3/12/13
to Josh Suereth, Rich Oliver, Nils Kilden-Pedersen, scala-user
I don't see where you'd want to allow this code as it stands. If you are intending to override bar in a subclass, then sprinkle with `abstract` to force the intended implementation. Otherwise, in the absence of a lazy, this really should be at least a warning and ideally an error, as it generates code that's guaranteed to fail at run-time otherwise. I get bitten by this periodically, and it usually takes me a little while to figure out where that null came from each time.

Matthew
--
Dr Matthew Pocock
Integrative Bioinformatics Group, School of Computing Science, Newcastle University
skype: matthew.pocock
tel: (0191) 2566550

Rich Oliver

unread,
Mar 13, 2013, 4:43:03 AM3/13/13
to scala...@googlegroups.com, Rich Oliver, Nils Kilden-Pedersen
Yes as indicated it shouldn't apply to abstract classes. I can't see the ordering of the initialisation is is any different from lack of initialisation which already gives a compile error, for non abstract classes.

Nils Kilden-Pedersen

unread,
Mar 13, 2013, 10:08:03 AM3/13/13
to Rich Oliver, scala...@googlegroups.com
On Wed, Mar 13, 2013 at 3:43 AM, Rich Oliver <rzi...@gmail.com> wrote:
Yes as indicated it shouldn't apply to abstract classes.

That's too broad of a statement. Abstract classes just means that there must be a subclass for you to instantiate it. It doesn't mean that all (or any) methods necessarily are abstract.

 

Oliver Ruebenacker

unread,
Mar 20, 2013, 4:45:03 PM3/20/13
to Nils Kilden-Pedersen, Rich Oliver, scala...@googlegroups.com
Hello,

A variation on the topic:

scala> object A { val a = "woof" }; object B { import A._; println(a)}; B
woof
defined module A
defined module B
res3: B.type = B$@64fde8da

scala> object A { val a = "woof" }; object B { import A._; println(a);
val a = "wow" }; B
null
defined module A
defined module B
res4: B.type = B$@2be44538

Take care
Oliver
Reply all
Reply to author
Forward
0 new messages