Partial Classes in DWScript

May 14th, 2012

Support for partial classes has been added to DWScript: they can be declared as “class partial (TAncestor)” as well as with the Oxygene syntaxpartial class (TAncestor)” syntax.

Partial classes allow to split the definition and implementation of a class over multiple files, or over multiple sections of the same file. Partial declarations must have the same modifiers (abstract, sealed, static…), must specify the same ancestor (or none), however each partial declaration is allowed to introduce new interfaces and their implementations.

Partial class can have multiple purposes, to quote wikipedia:

  • Facilitates the implementation of code generators that have to “inject” code declarations/implementations that comes form other descriptions, such as a Visual UI designer, data and string resources, etc.
  • Enables separation of concerns, in a way similar to aspect-oriented programming but without using any extra tools.
  • Enables optionally composited functionality, which can be omitted or substituted depending on target or requirements..
  • Enables separation of a class’s interface and implementation code in a unique way. (irrelevant for Pascal!)
  • Enables multiple developers to work on a single class concurrently without the need to merge individual code into one file at a later time.
  • Eases navigation through large classes within a editor.

 

Eric News

Mutant records: on methods (and helpers)

May 7th, 2012

When “record with methods” were introduced, an important feature was overlooked: mutability. This article discusses the problem, and introduces a possible syntax extension to solve it. Ideas & comments welcome!

The Problem

Effectively, “records with methods” treat all record “const” elements as “var“, even when they shouldn’t, and effectively ignores the compiler option “assignable constants”. Witness the following record:

type
   TRec = record
      Field : Integer;
      procedure IncMe;
   end;
...
procedure TRec.IncMe;
begin
   Inc(Field);
end;

Then the compiler will happily allow you to do:

const MyNullRec : TRec = (Field: 0);
...
MyNullRec.IncMe; // watch me, I'm magic!
WriteLn(MyNullRec.Field); // will write 1 !

…it will also accept…

procedure Myproc(const r : TRec);
begin
   r.IncMe; // modified whatever r as if it had been a var
end;

…and that as well…

type
   TMyClass = class
      private
         FRec : TRec;
         procedure SetRec(const aRec : TRec);
      public
         property RW : TRec read FRec write SetRec;
         property RO : TRec read FRec;
   end;
...
var myClass : TMyClass;
...
// everything's as it should be there
myClass.RO := aRec; // gives an error
myClass.RW := aRec; // uses SetRec
// but that compiles to...
myClass.RO.IncMe; // modifying a read-only property !
myClass.RW.IncMe; // modifies and bypasses SetRec !

It would “compile” even if the getter was a function returning a record… or just in about any case, really.

And of course you can cascade all the above in a real-world case, so you end up with weird side-effects and really hard-to-track down bugs.

Besides bug, that also raises the issue of usability of records like TPoint and other simple vector/matrix types, as properties of objects. As long as they did not have methods, you would have to use the setter to modify such a property, so the objects had guaranteed notification of a position change f.i., with methods being able to magically mutate the records, you no longer have that guarantee. And adding all the baggage so that a TPoint can be owned and notify its owner of a change would be over-the-top, wouldn’t it?

What’s Missing

Two syntax elements are missing:

  • the ability to specify if a method is mutating the record or not, ie. if it should get “Self” as a “var” or as a “const“, so that you can make the above behavior explicit, and the compiler can emit errors when it’s not appropriate
  • the ability to mark a record as mutable/immutable, to further remove the ambiguities

Ideas for such a syntax, in explicit form, and reusing existing keywords:

type
   TRec = record
      Field : Integer;
      var procedure IncMe; // mutating
      const function ToString : String; // non-mutating
   end;

Modifiers in Pascal (except “class“) usually follow the declaration, but “var” and “const” can’t, as they could be ambiguous with the declaration of a following constant/variant. So if reusing those, rather than introducing new keywords, they have to be in front.

Also you could mark a record as explicitly mutable or immutable, by prefixing it with “var” or “const”

type
   TImmutableRec = const record
      Field : Integer;
      var procedure IncMe; // mark as explicitly mutating ?
      function ToString : String; // non-mutating by default
   end;
   TMutableRec = var record
      Field : Integer;
      procedure IncMe; // implicitly mutating
      const function ToString : String; // mark as explicitly non-mutating
   end;

For backward compatibility with existing records with methods, the implicit default would have to be “var record” as the records are currently all mutable.

With that extra information, the compiler could then perform proper checks and proper parameter passing, so the side-effects mentioned could happen only if you explicitly went for them, rather than by mistake as is currently the case.

Additionally, immutable records would implicitly have their fields as read-only everywhere outside the record constructor (which would then become truly useful as language syntax element). It might also be possible to extend the syntax to be able to declare explicitly immutable classes.

The case of helpers

When generalizing helpers (as in DWScript), the above mutability issue also applies to helpers for value types, ie. records and static arrays, so a similar syntax would have to be introduced.

To minimize side-effects, helpers on record and static arrays currently pass their “Self” as const, this allows to use such helpers in all cases (including “const” parameters, function results, read-only properties…), but is restrictive, as it prevents mutation. So once the above syntax is decided upon, it could apply to them as well.

For helpers on reference types in DWScript (classes, interfaces, dynamic arrays), the issue doesn’t apply.

Note that in Delphi, helpers also have the design bug of being able to magically access private fields… but that’s another story.

 

Eric Ideas , , , , , ,

Helpers added to DWScript

May 2nd, 2012

In the SVN version, DWScript now has experimental support for helpers. This is a generalization of Delphi’s class helpers, which can be applied to (almost) any type. You can also have multiple helpers per type.

The syntax is currently the following:

type
   TMyHelper = helper for some_type_here
      private
         ...private helper methods, class vars & class consts...
      public
         ...public helper methods, class vars & class consts...
   end;

You can have helpers for base types, classes, records, interfaces or arrays.

For instance if you write

type TFloatHelper = helper for Float
      const PI = 3.141592;
      function ToString : String;
   end;
...
function TFloatHelper.ToString : String;
begin
   Result := FloatToStr(Self);
end;

then it allows you to code things like ‘Float.Pi‘ or ‘myFloatVar.ToString‘.

The literal form is also accepted (‘TFloatHelper.Pi‘ and ‘TFloatHelper.ToString(myFloatVar)‘).

It’s possible to have class methods in helpers, for types with a meta-symbol, Self will be that meta-symbol (f.i. for a TObject helper, Self would be TClass), for others (like Float or records), Self will not be defined.

Current limitations of the experimental support:

  • priority/scope when multiple helpers introduce the same name isn’t fully spec’ed yet and may evolve
  • compiler may accept things it should reject, and vice-versa :)
  • parser performance was slightly reduced, optimization will happen when the spec has been completed
  • inherited‘ isn’t yet supported to call a non-helped version of an helped member added
  • suggestions class doesn’t yet support suggesting helpers

The scope resolution when there are multiple-helpers introducing the same method is at the moment not fully defined, currently the compiler will pick the first one it finds in scope. Other options are under considerations: using the same resolution logic as overloads, allowing helpers to be “sub-classed” and then using class-like resolution logic… if you have good ideas pilfered from other languages, or bad approaches that should be avoided, now is the time to chime in!

The syntax will also probably be extended to encompass the Delphi syntax (“class helper”) so you can share code, and maybe others variations (“interface helper”, “type helper”…) though which/how remains to be determined.

Once all the syntax and scoping elements have been finalized, the compiler will get proper optimizations, but for now, it’s expected to be somewhat less efficient.

Helpers on functions pointers/delegates types aren’t yet allowed, mostly because of ambiguity, f.i. in

type
   TFunc = function : Integer;
   TFuncHelper = helper for TFunc
      function ToString : String;
   end;
   TIntegerHelper = helper for Integer
      function ToString : String;
   end;
...
var f : TFunc;
f.ToString;

the “f.ToString” could be understood as meaning “TFuncHelper.ToString(f)” or “TIntegerHelper.ToString(f())” if helpers on function pointers were allowed. If a good resolution is found, they could be “helped” too, but helpers may not be very relevant to function pointers (?).

Eric News ,

Dynamic arrays as stacks, tighter JavaScript

April 30th, 2012

Here is a summary of the changes of recent happenings in the SVN, note that this list doesn’t include the very latest changes and features, which will get an article of their own.

Language:

  • dynamic arrays now support Pop(), Peek() and Push() and can be used as stacks.
  • dynamic arrays Add() and Push() now accept a variable number of parameters, and in addition to elements, they now accept dynamic arrays too (which will be concatenated)
  • Count() can now be used as an alternative to Length() for static and dynamic arrays.
  • Class variables are now supported, both by classes and records.
  • Oxygene-like syntax for explicitly scoped “enum”  and “flags” is now supported:
    MyEnum = enum (One, Two, Three)
    MyFlags = flags (Alpha, Beta, Gamma)

    Both forms must be prefixed, as in MyEnum.Two, the “enum” form is otherwise similar to classic enumerations. The flags form is a special case in which the elements values are powers of two (f.i. MyFlags.Beta = 2, MyFlags.Gamma = 4)

  • class methods can now be marked as static.
  • Class const syntax is now supported (had to be written as just ‘const’ before)
  • multi-line indented strings can now be either #” or #’
  • tweaked overload metric to give precedence to Float over Integer when Variants are involved
  • added a new hint if you redeclare a variable with the same name in a local sub-scope (doesn’t apply across sub-procedures)
  • improved some error locations, other minor fixes

Built-in functions and interfacing:

  • Partial RTTI support for indexed properties (Stefan Glienke)
  • StrSplit() splits a string on a delimiter and returns an array of string
    StrSplit(‘jan,feb,mar’, ‘,’) returns ['jan', 'feb', 'mar'] as a dynamic array
  • StrJoin() takes an array of string and returns a string made by joining all the individual strings
    StrJoin(a, ‘,’) with a the above array would return ‘jan,feb,mar’

JavaScript CodeGen:

  • various improvements to the output when optimizing for size (about 10% of size reduction)
  • fixed an issue with var parameters of a parent procedure used in a local procedure or anonymous method
  • smart-linker is now capable of eliminating some cases of unused interfaces
  • fixed an issue with aliased types used as record fields

Support Tools:

  • added fledgling code metrics classes

As a side note, the recent (and planned) improvements to dynamic arrays are currently tipping the generics vs templates balance towards templates, as they’re becoming capable enough “generic collections” on their own, meaning that “classic” generics wouldn’t bring much extra capability to the language for everyday usage.

Eric News

OptimalCode – “Delphi Optimization Guidelines”

April 26th, 2012

If you recognize the title of this article by Robert Lee, then chances are you’ve been around Delphi for a while! :-)

Alas the optimalcode.com website and Robert Lee disappeared years ago without a trace, but the “Delphi Optimization Guidelines” (dating back from 2002-3003) has been safeguarded and preserved. Recently someone pointed to me that the mirror I had in my Links section had disappeared too…

I guess it’s my turn to host the sacred relics, so I’ve updated the link and placed not one, but three copies on this site:

The guide is, at the time of this writing, nearly 10 years old, so read it with that in mind!
That said many tips still apply to the 32bit Delphi XE2 compiler, and quite a few tips are valid regardless of compiler and programming language.

Eric Tips , , ,