Ist es wichtig, dass eine Programmiersprache den Entwickler unterstützt, oder reicht eine gute IDE dafür aus?
Programmiersprachen werden oft auf ihre Syntax reduziert.
Sie bieten aber vor allem Konzepte an (ich nenne sie gerne Mechanismen).
Diese Konzepte sind dabei recht vielfältig.
Beispiele: Funktionen, Pointer (->C), Verweise, Vererbung, Kapselung, Delegation, Dispatch, Typensystem, Namensräume, Module, Lambda-Ausdrücke, Currying (->Haskell), Default-Parameter, Named-Parameter, Lazy-Evaluation (->Haskell), Deklarative Programmierung (->Prolog), Prototypen-Vererbung (->javascript), Code als Daten (->LISP), Stack-Orientierung (->Forth), Mehrdimensionalität (->APL), missing_method (->Ruby), usw.
Man kommt dem am ehesten näher, wenn man Programmiersprachen direkt und im Detail miteinander vergleicht.
Selbst wenn eine Sprache grob gesehen dieselben Möglichkeiten bietet, machen doch oft Feinheiten einen großen Unterschied. Z.B. eröffnet die schiere Existenz von missing_method in Ruby einige Vorgehensweise, die anderen Sprachen verwehrt bleibt. Auch können Mechanismen sehr unterschiedlich ausgeführt sein, z.B. ist die Vererbung in C++ traditionell eine Implementierungsvererbung (für eine Compilersprache naheliegend), was einige Folgen hat. Die Prototypenvererbung von javascript hat dagegen vielfach andere Eigenschaften.
Programmiersprachen sind Sprachen, die die Definition und Lösung eines Problems möglichst effizient ermöglichen sollen. Im Idealfall denkt man in der Programmier-Sprache über das Problem nach.
APL, Wikipedia: "Bevor der erste Interpreter verfügbar war, diente APL bereits als Metasprache für die Beschreibung der Architektur des damals neu entwickelten Computersystems IBM System/360 aus der Sicht des Programmierers".
Die Sprache ist also zunächst ein Formalismus, vergleichbar mit der mathematischen Formel-Sprache, die auch nur eine effiziente und präzise Ausdrucksweise zum Ziel haben. Programmiersprachen haben dazu die zusätzliche Eigenschaft, dass sie auch vom Computer verstanden werden. heutige Mathematik-Systeme zeigen, dass dies durchaus auch mit mathematischen Formeln funktioniert.
Je größer der Werkzeugkasten an Mechanismen ist und je besser er zum jeweiligen Problem passt, desto einfach wird es, das Problem zu beschreiben und zu lösen.
Es gibt Spezialsprachen, die auf den Problembereich zugeschnitten sind (-> domain specific language = DSL). Dagegen bieten Universalsprachen gleich einen ganzen Haufen von Mechanismen an.
Auch wenn die Verfechter einzelner Universalsprachen gerne behaupten, die Sprache könne schlichtweg alles, wird man feststellen, dass dies nicht der Fall ist. Gewisse Mechanismen widersprechen sich nämlich. Die deduktive Vorgehensweise von Prolog lässt sich nicht einfach mit der sequenziellen Sichtweise von C verbinden. Es gibt durchaus Versuche, Kombinationen zu kreieren, aber dadurch wird die gefühlte Reinheit (Klarheit, Eleganz, …) der Sprache meistens reduziert. Die Glaubenskriege der Sprachen-Fanatiker werden uns also vermutlich erhalten bleiben.
Die Sprachmittel beeinflussen die Denkweise des Programmierers. Deswegen ist die Wahl der Sprache aus meiner Sicht nicht unwichtig.
Kennt man als Programmierer viele (vor allem sehr unterschiedliche) Sprachen, kommt man aber immer mehr dazu in Mechanismen zu denken. Man möchte irgendwann nicht mehr sein Denken den Möglichkeiten der aktuell verwendeten Sprache unterordnen. Ich persönlich sehe inzwischen bei fast jeder Sprache eine Einschränkung dessen, was ich in Mechanismen eigentlich formulieren möchte.
Zum Thema IDE:
Sie kann in der Regel syntaktische Probleme (wie z.B. die eigentlich unnötige Wiederholung von langen Typen-Namen erleichtern. Nicht vorhandene Mechanismen können durch eine IDE aber nur in seltenen Fällen hinzugefügt werden.
Beispiel in fiktiven Sprachen:
BlaBlaBlaBlubb x = new BlaBlaBlaBlubb(…)
die IDE bietet beim zweiten Namen den schon geschriebenen an.
auto x = new BlaBlaBlaBlubb(…)
ist aber trotzdem besser zu lesen, denn der Leser muss den linken Namen mit dem rechten vergleichen, während in der zweiten Variante klar ist, dass x denselben (deduzierten) Typ hat. Wer jetzt sagt, dass die beiden Namen immer gleich sind, liegt falsch, denn es könnte auch
BlaBlaBlaBlubb x = new BlaBlaBlaBlubbS(..:)
sein, also eine Spezialisierung wird erzeugt und einer allgemeineren Referenz zugewiesen. Die IDE bietet also zwar Hilfestellung beim Schreiben, löst aber nur diese Hälfte des Problems, nicht aber die Lese-Seite. Das ist also eine "nach mir die Sintflut"-Strategie.
Da die allermeisten Sprachen Turing-Vollständigkeit bieten, kann man einige Mechanismen irgendwie nachbilden (z.B. auch durch Libraries, Templates, Makros) und eine IDE kann theoretisch diese Nachbildung unterstützen. Das Ergebnis ist aber in der Regel schlechter lesbar und komplexer als eine Sprache, die den Mechanismus direkt anbietet. Oft ist dann auch die Ausführung ineffizienter. Preprozessoren können diese Mängel eventuell lösen, aber das ist letztlich die Einführung einer neuen Sprache, hier eben implementiert durch einen Precompiler. So wurde beispielsweise C++ zuerst als Precompiler implementiert und erzeugte daraus recht unleserliches C. Je weiter weg ein Mechanismus von der Zielsprache liegt, desto komplexer wird das Ergebnis.
Bei Nachbildungen von Mechanismen zeigt sich übrigens die Fähigkeit von Sprachen auf einer Meta-Ebene über sich selbst zu sprechen (Meta-Programmierung). Im Extremfall kann eine Sprache sogar sich selbst erweitern. LISP ist ein gutes Beispiel dafür, was alles möglich ist (und umso erstaunlicher wenn man bedenkt wie alt diese Sprache ist).
Ebenso ist ein Qualitätskriterium, ob es bei Metaprogrammierung einen Bruch zu den sonstigen Sprachelementen gibt. Beispielsweise kann man in C/C++ zwar eine neue Schleifenart (z.B. Parallelausführung) zwar durch eine Funktion und Funktions-Pointer (bzw. mittlerweile auch Lambda-Ausdrücke) nachbilden, aber da man keine Statements erzeugen kann, sieht das Ergebnis eben nicht wie eine normale Schleife aus. Sprachen die gar keine Statements haben (man braucht sie nämlich nicht!) haben dieses Problem nicht. Eine Sprache wie Ruby hat zudem die Erweiterung in dieser Richtung schon im Design vorgesehen (Block-Parameter), was zur Folge hat, dass man dort die Sprache vollkommen transparent um weitere Kontroll-Kontrukte erweitern kann, die sich nahtlos einfügen. LISP konnte all dies übrigens auch ohne mit der Wimper zu zucken. Viele der heutigen Mechanismen wurden zuerst in LISP erforscht (z.B. Objektorientierung), weil LISP eben nahtlos über sich selbst reden kann.
Generell sind hier Sprachen im Vorteil, die eine möglichst einfache orthogonale Syntax haben. Die Anlehnung an natürliche Sprachen habe ich immer als Nachteil empfunden (recht extrem: COBOL). Ein Formalismus sollte knapp und einfach strukturiert sein, das führt zu klarerem und eindeutigeren Denken.
Mein Fazit ist also, IDEs vertuschen letztlich nur Schwächen einer Sprache (die Bereiche, die üblicherwiese gegen Sprachen ins Feld geführt werden). Eine Sprache mit zu wenig Fähigkeiten (für das Problem) profitiert zwar von der IDE, aber eine gute Sprache ermöglicht direkteres Formulieren des Problems. IDE als Ersatz für Sprach-Mechnismen führt zu Wegwerfcode.