February 18, 2009
On Tue, Feb 17, 2009 at 10:36 PM, Michel Fortin <michel.fortin@michelf.com> wrote:
> On 2009-02-17 18:17:55 -0500, Christopher Wright <dhasenan@gmail.com> said:
>
>> One possible change: implicit casting with an assertion that the nullable value is not null. I'm not sure whether this is a good idea. On the one hand, it's easier for the programmer to use nullable types in that case; on the other, it encourages people not to have error handling.
>
> I think it's a good idea: good enough to be useful, simple enough to be implemented without much hassle. Once we have enough code using non-nullable, it'll be easier to evaluate the impacts of adding compile-time constrains for nullables, and whether it's worth it or not.
>
> I wouldn't make it just like an assertion though. I'd make it something separate you can deactiave with a compiler switch, just like bound checking.

Actually each little debug check has a separate internal flag in the compiler.  It just doesn't expose them all at that level of granularity.  It just lets you turn them all on or all off.  :P
February 18, 2009
Christopher Wright wrote:
> Nick Sabalausky wrote:
>> "Christopher Wright" <dhasenan@gmail.com> wrote in message news:gnfgj6$1484$2@digitalmars.com...
>>> One possible change: implicit casting with an assertion that the nullable value is not null.
>>
>> I can tell right now I wouldn't like that. That would make it far too easy to make mistakes, as it would open up a way for mistakes to circumvent the whole point of having non-nullables. If I accidentially tried to provide a nullable to something that expected a non-nullable, I'd want some sort of up-front notice so that I can either fix it or confirm "yes, I really did mean that" rather than have to hope that I'm lucky enough for the value to actually be null when I test it. An implicit cast should either "just work" with no risk of runtime-error, or be disallowed in favor of something more explicit.
> 
> I think I favor this, actually. If you don't care, you can cast manually. But the implementation difference should be miniscule.

One problem here is static constructors. They're supposed to run in import order, I believe, so if you do this:

module A;
Object o;
static this () { o = new Object; }

module B;
import A;
static this () { writeln(o); }

That should be safe. I think.

Class member variables are also problematic. It's probably going to be annoying to make sure each nullable variable gets initialized in every constructor. I could push that off to runtime, of course, but that isn't so great.

Struct member variables are the killer, unless you have a struct constructor.
February 18, 2009

Christopher Wright wrote:
> One problem here is static constructors. They're supposed to run in import order, I believe, so if you do this:
> 
> module A;
> Object o;
> static this () { o = new Object; }
> 
> module B;
> import A;
> static this () { writeln(o); }
> 
> That should be safe. I think.
> 
> Class member variables are also problematic. It's probably going to be annoying to make sure each nullable variable gets initialized in every constructor. I could push that off to runtime, of course, but that isn't so great.
> 
> Struct member variables are the killer, unless you have a struct constructor.

Mmm... non-nullable types would also be the only type in D that cannot have it's own initialiser assigned to it.

This is actually something I ran across trying to make a non-nullable struct; it can't be done because you can't force a variable to be non-default initialised.

I'm not sure what the solution would be.  A few thoughts:

* Allow types to be flagged, within the compiler, as being "must-set." This includes all non-nullable types AND any aggregates that contain non-nullable types.  If they're local variables, they have to be assigned to at declaration, and if they're members, the have to be assigned to within the constructor.

* Make these types the exception and don't give them an init property. Generic code will either have to test for its absence, fail for non-nullable types, or be written differently.

* Alternately, have an init property which is invalid and can't be assigned.  Probably worse since you can't easily test for it.

* If using the T? syntax to mean "nullable T," generic code could be written to use T? for all classes, then do the conversion to non-nullable at return time.

* What should the following output?

  class T {}

  static if( is( T? : T ) ) pragma(msg, "T? : T");
  static if( is( T : T? ) ) pragma(msg, "T : T?");



  -- Daniel
February 18, 2009
Daniel Keep wrote:
> 
> Christopher Wright wrote:
>> One problem here is static constructors. They're supposed to run in
>> import order, I believe, so if you do this:
>>
>> module A;
>> Object o;
>> static this () { o = new Object; }
>>
>> module B;
>> import A;
>> static this () { writeln(o); }
>>
>> That should be safe. I think.
>>
>> Class member variables are also problematic. It's probably going to be
>> annoying to make sure each nullable variable gets initialized in every
>> constructor. I could push that off to runtime, of course, but that isn't
>> so great.
>>
>> Struct member variables are the killer, unless you have a struct
>> constructor.
> 
> Mmm... non-nullable types would also be the only type in D that cannot
> have it's own initialiser assigned to it.

You could have the compiler use the default constructor to create an instance and assign that to Type.init, but that is evil.

> This is actually something I ran across trying to make a non-nullable
> struct; it can't be done because you can't force a variable to be
> non-default initialised.
> 
> I'm not sure what the solution would be.  A few thoughts:
> 
> * Allow types to be flagged, within the compiler, as being "must-set."
> This includes all non-nullable types AND any aggregates that contain
> non-nullable types.  If they're local variables, they have to be
> assigned to at declaration, and if they're members, the have to be
> assigned to within the constructor.

Determining this is a bit ugly...I think I said that.

> * Make these types the exception and don't give them an init property.
> Generic code will either have to test for its absence, fail for
> non-nullable types, or be written differently.

No init property by default, but you could easily supply one. It wouldn't be CTFE-friendly, though.

> * Alternately, have an init property which is invalid and can't be
> assigned.  Probably worse since you can't easily test for it.

You can; see below. But the test isn't obvious.

> * If using the T? syntax to mean "nullable T," generic code could be
> written to use T? for all classes, then do the conversion to
> non-nullable at return time.

Nullable seems to be a storage class. T? will be inconsistent as such. Additionally, I am certainly not going to implement nullable value types, and T? is the C# syntax for nullable value types, so that is inconsistent.

One advantage of using "nullable T" rather than "T?" is IsExpressions:
static if (is (T == nullable)) ...

The alternative will probably be necessary, as well:
static if (is (T U == nullable U)) ...

> * What should the following output?
> 
>   class T {}
> 
>   static if( is( T? : T ) ) pragma(msg, "T? : T");
>   static if( is( T : T? ) ) pragma(msg, "T : T?");

T is implicitly convertible to nullable(T). There is some debate on whether the opposite should be allowed. So that will either print:
T : T?

or:
T? : T
T : T?
1 2 3 4 5 6 7 8 9
Next ›   Last »