And so what? You can't fully duplicate Writeln either.
Which I think is equally as bad. I think specifically bending the rules for a single use case, but is not usable in other, similar use-cases, is simply bad design.
If some functions, like WriteLn benefit from a special feature (varadic compile time arguments with different types in this case), you aknowledge that there are use-cases for this that are worth creating a special rule for. But by only having this rule apply to a single function (WriteLn and Write in this case), you simultaniously say that only one of the use-cases is worthy to use this.
Isn't this kinda a contradiction? Saying on the one thing that this is so important it needs special handling, but not important enough to let others use it as well
It's one of the things I most like about Haskell, it is basically just a really basic set of language rules, which are used for creating a standard library, which provides everything. Even the concept of Integers in haskell is defined within the confines of the Language itself (and one of the first lections when you are learing haskell is usually to create your own integer type).
In Pascal this would not be possible, if you want to recreate what the system unit provides, either to extend it to other domains that are currently not covered by the system unit (to stick with the writeln example, e.g. to provide a writeln like functionality for TStream), or simply to understand how such things work internally, you will certainly hit a point where you just can't because the way it is used in the system unit is through compiler magic that you have no access to.
You do not need to go as far as with Haskell, where everything is Haskell, but you can also look at C++, which still has some compiler magic, but where you can also recreate all of the standard library only with C++ code. There are no magical functions like writeln or types like arrays or strings. Also in Python, while there is still some magic behind the scenes, one of the major goals of python 3 (which broke backwards compatibility with python 2) was to have less special cases and unify the systems.
There is nothing more frustrating than trying to understand and recreate a concept just to be told: Well sure we can do that, but you can't
One only needs to know how to do it:
procedure Test(var aArg: LongInt);
begin
Writeln(HexStr(@aArg));
end;
var
i: LongInt;
begin
Test(i);
Test(PLongInt(Nil)^);
end.
Ignoring the obvious problem that dereferencing a nil pointer is raising all the red flags and is something you probably never want in your code, such a thing would still require a compile time branch:
procedure DoSomethingWithNil;
begin
WriteLn('Nil was passed');
end;
procedure DoSomethingWith(var AValue: Integer);
begin
if not Assigned(@AValue) then
begin
DoSomethingWithNil;
Exit;
end;
WriteLn(AValue, ' was passed');
end;
procedure Test;
var
i: Integer;
begin
DoSomethingWith(PInteger(Nil)^);
DoSomethingWith(i);
end;
And while this could easiely be optmised away by static analysis, the FPC is simply not good enough at optimisation to do so (this code on -O3 on FPC 3.2.2 for Linux does not optimise the "DoSomethingWith(PInteger(Nil)^);" to "DoSomethingWithNil")