@Arioch: As I understood I still need to "register" a Formatter for each class I want to visit, right?
In "visitor" yes, because the idea of it is exactly avoiding coding in implementations into the class itself.
if you do - you can have
TObject.ToString virtual - and then just use it. But that is exactly what you seek to avoid for a number of reasons.
then, if you create formatters outside of the target class (or other data type) - you indeed have to register them some way.
if nothing else - because otherwise Smart Linker would probably eliminate them like a code no one calls :-)
May You hope for type inferenece to do job for you. Personally i gave up on that in Delphi after trying to run VTV forks working without "record helpers" abomination. Delphi gave up on enhancing it and perhaps so did i. Maybe FPC is noticeably better in this department, dunno.
But i really have issue with your initial
for i in vec do
begin
Result := Result + i.ToString() + ', ';
I believe if you do such an infrastructure - it better be self-sufficient and not rely on TObject.ToString which you seek to replace.
You have to call prettyFormat on "i" too.
And then "i" can, inside itself, call prettyFormat on it's inner memeber, etc, in uniform way.
Also to me the need to write boilerplate like "writeln(specialize prettyFormat<TNumbersL, integer>(numbersL));" on every call looks worse than need to write boilerplate of "RegisterFormatter" once.
To me it is equal to have ad hoc register at eveyr call site.
To me you should just call
Writeln(prettyFormat(numbersL)); and the rest had to be inferred by the compiler (including "specialize", yes :-) ). And if it won't - to me that kills the whole idea.
Your boilerplate has the advantage of being computed in compile-time, rather than runtime dictionary look-ups. But then with code like "Result := Result.remove(Result.length - 2);" you clearly do not optimize for speed anyway, so that is moot point too.
So, personally, all in all, i'd stick with something Visitor-like, in spirit at least.
> but the java version looks more familiar
AFAIR, Java, like C++, lacked TClass concept and had to workaround it inventing CoClasses (COM terms) and Factory classes (Java terms).
As in every languages, patterns emerged to overcome those shortcomings. So many Java patterns when copied to Pascal verbatim indeed look overengineered, because Pascla has different set of limitations.
But "the spirit", the reasoning behind the pattern remain valid.