Ich bin sicher, dass für viele "Eleganz"-Probleme statischer Sprachen nicht die statische Typprüfung selbst verantwortlich ist, sondern die mangelnde Ausdruckskraft des in der Sprache implementierten statischen Typensystems und die begrenzten Möglichkeiten des Compilers. Wenn dies "richtiger" gemacht wird (wie z.B. in Haskell), dann werden die Programme plötzlich knapper, eleganter und sicherer als ihr dynamisches Gegenstück.
Hier eine Illustration (C++-spezifisch, sorry): C++ ist so mächtig, dass es eine Metasprache mit seinem Template-Klassensystem. Aber dennoch ist eine sehr einfache Funktion schwer zu deklarieren:
template<class X,class Y>
? max(X x, Y y)
Es gibt eine erstaunliche Anzahl möglicher Lösungen, wie z. B. ?= boost::variant<X,Y>
oder die Berechnung von ?= is_convertible(X,Y)?(X:is_convertible(Y,X):Y:error)
keine davon ist wirklich befriedigend.
Stellen Sie sich nun einen Präprozessor vor, der ein Eingabeprogramm in eine äquivalente Form der Weitergabe von Fortsetzungen umwandeln kann, wobei jede Fortsetzung ein aufrufbares Objekt ist, das alle möglichen Argumenttypen akzeptiert. Eine CPS-Version von max würde wie folgt aussehen:
template<class X, class Y, class C>
void cps_max(X x, Y y, C cont) // cont is a object which can be called with X or Y
{
if (x>y) cont(x); else cont(y);
}
Das Problem ist gelöst, max ruft eine Fortsetzung auf, die X oder Y akzeptiert. Es gibt also eine Lösung für max mit statischer Typüberprüfung, aber wir können max nicht in seiner Nicht-CPS-Form ausdrücken, untransform(cps_max)
ist sozusagen undefiniert. Wir haben also ein Argument, dass max
richtig gemacht werden kann, aber wir haben nicht die Mittel dazu. Das ist der Mangel an Ausdruckskraft.
Aktualisierung für 2501: Angenommen, es gibt einige Unabhängig Typen X und Y und es gibt eine bool operator<(X,Y)
. Was sollte max(X,Y)
zurückkehren? Nehmen wir weiter an, dass X und Y beide eine Mitgliedsfunktion haben foo();
. Wie könnten wir es möglich machen, zu schreiben:
void f(X x, Y y) {
max(X,Y).foo();
}
entweder X oder Y zurückzugeben und foo() auf dem Ergebnis aufzurufen, ist für eine dynamische Sprache kein Problem, für die meisten statischen Sprachen jedoch nahezu unmöglich. Wir können jedoch die gewünschte Funktionalität erreichen, indem wir f() so umschreiben, dass es cps_max verwendet:
struct call_foo { template<class T> void operator(const T &t) const { t.foo(); } };
void f(X x, Y y) {
cps_max(x,y,call_foo());
}
Für die statische Typprüfung kann dies also kein Problem sein, aber es sieht sehr hässlich aus und geht nicht über einfache Beispiele hinaus. Was fehlt also in dieser statischen Sprache, dass wir keine statische Typenprüfung anbieten können? y lesbare Lösung.