“new” keyword and “default” constructors

DWScript SVN version just introduced support for the “new” keyword and “default” constructors.

The syntax is similar to that of Oxygene/Prism, you can now create a new instance with

obj1 := new TSomeObject;
obj2 := new TSomeOtherObject(param1, param2);

By default, the above syntax will be duck-typed to the .Create constructor of a given class, but you can alternatively specify a “default” constructor (one per class) to select a specific named constructor that “new” will use:

type TSomeClass = class
   constructor ImTheOne; default;
end;

The Oxygene syntax is also extended in two ways, you can use “new” on a metaclass variable:

type TSomeObjectClass = class of TSomeObject;
...
var ref : TSomeObjectClass;
...
obj := new ref;

And you can further use it on an expression that returns a metaclass by placing it between brackets:

function TOtherClass.GetMetaClass : TSomeObjectClass;
...
var o : TOtherClass;
...
obj := new (o.GetMetaClass)(param1, param2);

The brackets are not just there to make it look like Turbo Pascal-era code, they are required for disambiguation, f.i. the two lines

obj1 := new TSomeClass(param);
obj2 := new (TSomeClass(param));

are compiled as a syntax variation of

obj1 := TSomeClass.Create(param);
obj2 := TSomeClass(param).Create;

The “new” keyword is also planned to be used as the basis of dynamic array instantiation.

8 thoughts on ““new” keyword and “default” constructors

  1. This is “good”… but it’s not “big” 🙂
    You’re doing amazing stuff here!

  2. Out of interest, any particular reason for this? If C# were to acquire the Create syntax, I’m fairly sure it wouldn’t make the language any easier to learn or use. In fact, multiple ways to do the same thing are typically a net negative for me, but obviously, YMMV.

  3. I agree Chris – particularly of concern here is that the two features in combination create a potential for confusion and inadvertent errors that didn’t previously exist.

    Imagine a class with Create and CreateEx parameterless constructors.

    Bob writes a script using “new”, which is duck-typed to the “Create” constructor. Then Janet decides that they would prefer “CreateEx” to be the “default” on that class that Bob is using.

    Bob’s script suddenly starts doing unexpected things, calling the “wrong” constructor” and he has no idea why! (or maybe even some script that Janet wrote many moons ago and had long since forgotten/stopped thinking about)

    “New” on it’s own as a shortcut for “constructor with these parameters, calling ‘Create’ if there are multiple candidate constructors” would be less problematic imho, without “default”.

    ymmv, as always

  4. @Chris
    The major reasons are:
    – script users, the “new” syntax is the one in most “modern” languages and feels more natural to them (and arguably it is)
    – closer to modern Pascal flavor that Oxygene is
    – unifies the instantiation syntax with (upcoming) dynamic arrays
    In essence, I see it taking over the traditional form.
    – makes it unambiguous you’re creating a new instance (while Delphi’s syntax could be a call to a constructor OR a class function)

    @Jolyon Smith
    The case you evoke already exists, not just for array properties, but for all object methods, as it’s always possible to introduce intermediate classes that will introduce a new method with the same name, resulting in the code being redirected. It also exists in Delphi in a more generalized fashion through overloads.

    Thing is, if Bob wants to call a specific constructor, he can use the named form, but if he wants to call the default constructor, he can use New, and Janet then has an easier time maintaining what that default constructor is. Currently in Delphi, that maintenance typically involves overloading Create with virtual or reintroduced versions, which is problematic.

    Also in Delphi the constructors have a factory syntax (you can replace them with class functions f.i.), with “new” you can have a separation of the notion, and looking at the code, you *know* you’re creating a new instance, while a TSomeObject.Create could have been overloaded by a class functions, and thus may not be creating anything.

  5. @Eric: The fact that the cheese has already gone off is no excuse for putting rotten eggs in the fridge.

    We already know the cheese is off – we avoid it or take care to use only the good cheese. Adding more stuff that could give us food poisoning doesn’t help, it just increases the chances that a trip to the fridge could give us food poisoning.

    🙂

    Yes, Bob could have used a specific constructor, but he thought he was – his “new” was calling “Create”. What he didn’t/couldn’t know was that Janet was later going to change the behaviour of “new” (on that class) by adding a “default” to that class.

    If Bob calls a virtual “Create” using a class ref, then yes, he might still face a change in his code if the class ref his code ends up with changes and is now a further derived class that overrides the constructor.

    But if he is calling TMyClass.Create then TMyClass.Create is what he calls, no matter what other constructors are added to TMyClass or what classes may derive from that class and overide that constructor.

    “new” and “default” remove that certainty. The problem being that “new” initially does what TMyClass.Create does, but that can change under Bob’s feet.

    And yes, you can have a method called “Create” that isn’t a constructor – more off cheese, but in this case a deliberate violation on the part of the coder of a well established convention. You can also write a method called “Load” which actually “Saves” – language features that try to prevent developers being stupid are doomed to fail, imho.

  6. To clarify my concern when I say…:

    “new” and “default” remove that certainty. The
    problem being that “new” initially does what
    TMyClass.Create does, but that can change under
    Bob’s feet.

    i.e. “new” on it’s own is a convenient shortcut and syntactic sugar for something that already exists with a known and predictable behaviour (yes, subject to some conditions, but the *same* conditions that we already know of).

    It’s the addition of the capability for “default” constructor directive on the classes that “new” works on/with that adds an all new [sic] level of uncertainty and makes “new” unpalateable as a result, imho.

  7. @Eric: each to their own – the first thing that comes to mind when I see the ‘new’ syntax is the old COM-based VB! WRT Oxygene, for me (YMMV obviously) it’s not so much more ‘modern’ as just ‘C#-tinged’. In fact, if anything, the ‘new’ syntax is arguably more Pascal-ish than Create (cf. the old New standard procedure). Of course, that might be a point in new’s favour for some people!

    Regarding dynamic arrays, Delphi has supported the Create syntax (albeit in a limited fashion) for some time now, so Delphi as its own ‘unification’ of the instantiation syntax.

  8. @Chris
    Yes, originally there was a “new” in Pascal (and there still is, for pointers to records f.i.).

    The issues with using “.Create” came back from the field of script users, with no priori knowledge of Pascal, for which “new” is the typical approach, while “.Create” is a bit confusing, and IMHO, it’s actually less “readable”. Pascal as a whole is a very readable language (in that you can read it out loud), and “TObject dot Create” just doesn’t convey the meaning as clearly as “new TObject”.

    As for Create syntax on dynamic arrays, I’m unaware of it, how is written? When was it introduced?

Comments are closed.