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…
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:
- Turbo Pascal
- Mac Pascal
It is however rejected when compiling with the remaining syntax options:
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. 🙂
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.
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.
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.
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. 🙂
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. 🙂