February 14, 2009
Nick Sabalausky wrote:
> The "burning hoop", as you describe it, of checking a nullable var for null before dereferencing is just simply something that the programmer should already be doing anyway. Take the following case:
> 
> void Foo(void delegate()? dg) //nullable
> {
>     dg();
> }

You're right for that small example. Now let's say you have an object a dozen methods referencing the same nullable member. They're private methods called by public methods that check if the member is null first. I don't want to have to check whether the variable is null every single time I use it; I want to do it once at the start and assume (since it's a single-threaded application and I'm not assigning to that member) that that check works for everything.

I'm just fine with getting a segfault when using a nullable variable that's null. That's expected behavior.
February 14, 2009
Denis Koroskin wrote:
> On Sat, 14 Feb 2009 15:42:57 +0300, Yigal Chripun <yigal100@gmail.com>
> wrote:
>
>> Denis Koroskin wrote:
>>> On Sat, 14 Feb 2009 11:39:17 +0300, Nick Sabalausky <a@a.a> wrote:
>>>
>>>> "Michel Fortin" <michel.fortin@michelf.com> wrote in message
>>>> news:gn4pl9$1217$1@digitalmars.com...
>>>>> On 2009-02-13 14:01:57 -0500, "Nick Sabalausky" <a@a.a> said:
>>>>>
>>>>>>> Or use an existing syntax:
>>>>>>>
>>>>>>> Foo? foo = ...;
>>>>>>> if (Foo value = foo) {
>>>>>>> // value is a local variable that is only accessible if foo is not
>>>>>>> null
>>>>>>> } else {
>>>>>>> // value is not accessible here
>>>>>>> }
>>>>>>
>>>>>> I could live with that, but I'd prefer my suggestion because it
>>>>>> wouldn't
>>>>>> require the creation of an extra label for what's essentially the
>>>>>> same
>>>>>> variable. With your code we'd just end up with a whole bunch of:
>>>>>>
>>>>>> Foo? myObj = ...;
>>>>>> if (Foo nonnullMyObj = myObj) //etc...
>>>>>>
>>>>>> // or
>>>>>>
>>>>>> Foo? nullableMyObj = ...;
>>>>>> if (Foo myObj = nullableMyObj) //etc...
>>>>>>
>>>>>> ...Which just seems unnecessary to me.
>>>>>
>>>>> Foo? myObj = ...;
>>>>> if (myObj !is null)
>>>>> {
>>>>> doSomethingWith(myObj);
>>>>> myObj = null; // should this be an error?
>>>>> }
>>>>>
>>>>
>>>> --------
>>>> // This (a more generalized case of above)...
>>>>
>>>> Foo? myObj = ...;
>>>> if(myObj !is null)
>>>> {
>>>> bar1(myObj);
>>>> if(blah1 > blah2)
>>>> myObj = null; // Yes, error
>>>> bar2();
>>>> }
>>>>
>>>> // ...would change to this...
>>>>
>>>> Foo? myObj = ...;
>>>> bool turnToNull=false;
>>>> if(myObj !is null)
>>>> {
>>>> bar1(myObj);
>>>> if(blah1 > blah2)
>>>> turnToNull = true;
>>>> }
>>>> if(turnToNull)
>>>> {
>>>> myObj = null;
>>>> bar2();
>>>> }
>>>> --------
>>>>
>>>>> And what about:
>>>>>
>>>>> Foo? myObj = ...;
>>>>> while (myObj !is null)
>>>>> myObj = myObj.next;
>>>>>
>>>>
>>>> --------
>>>> Foo? myObj = ...;
>>>> while(true)
>>>> {
>>>> if(myObj.next !is null)
>>>
>>> Dang! NullPointerException, because you never checked myObj against null
>>>
>>>> myObj = myObj.next;
>>>> else
>>>> break;
>>>> }
>>>> myObj = null; //this line optional
>>>>
>>>> // Or, if you don't like "while(true)",
>>>> // you could use a "bool done" flag.
>>>> --------
>>>>
>>>> With Denis's syntax, I'm not sure how the second one would be possible,
>>>
>>> Foo? myObj = ...;
>>> if (Foo obj = myObj) {
>>> while (true) {
>>> if (auto next = obj.next) {
>>> obj = next;
>>> } else {
>>> break;
>>> }
>>> }
>>> }
>>>
>>>> but
>>>> the first one would certainly be nicer. So I'm tempted to say that both
>>>> syntaxes should be allowed...
>>>>
>>>> However, you do have a valid point that my syntax has a problem
>>>> whenever
>>>> assigning a possibly-null value to the original nullable variable from
>>>> within the scope of the null-check. It can definitely be worked
>>>> around, but
>>>> I am coming to realize that maybe removing a variable's nullability
>>>> shouldn't happen implicitly unless it can also be added back
>>>> implicity -
>>>> which would require flow analysis.
>>>>
>>>> Between that and the difficultly of Denis's syntax handling that simple
>>>> while loop example, I guess full-blown flow-analysis would be needed
>>>> after
>>>> all in order to realistically eliminate null reference errors on
>>>> nullable
>>>> types. Dang!
>>>>
>>>
>>> I don't think code flow analysis the way you suggest is useful. I
>>> certainly don't want my variable types to be changed at some point after
>>> null-check.
>>>
>>> Code flow analysis would be nice to have, but in a different way:
>>>
>>> Foo b; // not initialized, can not be read yet
>>>
>>> if (condition) {
>>> b = createFoo(42);
>>> } else if (otherCondition) {
>>> b = new Foo();
>>> } else {
>>> // return b; // error, b is write only
>>> return;
>>> }
>>>
>>> // b is both read/write accessible at this point
>>>
>>>
>>
>> can't this be done with:
>>
>> Foo b = condition ? createFoo(42) :
>> (otherCondition ? new Foo() : return);
>> // use b
>
> It solves the simplest examples, but not more complex ones, so it is not
> generic enough.
>

In what way it's not generic enough?
IMO, this is not a matter of flow analysis at all, but rather a matter of if statement syntax.
IMO "if" needs to be expression instead of a statement.
also See http://nemerle.org/Quick_Guide#Decisions
February 14, 2009
On Sat, Feb 14, 2009 at 8:37 AM, Christopher Wright <dhasenan@gmail.com> wrote:
>> void Foo(void delegate()? dg) //nullable
>> {
>>    dg();
>> }
>
> You're right for that small example. Now let's say you have an object a dozen methods referencing the same nullable member. They're private methods called by public methods that check if the member is null first. I don't want to have to check whether the variable is null every single time I use it; I want to do it once at the start and assume (since it's a single-threaded application and I'm not assigning to that member) that that check works for everything.

....that's the entire point of non-null references.

class Foo
{
    private void delegate() dg; // non-null

    public this(void delegate()? dg) // nullable
    {
        if(dg !is null)
            this.dg = dg; // hey, dg must be non-null!
        else
            throw new Exception("NO! *SLAM*");
    }

    private void method1()
    {
        // use dg here, and you're assured that it's non-null
        // all methods are guaranteed the same thing
    }
}

Why on earth would you rather use a nullable reference when you KNOW that it should never be null?
February 14, 2009
Yigal Chripun:
> IMO "if" needs to be expression instead of a statement. also See http://nemerle.org/Quick_Guide#Decisions

If you want to turn D into a more functional language, then that may be positive. But I think D will not change so much, so better for you to stick to things that are more likely to change.

Bye,
bearophile
February 14, 2009
On 2009-02-14 08:37:04 -0500, Christopher Wright <dhasenan@gmail.com> said:

> I'm just fine with getting a segfault when using a nullable variable that's null. That's expected behavior.

I leaning to think it'd be best to have non-nullable pointers that just throw an exception when you attempt to assign null to them.

That way you can have implicit conversions from nullable to non-nullable pointers where the compiler would add a check for null and throw; you no longer have to check for null for arguments that shouldn't be null (declaring the argument as non-nullable would force the caller to make sure it is not null, which cost nothing if the caller is passing a non-nullable variable); and you can catch incorrect assignment of null to non-nullable pointer before the assignment itself (instead of detecting it later it's too late).

Non-nullable should still be the default because it's faster (no check for null) and safer to use, and I'd trust the optimiser to remove unnecessary checks for null when nullable pointers are casted to non-nullable ones (when you check yourself before casting for instance).

-- 
Michel Fortin
michel.fortin@michelf.com
http://michelf.com/

February 14, 2009
Jarrett Billingsley wrote:
> On Sat, Feb 14, 2009 at 8:37 AM, Christopher Wright <dhasenan@gmail.com> wrote:
>>> void Foo(void delegate()? dg) //nullable
>>> {
>>>    dg();
>>> }
>> You're right for that small example. Now let's say you have an object a
>> dozen methods referencing the same nullable member. They're private methods
>> called by public methods that check if the member is null first. I don't
>> want to have to check whether the variable is null every single time I use
>> it; I want to do it once at the start and assume (since it's a
>> single-threaded application and I'm not assigning to that member) that that
>> check works for everything.
> 
> ....that's the entire point of non-null references.

No. I'm saying this class has a complex code path if some things are not null, and some other code path if they are null. It's quite valid if they are null, but I want to do other things if they aren't, and I don't want to cast constantly.
February 14, 2009
"bearophile" <bearophileHUGS@lycos.com> wrote in message news:gn6ism$1rgc$1@digitalmars.com...
> Yigal Chripun:
>> IMO "if" needs to be expression instead of a statement. also See http://nemerle.org/Quick_Guide#Decisions
>
> If you want to turn D into a more functional language, then that may be positive. But I think D will not change so much, so better for you to stick to things that are more likely to change.
>

D's already becoming more functional. Evidence: pure, immutable, std.algorithm.


February 14, 2009
Nick Sabalausky wrote:
> "bearophile" <bearophileHUGS@lycos.com> wrote in message news:gn6ism$1rgc$1@digitalmars.com...
>> Yigal Chripun:
>>> IMO "if" needs to be expression instead of a statement.
>>> also See http://nemerle.org/Quick_Guide#Decisions
>> If you want to turn D into a more functional language, then that may be positive. But I think D will not change so much, so better for you to stick to things that are more likely to change.
>>
> 
> D's already becoming more functional. Evidence: pure, immutable, std.algorithm. 

...std.functional.

Andrei
February 14, 2009
"Denis Koroskin" <2korden@gmail.com> wrote in message news:op.upcaojexo7cclz@korden-pc...
> On Sat, 14 Feb 2009 11:39:17 +0300, Nick Sabalausky <a@a.a> wrote:
>
>> "Michel Fortin" <michel.fortin@michelf.com> wrote in message news:gn4pl9$1217$1@digitalmars.com...
>>> On 2009-02-13 14:01:57 -0500, "Nick Sabalausky" <a@a.a> said:
>>>
>>>>> Or use an existing syntax:
>>>>>
>>>>> Foo? foo = ...;
>>>>> if (Foo value = foo) {
>>>>> // value is a local variable that is only accessible if foo is not
>>>>> null
>>>>> } else {
>>>>> // value is not accessible here
>>>>> }
>>>>
>>>> I could live with that, but I'd prefer my suggestion because it
>>>> wouldn't
>>>> require the creation of an extra label for what's essentially the same
>>>> variable. With your code we'd just end up with a whole bunch of:
>>>>
>>>> Foo? myObj = ...;
>>>> if (Foo nonnullMyObj = myObj) //etc...
>>>>
>>>> // or
>>>>
>>>> Foo? nullableMyObj = ...;
>>>> if (Foo myObj = nullableMyObj) //etc...
>>>>
>>>> ...Which just seems unnecessary to me.
>>>
>>> Foo? myObj = ...;
>>> if (myObj !is null)
>>> {
>>>     doSomethingWith(myObj);
>>>     myObj = null; // should this be an error?
>>> }
>>>
>>
>> --------
>> // This (a more generalized case of above)...
>>
>> Foo? myObj = ...;
>> if(myObj !is null)
>> {
>>     bar1(myObj);
>>     if(blah1 > blah2)
>>         myObj = null; // Yes, error
>>     bar2();
>> }
>>
>> // ...would change to this...
>>
>> Foo? myObj = ...;
>> bool turnToNull=false;
>> if(myObj !is null)
>> {
>>     bar1(myObj);
>>     if(blah1 > blah2)
>>         turnToNull = true;
>> }
>> if(turnToNull)
>> {
>>     myObj = null;
>>     bar2();
>> }
>> --------
>>
>>> And what about:
>>>
>>> Foo? myObj = ...;
>>> while (myObj !is null)
>>>     myObj = myObj.next;
>>>
>>
>> --------
>> Foo? myObj = ...;
>> while(true)
>> {
>>     if(myObj.next !is null)
>
> Dang! NullPointerException, because you never checked myObj against null
>
>>         myObj = myObj.next;
>>     else
>>         break;
>> }
>> myObj = null; //this line optional
>>
>> // Or, if you don't like "while(true)",
>> // you could use a "bool done" flag.
>> --------
>>

I blame fatigue ;)

--------
Foo? myObj = ...;
if(myObj !is null)
{
    while(true)
    {
        if(myObj.next !is null)
            myObj = myObj.next;
        else
            break;
    }
}
myObj = null; //this line optional
--------

>
> I don't think code flow analysis the way you suggest is useful. I certainly don't want my variable types to be changed at some point after null-check.
>

I'm not sure if we're on the same page here. What I was thinking about code flow analysis was this:

Foo? maybeGetObj() {...}
void takesAFoo(Foo f) {...}

Foo? f = maybeGetObj(); // Ok, f *is* permanently "Foo?"
f.mFunc(); // Illegal, might be null
takesAFoo(f); // Illegal, might be null

// Tells compiler f is now implicitly convertable to "Foo"
if(f !is null)
{
    f.mFunc(); // Ok
    takesAFoo(f); // Ok

    // With flow-analysis, this is ok and tells
    // compiler f is no longer implicitly
    // convertable to "Foo"
    f = maybeGetObj();
    f.mFunc(); // Illegal
    takesAFoo(f); // Illegal

    f = new Foo(); // now implicitly convertable to "Foo"
    f.mFunc(); // Ok
    takesAFoo(f); // Ok

    // Ok, but possible warning that the condition's
    // result will always be the same (true in this case).
    if(f !is null) {...}

    f = null; // no longer implicitly convertable to "Foo"
}
else // f is still *not* implicitly convertable to "Foo"
{
    f = new Foo();  // now implicitly convertable to "Foo"
}
// Depending which branch of the if was taken,
// f might be implicitly convertable to "Foo",
// or it might not be. So, from here, assume
// that it *isn't* implicitly convertable to "Foo".

Foo f2=...; // Ok, f2 *is* permanently "Foo"

 // Illegal, f2 *is* "Foo"
f2 = null;

// Error, comparison of incompatible types
if(f2 !is null) {...}

> Code flow analysis would be nice to have, but in a different way:
>
> Foo b; // not initialized, can not be read yet
>
> if (condition) {
>    b = createFoo(42);
> } else if (otherCondition) {
>    b = new Foo();
> } else {
>    // return b; // error, b is write only
>    return;
> }
>
> // b is both read/write accessible at this point
>

Agreed, that would be nice to have.


February 14, 2009
"Andrei Alexandrescu" <SeeWebsiteForEmail@erdani.org> wrote in message news:gn7447$51u$1@digitalmars.com...
> Nick Sabalausky wrote:
>> "bearophile" <bearophileHUGS@lycos.com> wrote in message news:gn6ism$1rgc$1@digitalmars.com...
>>> Yigal Chripun:
>>>> IMO "if" needs to be expression instead of a statement. also See http://nemerle.org/Quick_Guide#Decisions
>>> If you want to turn D into a more functional language, then that may be positive. But I think D will not change so much, so better for you to stick to things that are more likely to change.
>>>
>>
>> D's already becoming more functional. Evidence: pure, immutable, std.algorithm.
>
> ...std.functional.
>
> Andrei

Sorry, I'm still in D1-land ;-)