Re: Reopening the debate about non-nullable-by-default: initialization of member fields

2014-05-03 Thread w0rp via Digitalmars-d
I would allow this bug. This also happens with 'final' member in 
Java, and there you expect the value to not be null. A rule of 
thumb should be to never call virtual methods from inside of a 
constructor. Bad things happen.


Re: Reopening the debate about non-nullable-by-default: initialization of member fields

2014-05-03 Thread bearophile via Digitalmars-d

Jonathan M Davis:


Idan Arye:

We are all sick and tired of this debate, but today I've seen a
question in Stack Exchange's Programmers board that raises a
point I don't recall being discussed here:


Yeah, I brought this up before, and it's one of the reasons why 
I'm against non-nullable by default.


I think that problem was raised plenty of times during the 
discussions about non-nullable. And I think this paper (and 
successive ones) solve enough the problems:

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

And aren't the ideas about cooked/uncooked (referred as raw in 
the paper) already partially present in D?


Bye,
bearophile


Re: Reopening the debate about non-nullable-by-default: initialization of member fields

2014-05-02 Thread Jonathan M Davis via Digitalmars-d
On Sat, 03 May 2014 00:50:14 +
Idan Arye via Digitalmars-d  wrote:

> We are all sick and tired of this debate, but today I've seen a
> question in Stack Exchange's Programmers board that raises a
> point I don't recall being discussed here:
>
> http://programmers.stackexchange.com/questions/237749/how-do-languages-with-maybe-types-instead-of-nulls-handle-edge-conditions
>
> Consider the following code:
>
> class Foo{
> void doSomething(){
> }
> }
>
> class Bar{
> Foo foo;
>
> this(Foo foo){
> doSomething();
> this.foo=foo;
> }
>
> void doSomething(){
> foo.doSomething();
> }
> }
>
> Constructing an instance of `Bar`, of course, segfaults when it
> calls `doSomething` that tries to call `foo`'s `doSomething`. The
> non-nullable-by-default should avoid such problems, but in this
> case it doesn't work since we call `doSomething` in the
> constructor, before we initialized `foo`.

Yeah, I brought this up before, and it's one of the reasons why I'm against
non-nullable by default. It means that class references will need to be
treated the same as structs whose init property is disabled, which can be
_very_ limiting. And I don't know if we currently handle structs with
disabled init properties correctly in all cases, since it's not all that
hard for something subtle to have been missed and allow for such a struct
to be used before it was actually initialized (and the fact that not much
code uses them would make it that much more likely that such a bug would
go unnoticed). Hopefully, all those issues have been sorted out by now
though. If so, then I would think that we already have all of the rules in
place for how non-nullable references would be dealt with with regards to
initialization, but they'd still be very limiting, because most of D expects
that all types have an init property.

- Jonathan M Davis


Re: Reopening the debate about non-nullable-by-default: initialization of member fields

2014-05-02 Thread bearophile via Digitalmars-d

Idan Arye:

today I've seen a question in Stack Exchange's Programmers 
board that raises a point I don't recall being discussed here:


This program:

class A {
immutable int x;
this() {
foo();
x = 1;
x = 2;
}
void foo() {
auto y = x;
}
}
void main() {}


Gives:

temp.d(6,9): Error: immutable field 'x' initialized multiple times

So D can tell x is initialized more than 1 time, but it can't 
tell x is initialized 0 times inside foo().


Bye,
bearophile


Reopening the debate about non-nullable-by-default: initialization of member fields

2014-05-02 Thread Idan Arye via Digitalmars-d
We are all sick and tired of this debate, but today I've seen a 
question in Stack Exchange's Programmers board that raises a 
point I don't recall being discussed here:


http://programmers.stackexchange.com/questions/237749/how-do-languages-with-maybe-types-instead-of-nulls-handle-edge-conditions

Consider the following code:

class Foo{
void doSomething(){
}
}

class Bar{
Foo foo;

this(Foo foo){
doSomething();
this.foo=foo;
}

void doSomething(){
foo.doSomething();
}
}

Constructing an instance of `Bar`, of course, segfaults when it 
calls `doSomething` that tries to call `foo`'s `doSomething`. The 
non-nullable-by-default should avoid such problems, but in this 
case it doesn't work since we call `doSomething` in the 
constructor, before we initialized `foo`.


Non-nullable-by-default is usually used in functional languages, 
where the emphasis on immutability requires a syntax that always 
allow initialization at declaration, so they avoid this problem 
elegantly. This is not the case in D - member fields are declared 
at the body of the class or struct and initialized at the 
constructor - separate statements that nothing stops you from 
putting other statements between them.


Of course, D does support initialization at declaration for 
member fields, but this is far from sufficient a solution since 
very often the information required for setting the member field 
resides in the constructor's arguments. In the example, we can't 
really initialize `foo` at the declaration since we are supposed 
to get it's initial value in the constructor's argument `Foo foo`.




I can think of 3 solutions - each with it's own major drawback 
and each with a flaw that prevents it from actually solving the 
problem, but I'll write them here anyways:


1) Using a static analysis that probes into function calls. The 
major drawback is that it'll probably be very hard to implement. 
The reason it won't work is that it won't be able to probe into 
overriding methods, which might use an uninitialized member field 
that the overrided method doesn't use.


2) Disallow calling functions in the constructor before *all* 
non-nullable member fields are initialized(and of course, the 
simple static analysis that prevent usage before initialization 
directly in the constructor code). The major drawback is that 
sometimes you need to call a function in order to initialize the 
member field. The reason is best demonstrated with code:

class Foo{
void doSomething(){
}
}

class Bar{
this{
doSomething();
}

void doSomething(){
}
}

class Baz : Bar{
Foo foo;

this(Foo foo){
this.foo=foo;
}

override void doSomething(){
foo.doSomething();
}
}
`Bar`'s constructor is implicitly called before `Baz`'s 
constructor.


3) Use a Scala-like syntax where the class' body is a constructor 
that all other constructors must call, allowing initialization on 
declaration for member fields in all cases. The major drawback is 
that this is a new syntax that'll have to be used in order to 
have non-nullable member fields - which means it'll break almost 
every existing code that uses classes. Not fun. The reason it 
won't work is that declarations in the struct\class' body are not 
ordered. In Scala, for example, this compiles and breaks with 
null pointer exception when trying to construct `Bar`:

class Foo{
def doSomething(){
}
}

class Bar(foo : Foo){
doSomething();
val m_foo=foo;

def doSomething(){
m_foo.doSomething();
}
}
Also, like the previous two methods, overriding methods breaks 
it's promises.




This issue should be addressed before implementing 
non-nullable-by-default.