January 17, 2014
On Friday, 17 January 2014 at 01:42:38 UTC, Andrei Alexandrescu wrote:
> Walter and I were talking today about the null pointer issue and he had the following idea.
>
> One common idiom to replace null pointer exceptions with milder reproducible errors is the null object pattern, i.e. there is one object that is used in lieu of the null reference to initialize all otherwise uninitialized references. In D that would translate naturally to:
>
> class Widget
> {
>     private int x;
>     private Widget parent;
>     this(int y) { x = y; }
>     ...
>     // Here's the interesting part
>     static Widget init = new Widget(42);
> }
>
> Currently the last line doesn't compile, but we can make it work if the respective constructor is callable during compilation. The compiler will allocate a static buffer for the "new"ed Widget object and will make init point there.
>
> Whenever a Widget is to be default-initialized, it will point to Widget.init (i.e. it won't be null). This beautifully extends the language because currently (with no init definition) Widget.init is null.
>
> So the init Widget will satisfy:
>
> assert(x == 42 && parent is Widget.init);
>
> Further avenues are opened by thinking what happens if e.g. init is private or @disable-d.
>
> Thoughts?
>
>
> Andrei

Throwing out some more ideas, though non of this compiles right now (and I managed to make the compiler ICE).

class Widget
{
	private int x;
	this(int y) { x = y; }
	
	public @property int halfOfX() { return x / 2; }
	
	public void printHello() { std.stdio.writeln("Hello"); }
	
        //Error: 'this' is only defined in non-static member functions,
        //not Widget
	static Widget init = new class Widget
	{	
		@disable public override @property int halfOfX();
		
		@disable public override void printHello();
		
		this() { super(int.init); }
	};
}

Another idea that unfortunately doesn't work due to the way static assert works:

class Widget
{
	private int x;
	this(int y) { x = y; }
	
	public @property int halfOfX() { return x / 2; }
	
	public void printHello() { std.stdio.writeln("Hello"); }
	
	static Widget init = new class Widget
	{	
                //Error: static assert  "Tried to access halfOfX
                //property of null Widget"
		override @property int halfOfX()
                {
                    static assert(false,
                        "Tried to access halfOfX property of null Widget");
        }
		
	override void printHello()
        {
            static assert(false,
                "Tried to access halfOfX property of null Widget");
        }
		
		this() { super(int.init); }
	};
}

Is there any way to get around this?

And then there's this, which causes an ICE:

class Widget
{
	private int x;
	this(int y) { x = y; }
	
	public @property int halfOfX() { return x / 2; }
	
	public void printHello() { std.stdio.writeln("Hello"); }
	
	static Widget init = new class Widget
	{	
		this()
		{
		}
	};
}
January 17, 2014
On Friday, 17 January 2014 at 01:42:38 UTC, Andrei Alexandrescu wrote:
> One common idiom to replace null pointer exceptions with milder reproducible errors is the null object pattern, i.e. there is

Usually null failures are hard to track when some function returned a null value as an indication of error when the programmer expected an exception. In that case early failure is better.

I guess it cold make sense with "weak" pointers if they are wiped by the GC.

Like if you delete a User object, then all "weak" pointers are set to a UnknownUser default object. So that you can call user.isLoggedIn() and get false, but you should be able to default to a different Class to get a different set of virtual calls.

I could also see the use-case for lazy allocation. Basically having memory-cheap "smart" pointers that are checked when you trap zero-page access and automatically alloc/init an empty object before recovering. But the overhead is large.

Not sure if init() is the right way to do it though, because you might need something more flexible. E.g. maybe you want access to the node that contained the offending null-pointer.
January 17, 2014
On Friday, 17 January 2014 at 14:06:59 UTC, Ola Fosheim Grøstad wrote:
> Like if you delete a User object, then all "weak" pointers are set to a UnknownUser default object. So that you can call user.isLoggedIn() and get false, but you should be able to default to a different Class to get a different set of virtual calls.

This particular thing is easy enough to do with null too:

bool isLoggedIn(User user) {
    return user is null ? false : user.isLoggedInImpl();
}

January 17, 2014
On Friday, 17 January 2014 at 14:13:40 UTC, Adam D. Ruppe wrote:
> bool isLoggedIn(User user) {
>     return user is null ? false : user.isLoggedInImpl();
> }

In most cases you can avoid null-related issues if you plan for it, but if you adapt existing software you might not afford to do the changes. Even simple adaption of a framework can cost many weeks of hard work and then you need to keep it in sync with future versions of the framework.

I also think there might be cases where you run the null-test a lot of times with the same result and would be better without it. Like in deep nested data structures where the pointer almost never is null, and then a trap with recovery might be cheaper, amortized.

Sometimes you can write more efficient datastructures if you ban deletion:

Like:
ptr = someobject;
weakcontainer.add(ptr);
...
ptr.setdeleted();
ptr = null;
...
weakptr = weakcontainer.get(id);
if(weakptr.isValid())...;

In this case weakptr would point to the original object before GC, and to the init object after GC. Saving space, but not requiring changes to the weakcontainer implementation.
January 17, 2014
On 2014-01-17 01:42:37 +0000, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> said:

> Further avenues are opened by thinking what happens if e.g. init is private or @disable-d.
> 
> Thoughts?

Some more thoughts.

Couldn't we just add a class declaration attribute that'd say that by default this type is not-null? For instance:

	// references to A are not-null by default
	@notnull class A {}

	// references to B are not-null too (inherited)
	class B : A {}

Then you have two types:

	A  // not-null reference to A.
	A? // nullable reference to A.

Use it like this:

	A a;  // error: cannot be null, default initialization not allowed
	A? a; // default-initialized to null

	void test1(A a) // param a cannot be null
	{
		a.foo(); // fine
	}

	void test2(A? a) // param a can be null
	{
		a.foo(); // error: param a could be null
		if (a)
			a.foo(); // param a cannot be null, allowed
	}

	void test3(A? a) // param a can be null
	{
		A aa = a; // error: param a could be null, can't init to not-null type
		if (a)
			aa = a; // param a cannot be null, allowed
	}

This is basically what everyone would wish for a not-null type to do. The syntax is clean and the control flow forces you to check for null before use. Misuses result in a compile-time error.

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

January 17, 2014
On Friday, 17 January 2014 at 15:05:10 UTC, Michel Fortin wrote:
> On 2014-01-17 01:42:37 +0000, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> said:
>
>> Further avenues are opened by thinking what happens if e.g. init is private or @disable-d.
>> 
>> Thoughts?
>
> Some more thoughts.
>
> Couldn't we just add a class declaration attribute that'd say that by default this type is not-null?

I have been hoping D would become not-null-by-default since I discovered the language.  Looking at what it has done with other type classes, (shared, immutable, etc) it always seemed like something that would be right up its alley.  However in my dream world, it would simply apply to all reference types by default.

I'm not a fan of using an attribute for this due to how hidden it is from the programmer.  You should be able to determine the nullability of something from looking at the code that uses it.  But then again, I feel like there should also be a way to distinguish value types from reference types just by looking at the usage, so what do I know? =p
January 17, 2014
On Fri, Jan 17, 2014 at 12:51:38PM -0000, Regan Heath wrote:
> On Fri, 17 Jan 2014 05:29:05 -0000, H. S. Teoh <hsteoh@quickfur.ath.cx> wrote:
> >Now, if we modify this sentinel to instead record the location of the code that first initialized it (via __FILE__ and __LINE__ default parameters perhaps), then we can set it up to print out this information at a convenient juncture, so that the source of the uninitialized reference can be determined. *Then* perhaps it will be a start of a solution to this issue. (Though it still has limitations in the sense that the problem can only be caught at runtime, whereas some cases of null dereference preferably should be caught at compile-time.)
> 
> So.. if we had a base class for all objects which obtained the file
> and line when created by assignment (from init) and threw on any
> dereference (opDispatch) that would do it, right?
[...]

If Andrei's proposal were extended so that .init can be overridden by a member *function*, then this would work:

	class NullTracker {
		override typeof(this) init(string _file=__FILE__,
						size_t _line = __LINE__)
		{
			class Impl : NullTracker {
				string file;
				size_t line;
				this(string f, size_t l) { file=f; line=l; }

				override void method1() { nullDeref(); }
				override void method2() { nullDeref(); }
				...
				void nullDeref() {
					// N.B.: puts the *source* of the
					// null in the Exception.
					throw new Exception(
						"Null dereference",
						file, line);
				}
			}
			return new Impl(_file, _line);
		}

		void method1() {}
		void method2() {}
		...
	}


T

-- 
People walk. Computers run.
January 17, 2014
On Fri, Jan 17, 2014 at 10:00:26AM -0800, H. S. Teoh wrote: [...]
> If Andrei's proposal were extended so that .init can be overridden by a member *function*, then this would work:
> 
> 	class NullTracker {
> 		override typeof(this) init(string _file=__FILE__,
> 						size_t _line = __LINE__)
> 		{
> 			class Impl : NullTracker {
> 				string file;
> 				size_t line;
> 				this(string f, size_t l) { file=f; line=l; }
> 
> 				override void method1() { nullDeref(); }
> 				override void method2() { nullDeref(); }
> 				...
> 				void nullDeref() {
> 					// N.B.: puts the *source* of the
> 					// null in the Exception.
> 					throw new Exception(
> 						"Null dereference",
> 						file, line);
[...]

P.S. A better message might be "Null dereference, uninitialized object from %s:%d", to distinguish the site of the null dereference vs. its ultimate source.


T

-- 
"How are you doing?" "Doing what?"
January 17, 2014
On 1/17/2014 6:06 AM, "Ola Fosheim Grøstad" <ola.fosheim.grostad+dlang@gmail.com>" wrote:
> On Friday, 17 January 2014 at 01:42:38 UTC, Andrei Alexandrescu wrote:
>> One common idiom to replace null pointer exceptions with milder reproducible
>> errors is the null object pattern, i.e. there is
>
> Usually null failures are hard to track when some function returned a null value
> as an indication of error when the programmer expected an exception.

I've almost never had a problem tracking down the cause of a null pointer. Usually just a few minutes with a debugger and getting a backtrace.

January 17, 2014
On Friday, 17 January 2014 at 19:43:58 UTC, Walter Bright wrote:
> I've almost never had a problem tracking down the cause of a null pointer. Usually just a few minutes with a debugger and getting a backtrace.

It's not always easy to run a debugger on it. I just had an exception (not even a segfault) take two days to track down since, 1) the std.variant exception messages SUCK and 2) after finding where the exception was coming from, it was still hard to find why the input was weird.

A bad value from a third party server (generated with the one specific user's request) got stored and used a bit later.

The problem wasn't where it was used: it was where it came from. And that's what the NotNull seeks to help.