January 17, 2014 Non-null objects, the Null Object pattern, and T.init | ||||
---|---|---|---|---|
| ||||
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 |
January 17, 2014 Re: Non-null objects, the Null Object pattern, and T.init | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | On Friday, 17 January 2014 at 01:42:38 UTC, Andrei Alexandrescu wrote:
> static Widget init = new Widget(42);
The problem here is if it is in a mutable data segment:
Widget i; // implicitly set to init
assert(i is Widget.init); // uh oh
i.mutate();
Widget i2; // set to widget init...
assert(i is i2); // oh dear
// i2 now reflects the mutation done above!
And if the init is put in a read-only segment similar to a string buffer, we still get a segmentation fault when we try to mutate it.
I'm not necessarily against having the option, but I think it would generally be more trouble than it is worth. If you have an object that you want to guarantee is not null, make the nullable implementation
class Widget_impl {}
then define
alias Widget = NotNull!Widget_impl;
and force initialization that way.
|
January 17, 2014 Re: Non-null objects, the Null Object pattern, and T.init | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | 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
I am assuming init will be a property static function. So essentially we would be removing .init support for classes in compiler and pushing it out into Object.
I would rather if that is an option an init static function, that it would be required to have override otherwise it would be too 'magical' for me atleast.
The default can still be null.
|
January 17, 2014 Re: Non-null objects, the Null Object pattern, and T.init | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | On Friday, 17 January 2014 at 01:42:38 UTC, Andrei Alexandrescu wrote: > 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. Hmm, what about derived classes? How do you check for a valid Widget given a DerivedWidget.init? class DerivedWidget : Widget { static DerivedWidget init = new DerivedWidget(...); } bool valid(Widget w) { return w !is Widget.init; } DerivedWidget foo; assert(!valid(foo)); // doesn't fire, foo is valid? The nice thing about null is that it is the bottom type, so it is a universal sentinel value. Also, what about interfaces? You cannot define an init for an interface. Obviously that could just be a known and accepted limitation of this proposal, but I'm not a huge fan of solutions that only work in a subset of situations. Perhaps there is a solution that I haven't thought of. > assert(x == 42 && parent is Widget.init); Is that meant to say "x is Widget.init"? |
January 17, 2014 Re: Non-null objects, the Null Object pattern, and T.init | ||||
---|---|---|---|---|
| ||||
Posted in reply to Adam D. Ruppe | On 1/16/14 5:53 PM, Adam D. Ruppe wrote: > On Friday, 17 January 2014 at 01:42:38 UTC, Andrei Alexandrescu wrote: >> static Widget init = new Widget(42); > > The problem here is if it is in a mutable data segment: > > Widget i; // implicitly set to init > assert(i is Widget.init); // uh oh > i.mutate(); > > Widget i2; // set to widget init... > assert(i is i2); // oh dear > // i2 now reflects the mutation done above! Yah, that would be expected. > I'm not necessarily against having the option, but I think it would > generally be more trouble than it is worth. If you have an object that > you want to guarantee is not null, make the nullable implementation > > class Widget_impl {} > > then define > > alias Widget = NotNull!Widget_impl; > > and force initialization that way. Noted. Andrei |
January 17, 2014 Re: Non-null objects, the Null Object pattern, and T.init | ||||
---|---|---|---|---|
| ||||
Posted in reply to Rikki Cattermole | On Friday, 17 January 2014 at 01:57:24 UTC, Rikki Cattermole wrote:
> I am assuming init will be a property static function. So essentially we would be removing .init support for classes in compiler and pushing it out into Object.
I don't think a function would work without major work since that would mean adding non-blit* default construction to the language - which isn't present at all right now.
|
January 17, 2014 Re: Non-null objects, the Null Object pattern, and T.init | ||||
---|---|---|---|---|
| ||||
Posted in reply to Rikki Cattermole | On 1/16/14 5:57 PM, Rikki Cattermole wrote: > I am assuming init will be a property static function. In the example above it's just a static data member. > So essentially we > would be removing .init support for classes in compiler and pushing it > out into Object. > > I would rather if that is an option an init static function, that it > would be required to have override otherwise it would be too 'magical' > for me atleast. > The default can still be null. Override only works for nonstatic methods. Andrei |
January 17, 2014 Re: Non-null objects, the Null Object pattern, and T.init | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | 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
Most object don't have a sensible init value. That is just hiding
the problem under the carpet.
|
January 17, 2014 Re: Non-null objects, the Null Object pattern, and T.init | ||||
---|---|---|---|---|
| ||||
Posted in reply to Peter Alexander | On 1/16/14 6:02 PM, Peter Alexander wrote: > On Friday, 17 January 2014 at 01:42:38 UTC, Andrei Alexandrescu wrote: >> 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. > > Hmm, what about derived classes? How do you check for a valid Widget > given a DerivedWidget.init? > > class DerivedWidget : Widget > { > static DerivedWidget init = new DerivedWidget(...); > } > > bool valid(Widget w) { return w !is Widget.init; } > > DerivedWidget foo; > assert(!valid(foo)); // doesn't fire, foo is valid? > > > The nice thing about null is that it is the bottom type, so it is a > universal sentinel value. This is a good point. > Also, what about interfaces? You cannot define an init for an interface. > Obviously that could just be a known and accepted limitation of this > proposal, but I'm not a huge fan of solutions that only work in a subset > of situations. Perhaps there is a solution that I haven't thought of. > > >> assert(x == 42 && parent is Widget.init); > > Is that meant to say "x is Widget.init"? To clarify: assert(Widget.init.x == 42 && Widget.init.parent is Widget.init); Andrei |
January 17, 2014 Re: Non-null objects, the Null Object pattern, and T.init | ||||
---|---|---|---|---|
| ||||
Posted in reply to deadalnix | On Friday, 17 January 2014 at 02:07:08 UTC, deadalnix wrote:
> Most object don't have a sensible init value. That is just hiding
> the problem under the carpet.
That's true.
class Widget
{
Widget parent;
static Widget init = ???
}
How do you define init?
|
Copyright © 1999-2021 by the D Language Foundation