As the post title says, this will be a brief detour through some features of the Pascal language and a presentation of some (theoretical) alternatives that could have been introduced instead. That is, some are real but little known syntax, others are what I think might be preferable to the syntax we actually have.
So, as the old saying goes, let’s take them in order: Something Old, Something New, Something Borrowed and Something Blue…
Something Old
Parameterless Implementations for Methods With Parameters
Did you know that you can omit the parameters on your method implementations?
unit Parameterless; interface type TFoo = class procedure Bar(const aParam: Integer); end; implementation procedure TFoo.Bar; var i: Integer; begin i := aParam; // << all good, the parameter declaration isn't needed end; end.
I recently saw this described as a bug that was fixed only as recently as Delphi 2007.
Well, it was never a bug and in fact it still compiles just fine even in XE2 and most likely XE3 (on the machine that I am writing this I don’t have XE3 so cannot verify). I first came across this language feature in some Delphi 5 code, coincidentally while investigating an issue that did turn out to be a compiler bug (but entirely unrelated to this language feature).
Ironically the mis-identification of this as a bug came paired with a complaint that the self-documenting interface/implementation declarations in Pascal resulted in unnecessary duplication of declarations – something that this particular feature of Pascal is obviously designed to reduce.
One problem that this very old feature of the Pascal language suffers from is that in more modern implementations of the language it is now possible to have multiple versions of the same method, each of course having different parameters. i.e. overloads. In that situation the compiler will of course have to insist that you have matching parameter declarations in your implementations, simply so it can match each implementation with the corresponding interface declaration.
But otherwise, the re-declaration of the parameters in the implementation is optional, by design. Whose design ? Well, the original language.
In effect – and in practice – the interface declaration is a forward declaration of the formal parameters for the methods in the class, and ANSI Pascal doesn’t require the formal parameters to be repeated when the forward declaration is completed by an actual implementation.
procedure FooBar(aParam: Integer); forward; procedure FooBar; var i: Integer; begin i := aParam * 2; end;
The Parameterless unit code (not the code above but the snippet before that) compiles just fine in Lazarus/FPC with the following syntax settings for the compiler:
- Delphi
- Turbo Pascal
- Mac Pascal
It is however rejected when compiling with the remaining syntax options:
- ObjectPascal
- FPC
Note however that just because you can do something is never – on it’s own – a reason to do it.
In this case, although this is something the language supports I cannot think of any good reason why you would omit the parameter declarations on the implementation. Doing so makes reading the implementation a bit more hazardous (referencing symbols – i.e. parameters whose origin is not immediately clear). Not to mention that introducing an overload at some later stage in the evolution of the code will necessitate the introduction of the missing parameters on any initial implementation that omits required parameters.
It is a curiosity of (some implementations of) the language rather than a “feature”, imho. 🙂
Something New
New Delphi XE3 Language Feature: Record Helpers for Non-Records
We have had class and record helpers in Delphi for a while, and now you can declare a record helper for fundamental types too. That is, types that are neither records nor classes.
My coverage here will not focus on the syntax or feature itself – that is adequately covered by Rodrigo in his article – but explore an alternative that could and should have been pursued instead.
To me, whatever the benefits that might derive from this new capability, there are two problems: One practical and one aesthetic. However, the solution to the aesthetic problem also leads to a solution to the practical problem, thus making the case that aesthetic problems can be indicative of more concrete problems that could/should be addressed. If it looks wrong then it might well be “wrong”.
Or more simply put: Form follows function.
In this case we have a language feature (class helpers) originally introduced to meet a very narrow and specific need and which have significant constraints (and dangers) associated with them as a result. So significant are the constraints and dangers that class helpers were always reserved (in the documentation) for that specific utilitarian purpose and developers specifically warned not to try to use them as a general purpose language feature.
This didn’t stop people of course (some people do think that just because they can do something then they should).
So the feature was extended to record helpers, without addressing the core constraints, problems and dangers.
And now we have them extended even further, with the same constraints and dangers and now – as if to confirm those underlying problems – a really, really nasty “code smell” is starting to become noticeable (if it wasn’t already – I don’t know about you, but I’ve been holding my nose since they debuted):
That is, a language feature with a contrived, inaccurate name and an awkward and misleading syntax (which, as an aside, the syntax highlighting cannot be as helpful with because a new keyword was created which cannot be a reserved word due to it’s late entry into the syntax).
Let us look at the declaration for a record helper for a string:
type TStringHelper = record helper for String // "helper" is not reserved so is coloured as an identifier : end;
First of all, a String is not a record. You might stretch a point and argue that the hidden RTTI fields of a string constitute a “record”, but the same semantic contortions cannot be applied to Integer or Boolean etc.
The “record”ness doesn’t derive from the helper type itself either – if it did then “class helpers” would have been “record helpers” from the start.
No, these are really just “type helpers” and since class and record types are also types, why were these not simply “type helpers” from the very start ? This would cover all types – classes, records and fundamental.
Furthermore, since when were types declared using a keyword that described their intended use (‘helper’), rather than simply describing what they are and leaving the use to be reflected in the name given to the thing itself (rather than the type of the thing?)
We don’t declare logical flags by declaring “flags”. We declare boolean variables whose names indicate their use as a flag, or otherwise:
var bIsUpdating: Boolean; begin bIsUpdating := (fUpdateCount > 0); : end;
This decision to create a new keyword identifying a narrow usage for some syntactic element is especially puzzling when you consider that if you think about the language feature as what it is – rather than what it does – then you realise that everything needed to express that in a declaration was already in the reserved word list!
Not only that, a very great deal of the character of a “helper” was already present in another, pre-existing language feature.
Interfaces.
Set Time Machine for 2004… ENGAGE
So, let’s go back in time and see if we can’t design this language feature properly…
Actually, it’s so easy and obvious that it beggars belief that we ended up with the mess that we have.
Here is the alternative declaration that I suggest would have made better sense for a helper for any type, using the String helper as the example. Notice that in this case, every word used is already an existing reserved word, except – obviously and intuitively – for the identifiers in the declaration:
StringMethods = type interface for String : end;
First of all, the declaration states what the thing we are declaring is, not what it intended to be used for.
A “type interface” describes perfectly what is occurring: we’re declaring a new [programming] interface for some type.
Sure, it’s different from other interface or “distinct type” declarations, but at least it makes more sense than talking about “records” for things that aren’t remotely record-like. And since this construct wasn’t previously legal or valid, we can define what ever we want it to mean now that it is legal and valid (just as a “class helper” required a new set of syntax rules).
So on the one hand we get a new set of rules for something entirely new to the language, but which can be defined using existing language concepts and even keywords.
Even better though is that the adoption of the “interface” concept for this feature automatically leads to a solution to the scoping problem that arises when you have multiple, potential interfaces for a particular type.
This is the constraint that makes the existing class and record helper implementation so hopelessly broken (for general purpose use).
The solution is – again – already present in the language, and follows naturally from the adoption of the “interface” concept for this feature. It is the same solution that that already exists for objects that support multiple interfaces.
In existing code when you have a reference to some object but you need access to that object via some supported interface, then you can ask for that using the “as” operator. The exact same syntax could be supported where the interface being requested is a “type interface” rather than an IInterface (the compiler will know the difference and can emit the corresponding code accordingly):
(s as StringMethods).SomeMethodOfMyOwn;
This is better even than simply allowing “hard” type-casting.
The as operator is not only consistent with the same use for COM-style interfaces, but it is also already “overloaded” as a type-checked casting operator (for class types) that is already present in the language. All we are doing is adding a third overload – another use case – for the “as” operator.
Instead of making a runtime call to a QueryInterface implementation or a runtime walk of the class hierarchy, this use case for as would be implemented by the compiler to ensure that the named type interface was available and in scope.
There is no such similar use case for as in relation to “records” at all.
I am reminded – yet again of the [Lost] Spirit of Delphi I have written about in the past, notably in relation to more recent introductions to the language.
The ObjectPascal in Delphi used to be characterised by elegant and intuitive syntax.
Many of the more recent language features in Delphi are – imho – clumsy and, frankly, very poorly thought out.
Something Borrowed
Myth: Oxygene Cannot Compile a Delphi Class Without Significant Rewriting
This is not (in most cases) true. While the Oxygene dialect of Pascal is significantly updated and overhauled in comparison with the Delphi dialect, it does however retain some language features purely and specifically for compatibility with Delphi declarations.
procedure and function for example are supported, but they are not required in Oxygene code. Instead the single keyword method can be used, and whether a particular method is a procedure or a function is then determined by whether or not it is declared as having a return type or not.
The fact that this is even possible makes it apparent that the distinction between procedures and functions is actually being made twice in any one declaration: the formal declaration of whether a return value was provided (function vs procedure) and then, in the case of a function, the corresponding type of that return value.
When discussing this with a colleague recently I initially stated that this was an obvious redundancy that could be elegantly address by unifying the declaration in the way that Oxygene has done. However, in the very next breath I retracted that view, as it then occurred to me the value that the procedure/function disctinction has:
When reading a class declaration, I can immediately and easily see that a method identified as a procedure has no return value that I might be concerned with. Equally I can immediately and easily see that a function does have a return value that I might – and probably should – be very concerned with. If all methods are declared simply as method then I have to read to the end of the declaration for each method to see whether there is any such return value.
So on this occasion, given that I have the choice, I myself would prefer to stick to procedure/function as a form of documentation.
Your mileage may vary. 🙂
Something Blue
OK, I admit. I couldn’t come up with something to fit this part of the saying, so at this point I’ll open the topic up to the floor.
Feel free to nominate your favourite, or least favourite, syntactical gem in Pascal or a Pascal derived language in the comments. 🙂
I like the part about using ‘as’ keyword for type helpers. BTW C# has similar thing as class helpers called “extension methods” but AFAIK there is no conflict resolution technique for extension methods in C# other than namespaces but we have units for that. By conflict I mean situation when two extension methods have same signature.
The comparison with C# extension methods is the starting point for discussion of “helpers”. The designers of extension methods got it right. That’s what we are shooting for.
Extension methods are pure sugar. It’s all about enabling expressiveness. So you don’t want to have to use as SomeType when there are multiple helpers in scope. That destroys the original goal of expressiveness.
Again, C# gets it right. It allows many extension method static classes to be in scope simultaneously. The compiler searches through all that are in scope to find a match. In case of ambiguity, the compiler stops with an error.
I agree 100% that the current syntax for helpers is poor. I am disappointed in the mixed messages from Emba over their use. I hope they do something better, something that matches C# extension methods, in the next gen compiler.
Ada and Dylan can be source of inspiration.
In Dylan, there is nothing special about dotted methods. They are just another syntax for functions. a.b(c, d, e) is the same as b(a, c, d, e). This is not surprising given the underlying CLOS in Dylan: functions are not contained inside objects and classes.
This would be useful to replace helpers in Delphi at all. IIRC there can only be one active helper for any type. They don’t mix unless one derive a new helper from several (e. g. 3rd party) independent helpers.
A proposal is to get helper functions out of helpers. Having first argument mapped to object isn’t familiar in Delphi, but there is a syntax for event handlers: procedure of object. Why don’t we just define procedure of string and class function of string?
Overload can be used to overload helper functions between units.
But – and that is inconsistent again – Delphi has actually two different helpers. Class helpers are instances of TInterfacedObject like closures are, can be inherited, etc. Record helpers are none of those and cannot be inherited, etc (documentation is lying upon that). I think record helpers are actually implemented in that Dilan-like way, while class helpers just have a member reference to a object being extended. And while i agree that current syntax is kinda unnatural and alien even to IDE itself, i think “type interface for TButton{or any other class}” is ugly too. It is basically a nonsense. I think “class helpers” were introduced 1st and its name made sense then. Then someone talked about “extended records”. And in XE2 it was backward compatibility issue. Could helpers be introduced effectively without “class/record” prefix ? dunno.
I don’t much care *how* the language feature is implemented “under the hood”. And if language features are constrained by the implementation details then this too says something about the way in which the features were introduced. i.e. not by careful consideration and design but a “just good enough is good enough” approach.
In context of the helpers discussion … I’m not quite a fan of your suggestion regarding the as/is (=casts) usage … since a helper purpose should be just to introduce some syntax sugar to support the compiler to extends it’s scope when looking for methods … so definitely no cast should be needed. Invocation should be clean …like the type “native” associated methods.
Anyway, IMHO, to hit more rabbits at the same time with the helper support I’ll suggest something like this:
[1] Use a unified/simplified syntax by dropping both class and record) prefixes thus using just helper. The parser should be smart enough to know what kind of target (class, record, primitive …) will get…. So no odd looking record helper for int64 will be needed
[2] Introduce helper inheritance to allow a helper to extend any other helpers for a target type thus supporting kind of extension methods with the scope being extended from the other
[3] Bonus: Introduce helpers support for interfaces but without compatibility issues. As an inspiration for this the Delphi team might look into Java 8 proposed defender/virtual extension methods : http://cr.openjdk.java.net/~briangoetz/lambda/lambda-state-4.html , http://www.javabeat.net/2012/05/use-of-virtual-extension-methods-in-the-java-8-apis/ , http://cr.openjdk.java.net/~briangoetz/lambda/Defender%20Methods%20v4.pdf
As a result, we can have a nice, pascalish helper declaration like:
type
TNewStringHelper = helper for String (Unit1.TStringHelper1, Unit2.TStringHelper2)
// add my own helper methods/properties here
….
end;
The idea of as was not to require it but to support it where needed to resolve problems of multiple helpers existing for the same type.
The problem with using “helper” on it’s own is that it is a [potential] current, valid identifier, possibly for an existing type. So the compiler cannot reliably know that it is dealing with a helper declaration as opposed to a reference to some existing type called “helper”, followed by some syntax error.
The “class helper” and “record helper” syntaxes co-opt the class and record reserved words as “primers”, so that when the identifier token “helper” immediately follows it can reliably trigger the new language feature as it was previously not legal for such a sequence.
At least, that’s my guess. IANACW(*)
(*) “CW”: Compiler Writer 🙂
Regarding your [2]: class helpers already support inheritance. The syntax is the following:
type
TObjectHelper = class helper(TSomeOtherObjectHelper) for TObject
end;
The requirement for “TSomeOtherObjectHelper” is that it must extend a class that is the same or a parent of the class that is extended by “TObjectHelper”. In Delphi inheritance is not supported, while FPC supports it in non Delphi modes (where the type extended by the parent must be the same, because records don’t support inheritance).
Regards,
Sven
As far as I can see, helper inheritance is a (partial) solution to the problem of the “Highlander Helper” problem (There Can Be Only One). 🙂
Helper inheritance makes no sense otherwise that I can see.
Given the nature of helpers and what you can do with them, the idea that you would deliberately create two helpers for the same or related classes in the same codebase makes very little sense. The only real use for helper inheritance that I can see is to patch your own helper to extend someone else’s helper in an imported/reference codebase, in order to create the one “super Highlander helper” that combines both, an approach that falls down as soon as you find yourself with a 2nd such referenced/imported helper, in which case you have to go in and modify one or other imported/referenced helper simply in order that your own helper can function as you need.
The whole area is a mess, frankly. Instead of adding to the problem by extending the “feature” into yet more areas, Embarcadero should have fixed the problems with what they had first.
My guess is that fixing those problems was harder than quickly rolling out this new feature and they needed something to put in the box. Add to that the fact that they can always simply claim “Well of course they’re not perfect, but we did tell you not to use them so what do you expect ?” 😉
Yes, inheritance is just not useful for the extension method use case for helpers. Which is the use case that gets Delphi coders excited.
I gues Emba needed inheritance for the VCL.net work.
What’s needed is a way to extend types, avoiding the Highlander trap, and there to be no need for casting.
RE: parameterless.
FPC initially required interface and implementation to be the same. It would trip the check that interface and implementation declarations were the same.
FPC needed these changes because it was essentially a TP compiler that got a Delphi mode added later.
This was relatively quickly solved in delphi mode. though corner cases (e.g. using aliases of types like “string” in interface and “ansistring” and implementation) popped up for a while. (this is all before 2000-2002, don’t be afraid for current versions)
A problem that stuck longer was the fact that Delphi allows to declare a function in the interface, but only declare it as external in the implementation. The Jedi headers contained this. This violated the principle
that the interface should fully declare what is needed to call the function, and took a long time (and a major version) to fix.
I agree with you that the current syntax for declaring helpers is a bit bad. They all (“class helper”, “record helper”, “type helper”, “type interface”) have in common that they “misuse” a prefixing keyword that already has a different meaning. In my opinion adding a new keyword like “helper” to solely declare such helpers would have been a better way.
That said I have to defend the difference between “class helper” and “record helper”: you might not be aware of it, but as the one that implemented helper types in FPC I have dug very deep into them to implement them as compatible as possible. This dugging revealed the following differences:
* in class helpers you can use “(strict) private”, “(strict) protected”, “public” and “published” (the latter is forbidden in non-Delphi modes in FPC, as it’s useless) while in record helpers you can only use “(strict) private” and “public”
* in class helpers you can declare normal methods, class methods and static class methods while in record helpers you can not declare class methods (only static ones)
* record helpers don’t support inheritance like class helpers do; this goes so far that you can’t even use “inherited” inside a record helper to call a method of the extended record (in FPC I have allowed inheritance for non-Delphi modes, as I see no harm in it)
So you see that using different keywords clears up what you can expect of the type. Though I definitely agree that for primitve types “type helper” would have been better. Just yesterday (before your post) I did a proof of concept modification of FPC to support type helpers (and it already worked quite well 😉 ) and there I made it so that in non-Delphi modes you need to use “type helper” for primitive types (and I suspect that they won’t support constructors, but I don’t have a XE3 to test).
Your idea of using “as” is also interesting and as I plan to extend helpers to allow multiple active helpers for one type (with nicely defined rules of course when which method is used) I’ll keep that in mind. One has to define though what “s as SomeHelper” means if you have that as a RValue like in the following “s2 := s as SomeHelper”. The most likely definition would be the type of “s”…
If you want to learn a bit more about some pecularities of class and record helpers I suggest you to take a look at the “thlpXX.pp”, “trhlpXX.pp” and “tchlpXX.pp” test cases located in the “tests/test” directory of a (current) FPC source distribution.
Finally I have to add a remark about my one “most disliked syntax”: attributes. Their syntax soooo smells like C# that it’s terrible. I personally would have preferred the following more Pascal like syntax:
type
TSomeClass = class
procedure SomeMethod; attributes [TSomeAttribute1, TSomeAttribute2(Arg1)];
end;
Just the same way as modifiers like “deprecated”, etc. are done: defined after the identifer they belong to. As it should be in Pascal…
Regards,
Sven
Wow, a very interesting comment! Thanks for taking the time. It’s great to get a bit of insight into the work going on with FPC in this way. 🙂
And yes, I agree 100% w.r.t the attribute syntax – I posted on this very subject (and then some!) myself, about 3 years ago!
If you continue to write such interesting posts, you’ll continue to get such informative comments 😛
I have just a small correction to do for my comment: In class helpers “published” is allowed for all modes. And it can also be used if you take a look at this bug report.
Regards,
Sven
Btw, they might not have overloaded the meaning of “type” since it is a token that has meaning in the global scope, and indicates one of the major block types (together with var, const and procedure/function)
Overloading their meaning might have consequences for parsers that try to parse fragments of code. (codetools,highlighting)
Except that “type” does already have meaning in more confined scope. In type declarations in fact, being used to declare distinct types, as opposed to types that are merely aliases of some other type:
type TBankAccountNumber = type Integer;
Hmm, interesting ideas. I also like the concept of using “as” for resolving ambiguity. However, I would find the use of the “interface” keyword as a replacement for “helper” highly inappropriate. For me, the one thing that defines an “interface” is that it is just that: It must not ever contain an implementation.
“interface” can define a contract for an implementation – as in a COM interface. But “interface” can also define the public exposure of an implementation whose details are otherwise private – as in API.
Or indeed, as in the “interface” section of a unit, which is directly analogous to the use of “interface” in relation to providing some new API for an existing type.
The use of “interface” in Pascal is already not as limited or as tightly defined as you might think. 🙂