This project is read-only.

Overridable Default Values For Null?

Topics: C# Language Design, VB Language Design
Aug 8, 2014 at 11:30 AM
Edited Aug 8, 2014 at 11:48 AM
Separate discussion of a topic raised in Non Nullable Reference types discussion thread.

note
In some of the code examples the type appears after the identifier this is so the concept or idea is more prominent rather than the actual syntax used. As this is subject the programming language used.

My default goto programming language to do "back of the paper" coding is vb.net, but I can code in other languages and styles. I learnt programming in 1980s and the language available (to me) at the time where predominantly in the identifier followed by type
So using C style languages (type identifier ) can break my flow of consciousness, to correct syntax and grammar error. Exception is Nemerle which uses identifier type

Overridable Default Values For Null?

With the proposed Non-Null reference types, null is in essence an illegal value for a reference. So I propose that you should be able to define a "default" overload for null in non-null reference type. If null is assign to a non-null ( T! ) the overload instance is used instead.

Type Bang vs Nonnull prefix

Type Bang
A postfix identifier ! after the name of the class

c#
class person!
{
}
vb.net
Public Class  Foo!

End Class
Could be easily missed when scanning through code. In the vb.net example it doesn't look very vb-like. I think this style is OK for type signature of methods and variables though.

nonnull prefix
A prefix to the class declaration would indicate the the class in non-null.

c#
nonnull class person
{

}
vb.net
Public NonNull Class  Foo

End Class
Using a word instead makes the intent clearer when scanning.
In both example it doesn't look to bad in term of the style.

Default Parameter-Less Constructor ?

Should a non-null be required to supply a parameter less constructor.
nonnull class Example
{
  Example()  /* explicitly required */
  {
  }
Doesn't convey that is used in the case of assigning null.

Object base object modified

Extend or modify to base of all .net objects object to have an overridable method. That is called when assigning null to a non-null.
Base Object
{
  overridable Object! Default() 
  {
    return default<Object>
    /* null for class types */
  {
}
Special cased in the compiler(s) to allow null for the base objects of class and structure.
When the default is subsequently overloaded that return valued must be a non-null instance.
Class Person
{
  ...
  override default Person! () 
  {
    return New Person();
  }
}

Person! p0 = null ; /* --> Person! = Person!.default */
Person! p1 = default<person!> ; /* Person!.default  */
This would result in a new instance of Person! being created for each null / default

The null default overload could be a shared / static instance.
Class Person
{
  ...

  static private Person! _defaultInstance = New Person
  override default Person! () 
  {
    return _default
  }
}
Person! p0 = null ; /* --> Person!.default --> Person!._defaultInstance_ */;
Person! p1 = default<person!> ; /* Person!.default --> Person!._defaultInstance_ */
This permits the class not to have a default constructor.


If the non-null class is used as a base then it would be possible to return an instance that inherits / derives from that type. A Null Object as in the Null Object Pattern
base nonnull MyObj
{
  overrides default () MyObject!
  {
   return New NullObject
  {
}

nonnull class NullObject
  inherits MyObj
{
}

Extension Method

What if the type is sealed / not inheritable and you need a non-null version?
Suppose we allow extension methods.
static class Null_Exts
{
  <extension>
  static string! default() 
  {
  }

}
You could the localise nonnull - ability on a file level.
...
using 
{
 imports Null_Exts
 ...

}

Structures

You could also potential return a non-null (0 in most cases) for the default value of a value (structure) type.
/* example */

Optional Non-Null Parameters

What about in the case of optional parameters where the parameter types is non null?
public method ( arg : T! = null  )
public method ( arg : T! = default )
 
/* args has the value of T!.default  in both examples*/
Another option is to allow an arbitrary expression so longs as it return a T!
public method ( arg : T! = expr< T! > ) ./^

Option Null_Coalesce False / True

option Null_Coalesce False
Assigning null to a non-null will result in null reference exception
option Null_Coalesce True 
Assigning null to a non-null will result in the null being coalesced to the default null overload value. If this has not been overloaded then some form for exception is thrown.
Aug 8, 2014 at 4:36 PM
richardtallent wrote:
Olmo wrote:

I thought you wanted to move this to another thread? ;)
  • Optional parameters/overloads/field initializer
While there is a way to default when an argument is not provided, there is no convenient way to assign a default value when an argument is provided but is null.

Also, the defaults for optional arguments are currently limited to literals because of the way they are currently implemented (the caller still provides all arguments under the hood). This approach would allow the caller to send nulls, and the method to replace the nulls automatically -- which means the default can be expressions that aren't currently supported, such as constants or enum values.

This is really a different situation than the others (and different means of implementation that is frankly a lot simpler), so maybe it should have its own thread too.
This (different) idea has some value, but how will the caller differentiate a default null from a explicit null?
  • Not many API will be benefited by default strings of "" (10%?). Most of the time the name has to have some meaning or be null.
String being nullable is just an anomaly of .NET's underlying design (stack vs. heap etc. etc.), it is no more "right" for a string to be nullable semantically than a Date (and arguably, dates have a stronger case for needing null values).

In the work I do (focused on intranet business applications), optional string fields are very common, and it doesn't make a damned bit of difference whether it is a null or an empty string, though the latter is often more convenient.
In fact, I can't think of a single place in any of my active code base where a string storing a null means anything different semantically than an empty string. I suspect I'm not alone -- otherwise, String.IsNullOrEmpty() would be rarely used rather than being recommended by Microsoft. So I'm gonna go with 90%. YMMV.
Agree, we all tend to put them together, mainly because there's no way for a user to differentiate "" and null in a text box.

All legitimate "" can be replaced by null, but that doesn't mean that all the nulls are "". Most of the times that I get a null exception I you realize that I forgot to set the name, not that the name should be "", it will take longer to realize if they weren't null.
  • Collection could be happy with an empty collection, like 50% accuracy for List and Dictionaries, and just 5% for arrays.
I would go higher. As with string, an "empty" collection is for many use cases semantically the same thing as one that has not yet been instantiated. The reasons these are implemented as a reference type in .NET have nothing to do with needing to be nullable. They are essentially arrays on steroids.
  • Normal reference types like Person, ComboBox, Windows, HttpContext, etc... 0%
I disagree. Your focus on user accounts and UI controls just isn't representative of the universe of data manipulated in .NET. But I'm willing to concede it's a much smaller surface area, and where it occurs, it's probably because the type is "collection-like" or "value-like" in purpose -- for example, a group of properties that for one reason or another can't be implemented as structs, but for which nullability is not meaningful or particularly useful.

Besides, this whole notion of "accuracy" is specious -- I'm not suggesting that anything happen in terms of defaulting unless the developer asked for it using an attribute, wrapping it in a struct like Nullable<T> is implemented, or some other explicit way. Just like you want "!" -- as something you explicitly turn on to avoid boilerplate code.
Ok, I was assuming there's one global default value for each type. So the idea is that there are parameters/properties that, when assigned null, get a default value instead. And this default value is set in an attribute.
So pretty much this could work for collections, but the idea of the default instance, in order to be efficient, will require that is a shared object.
Not at all. No one is creating new instances that wouldn't have been created before -- they just want to create them implicitly when an attempt is made to access their reference and it is currently set to null. Perhaps what you're saying would be the case for Adam's implementation concept, but there's more than one way to skin the cat. Personally, I think just coalescing to the result of the default constructor is good enough (with System.String modified to have a default constructor that returns an empty string).

Note that I'm not in favor of default values on public or protected Fields, so it should be possible to do all of this without creating hordes of zombies, changing the runtime, etc. It's all syntactic sugar that does a hat trick when the reference is accessed.
If, on the other side, a new List<T> is created every time a List<T> reference is created that will make the GC really busy, for example:
  • You create a Dictionary<string, List<ComboBox>>, let's suppose the Dictionary is created with lie 4 buckets with 4 elements each, 16 lists!.
  • Each of the lists has an initial array of 5 values..
  • Now you have create 80 absolutely worthless zombie ComboBox instances.
I've already mentioned that common composed collections (such as dictionaries that can store multiple values) should be implemented separately, so the semantics make sense. It's silly to have to manage lists within dictionaries as a workaround, all null issues aside.

And with collections in general, there's a lot of distance between the collection being set so it can be seen as empty but not as null, and the elements of the collection having the same behavior.

The primary method I'm promoting (code transformation to add coalescing on assignment) would NOT impact the contents of the list, only what actually goes into your own variable when you pull a null value from the list. This means the "new" value would act like a string does now -- changing it wouldn't update anything about the null that is still sitting in the list. That is a concern of mine with this approach, but it's not insurmountable.
How calling Add on a list will coallesce? the list won't have such attribute.

I will need a more ordered proposal to stop making false assumptions.
Aug 8, 2014 at 7:58 PM
Edited Aug 9, 2014 at 11:46 PM
richardtallent wrote:
This (different) idea has some value, but how will the caller differentiate a default null from a explicit null?
Its a non-null type it can't have null as a legal value. If you require / expect nullable reference use T (don't override default).
It could still use a reference equality to see if the reference is pointing a the default value.
If x Is T.Default Then
This will likely require a shared instance though.

You can think as the existing defaults like
(T : Struct) x = default<T> /* x = 0   */
(T : Class)  x = default<T> /* x = null */
In my proposal those default do not change. So is still backwards compatible.
It does give the developer option to use a default if they choose to. They could default to some for NullObject

Let's say that Nullable is implemented as a nonnull.
Struct Nullable<T>
{
  overrides default () Nullable<T : Struct >
  {
    return New Nullable<T>() ;  /* .HasValue = False /*
  }
}
Nullable<T> x = null ; /* Nullable<T>.Default */
var    x = null /* error */
object x = null /* error */
    T  x = null /* null */
    T! x = null /*  T.default */
/* this time the T doesn't override default */
   T! = null; /* Error: T doesn't implement a non-null default instance. */
public method_A ( T  x ) { ... }
public method_B ( T! x ) { ... }
/* calling methods with null */
method_A ( null )  /* parameter x has the value null */
T! x = null;
method_B ( xl ) /* parameter x uses the default value of the type T! */

It essentially equivalent to doing.
method_B( If( x , T.Default ) ) 
or
Dim x = CType( Null , T).OnNull( T.Default )

<extension()>Function OnNull( Of T As Class )( objT As T , defaultTo As T! ) As T!
  If objT Is Nothing Then Return defaultTo
  Return objT
End Function

How calling Add on a list will coallesce? the list won't have such attribute.
It'll only coalesce if the content type of the list is a nonnull-able type.
var mylist = new List< T >
mylist.Add(  null ) /*  a new null item added to the list  */

var mylist = new List<T!>
mylist.Add( null ) /* Will add a T.default to the list */

richardtallent wrote:
Most of the times that I get a null exception I you realize that I forgot to set the name
Or have the object implement some means or indication that the object is in a valid/invalid state.
Like you'd have a dirty flag to indicate that something on the data input form has changed.
class person
{
  _IsValid : Boolean = False
  _Name : String
  _DoB : Date
  _DoD : Date?

  public IsValid () : Boolean
  {
    get: { return _IsValid }
  }

  public Name () : String
  {
    get: { return _Name }
    set (value : string )
    {
      _Name = value
     Validate
    } 
    private Validate()
    {
      _IsValid = (_Name isnot Null ) && ( !_DoD.HasValue || ( _DoD.HasValue && _DoD.Value >= _DoB ) ) 
    }
}


Aug 9, 2014 at 8:22 AM
Olmo wrote:
This (different) idea has some value, but how will the caller differentiate a default null from a explicit null?
The caller would see the optional parameter defaulted to null, so where an argument is not provided in the case, it would pass null.

The compiler of the method (not of the caller) would coalesce the parameter to the true default just before that parameter is is accessed the first time, essentially turning this:
public int Search( SearchOptions options =?? new SearchOptions() { timeout = 15, orderby = "id"}) {
   // do stuff that doesn't use "options"...
   db.SetTimeOut(options.timeout);   // first attempt to access options
}
into this:
public int Search( SearchOptions options = null) {
   // do stuff that doesn't use "options"...
   options = options ?? new SearchOptions() { timeout = 15, orderby = "" };
   db.SetTimeOut(options.timeout);   // first attempt to access options
}
One of the annoying things about optional parameters currently is that the default has to be a compile-time constant... no complex expressions or function calls. For example, you can't set the default of a DateTime parameter as DateTime.Now.

This solution fixes the problem for reference types, but not for value type parameters like DateTime. To provide a default other than the "natural" default value, I'm pretty sure you'd have to use a nullable type:
public int Foo( DateTime? d = ?? DateTime.Now );
A bit ugly. Perhaps the ? could be implied by the ?? for value types. Still, it would be very useful. (In this case, again, it's Foo() that would end up calling DateTime.Now -- the caller would just pass null if there is no argument).
All legitimate "" can be replaced by null, but that doesn't mean that all the nulls are "".
Probably true. This would just give the developer the option to have a particular string act more like a value type and default to empty to avoid silly errors and boilerplate code where there should be no distinction.
Ok, I was assuming there's one global default value for each type.
That seems to be more aligned with Adam's concept. If that were the direction to go, I'd prefer an implicit default based on the default constructor (more below).
So the idea is that there are parameters/properties that, when assigned null, get a default value instead. And this default value is set in an attribute.
Attribute wouldn't be my preference. Here's how I think the intended default value could be explicitly conveyed to the compiler:
// optional parameters with auto coalesce, working around the normal limitation that optional parameter defaults can only be literal values
public int Search(SearchOptions options =?? new SearchOptions { timeout = 15, orderby = "" })

// auto-properties, extending the new initializer syntax
public string TimeOutMessage { get; set; } = ?? "The query timed out.";

// Private, internal, and possibly protected private fields -- could use an attribute too but I'm kinda digging using the same syntax.
private string _TimeOutMessage = ?? "The query timed out.";
For regular properties, I think there is no need for this functionality, it should just be a coalesce somewhere in the getter or setter, as appropriate. Public fields could not support this without converting them to properties, and I think that's appropriate -- auto-properties are too easy to use to not do so.

The other option would be to implicitly convey the default value by just having the compiler insert a call to the default constructor. It stands to reason that anything you'd want to default this way should have a default constructor that returns an identically-configured object, so this is a clean way of doing the work without creating magic method names or changing how the runtime deals with Default(T) for reference types (as Adam suggests). Here's a possible syntax for implicit defaults:
public int Search(SearchOptions options =??) {}      // compiler would coalesce "options" to "new SearchOptions()."
public string TimeOutMessage { get; set; } = ??;       // compiler would coalesce the backing field to "" when the getter is accessed.
private string _TimeOutMessage =??;       // compiler would coalesce _TimeOutMessage to "" whenever it is accessed.
While more terse, there are two things I don't like about the implicit version of this:
  • String.New() would need to be rewritten to return String.Empty, since strings are a very likely use case. Possible breaking change.
  • It's not nearly as flexible -- too much power is given to the constructor to decide what the default should be.
One advantage of either is that the assignment to the default only happens just before access -- i.e., just in time, which isn't the case for the common practice of doing all of your argument-checking/defaulting and instance variable initializations at the top of the method.
How calling Add on a list will coallesce? the list won't have such attribute.
I'm not proposing that List<T> should be configurable to default its T members to anything. Such a feat would, as you suggest, require injecting new functionality into the collection. This seems highly impractical. If you wanted automatic defaults for values inside a collection, it would be best to just implement a new collection type that wraps a traditional collection and applies the most sensible logic.

What you could do:
  • Have a List<T> variable that is defaulted to a new List<> instance if it is accessed while null.
  • Have an instance variable that is defaulted when you pluck nulls from the List. The list itself would still have a null. Caveat -- this would only work as expected for immutable types, i.e., where changes to your instance variable aren't expected to be propagated back into the list.
Aug 9, 2014 at 10:13 PM
In most Structure based objects null is interchangeable with Zero (0). So comparing against null is a misuse of the value of null.
C++ suffers a from this problem, since null is defined to be the integer value of Zero ( null = 0 ). C++ 14 introduced NullPtr<T> (Link: Ch9 Video )
public method < T : Structure > ( optional arg : T = null ) 
public method < T : Structure > ( optional arg : T = 0    )
In this proposal null in this context is replaced with a call to the objects default.
public method < T : Structure > ( optional arg : T = T.default )
My proposal does also permit a different default for a structure based object.
For example:
structure DateTime 
{
...
  overrides default () : DateTime
  {
    return DateTime.Now; 
  }
}
It also separates default constructor from default value
class String
{
  /* default constructor (parameterless)*/
  public String()   {  ...   }
  /* default value for null */
  overrides default () : String!
  {
   return String.Empty
  }
}
Aug 10, 2014 at 1:19 AM
How would a collection of nonnull reference types be initialized with a value in each item?
T![] xs = new T![10];

T! Q = xs[ 2 ]; /* What what is the content ? */
Within the context of this proposal.
T! Q = xs[2] ; 
 /* On first access xs]2] is rewriiten has WriteBack( xs[2] )
T! Q = xs[2].WriteBack ;

The contents of the collection at the indexed locations is initialized on first read access.

Implementation of WriteBack.
static class DefaultExt
{
public static WriteBack<T>( this out  mem : T! ) : T!
{
  mem = default<T!> ;
  return mem;
}
Aug 12, 2014 at 11:38 PM
AdamSpeight2008 wrote:
How would a collection of nonnull reference types be initialized with a value in each item?
The contents of the collection at the indexed locations is initialized on first read access.
Lazy initialization is only safe when all layers between the consumer and the backing store expect it; that would definitely not be the case for many existing .NET collections. It is reasonable in .NET to assume that if an array of some reference type is copied, any element which has not been written in either the original nor the copy will be reference-equal to its corresponding element in the other array. Such an assumption breaks down if the act of reading elements from the array may alter them.
Aug 13, 2014 at 1:51 AM
` We are only talking about variables and collections that contain non-null reference types.
If the array is copied that also means that both array a difference references, the content will still be the same referenced objects.
T![] original  /* lets say in already contain non-null items*/
T![] copyA     /* lets say it is the same size as the original*/

Array.Copy( original, copyA )

original[ 1 ] = "a" /* it this point the original diverges from CopyA */
   copyA[ 1 ] = "b" 

var x = original[ 20 ] /* Previously "un-initialised" non-null */
                       /* So it is assigned back the default value  */
A possible implementation of a non-null array.
Module Module1

  Sub Main()
    Dim a = NonNull.NonNullArray(Of String).Create(10,"")

    For i =0 To 9
      Console.WriteLine("{0,-2} {1} {2}",i,  a(i),a(i)="")
    Next
    Console.WriteLine

    Dim b  = NonNullArray(Of String).Create(10,"-default-")
    For Each e In b
      Console.WriteLine(e)
    Next

End Sub
  End Module


Public Class NonNullArray(Of T As Class)
  Implements IEnumerable(of T)
  Private _A() As T
  Private _Default As T

   Private  Sub New(size As Integer, def As T)
    ReDim _A(size)
    _Default = def
  End Sub

  Default Public Property Item(index As Integer) As T
    Get
      If (index < 0) OrElse (index >= _A.Count) Then Throw New IndexOutOfRangeException(String.Format("Index is of the bounts 0 <= {0} < {1}", index, _A.Count))
      Dim tmp = _A(index)
      If tmp Is Nothing Then
        _A(index) = _Default
        tmp = _Default
      End If
      Return tmp
    End Get
    Set(value As T)
      If (index < 0) OrElse (index >= _A.Count) Then Throw New IndexOutOfRangeException(String.Format("Index is of the bounts 0 <= {0} < {1}", index, _A.Count))
      Dim tmp = value
      If tmp Is Nothing Then tmp = _Default
      _A(index) = tmp
    End Set
  End Property


  Public Shared Function Create(size As Integer, def As T) As NonNullArray(Of T)
    If size < 1 Then Throw New ArgumentOutOfRangeException("size cannot be less than 1")
    If def Is Nothing then Throw New ArgumentException("def cannot be null")
    Return New NonNullArray(Of T)(size-1,def)
  End Function

  Public Iterator Function GetEnumerator() As IEnumerator(Of T) Implements IEnumerable(Of T).GetEnumerator
    For i =0 To _A.Count-1
      Yield Me(i)
    Next
  End Function

  Public Function GetEnumerator1() As IEnumerator Implements IEnumerable.GetEnumerator
    Return GetEnumerator 
  End Function


End Class