C ++ zu komplizieren ist unvermeidlich. Und nicht nur C ++

Ich wollte schon lange einen ähnlichen Text schreiben, aber meine Hände reichten nicht. Aber nach dem Ende des Sommertreffens des C ++ - Standardisierungsausschusses und dem Heulen, dass die Komplexität der Sprache noch weiter zunahm, musste ich die Zeit finden und meine eigenen Gedanken zu diesem Thema festlegen.


Es wird viel Text geben, also lade ich diejenigen, die ihre Zeit nicht bereuen, ein, unter die Katze zu schauen.


Programmiersprache ist ein technologisches Produkt, aber nicht so einfach


Vor einiger Zeit hatte ich die Gelegenheit, ein interessantes Buch zu lesen, The Innovator’s Dilemma . Dort wird anhand von Beispielen für Hightech-Produkte gezeigt, wie neue Produkte entstehen, die zunächst gegen die derzeit marktbeherrschenden Entscheidungen verlieren und dann die Marktlage radikal verändern.


Eines der auffälligsten Beispiele: Die digitale Fotografie, die in den 1990er Jahren völlig fehlte, aber nur 20 Jahre später zum Zusammenbruch eines Monsters des 20. Jahrhunderts wie Kodak führte (das übrigens als erstes einen Prototyp einer Digitalkamera herstellte).


Bild

Ein weiteres Beispiel in der Nähe von Entwicklern ist die Entwicklung von Festplatten. Von den gewaltigen Monstern der Mainframe- und Mini-Computer-Ära über die kleinen 3,5- und 2,5-Zoll-Modelle bis hin zu SSDs im M.2-Format, die direkt mit der eMMC-Karte verbunden sind.


Bild

Das Gleiche gilt für Disketten, die zuerst 3,5 Zoll erreichten und dann als Klasse unter dem Druck von USB-Sticks verschwanden.


Bild

Ein weiteres Beispiel aus unserem Berufsfeld ist die Entwicklung von PCs. Die anfangs nicht mit den "echten" Computern der Mitte der späten 1970er Jahre mithalten konnten. Und dann haben sie einfach die Mini-Computer als Klasse zerstört. Dann entstanden moderne Server, die etwas an die damaligen Mini-Computer erinnerten. Und jetzt verschwinden die PCs selbst unter dem Einfluss von Laptops, Tablets und Smartphones aus dem Massenkonsum.


Bild

Die Autoren des Buches erklären, dass sich Ereignisse in all diesen Fällen nach demselben Szenario entwickeln:


  • Erstens besteht ein Bedarf an einem Produkt, das ein bestimmtes Problem löst. Nehmen wir eine flexible Magnetplatte an.
  • Da in der Anfangsphase alle konkurrierenden Lösungen (von denen es im Prinzip nur wenige gibt) auf denselben Technologien basieren, wird der Markt zwischen mehr oder weniger ähnlichen Produkten aufgeteilt.
  • Hersteller erfolgreicher Produkte beginnen miteinander zu konkurrieren und bringen ihre Produkte in diesem Wettbewerb fast auf das günstigste Preis-Leistungs-Verhältnis. Und vor allem, während vorhandene Produkte so konsistent wie möglich mit den Ansichten darüber sind, wie die Aufgaben, für die diese Produkte bestimmt sind, angegangen werden sollen. Grob gesagt weiß jeder, dass 8-Zoll-Disketten hervorragend geeignet sind, um Informationen mithilfe von Disketten von einem Mini-Computer auf einen anderen zu übertragen. Und niemand sonst kann sich im Mainstream einen anderen Weg vorstellen.
  • Irgendwo am Rande, basierend auf einigen neuen Entdeckungen / Materialien / Technologien, erscheint ein neues Produkt, das bestehenden Produkten in fast jeder Hinsicht deutlich unterlegen ist. Generell teurer. In der Regel nicht vergleichbar in Fähigkeiten / Kapazitäten. Aber es hat eine sehr wichtige Qualität: Es kann dort eingesetzt werden, wo es unmöglich oder schwierig ist, Mainstream-Lösungen anzuwenden. Sie können beispielsweise kein 8-Zoll-Laufwerk an einen PC anschließen. PC-Besitzer sind jedoch nicht daran interessiert, 8-Zoll-Disketten mitzuführen. Es ist einfach nicht praktisch. Während eine 5,25 "-Diskette eine Selbstverständlichkeit ist. Und es ist egal, dass eine 5,25" -Diskette auf Kosten eines Kilobyte ursprünglich teurer als 8 war. "Es gab immer noch keine Wahl.
  • Um das neue Produkt herum bildete sich ein neuer Markt, der für die gängigen Produkthersteller zunächst überhaupt nicht interessant war. Da es sich jedoch um einen neuen Markt handelte, strömten neue Spieler dorthin, was zu einem ernsthaften Wettbewerb untereinander führte. Und dieser Wettbewerb setzte marginale und ineffektive Technologien schnell in äußerst effektive um. Was bald aufhörte, marginal zu sein, denn sobald es die alten Mainstream-Produkte in seiner Gesamtheit übertraf, ersetzte es sie schnell in ihrer eigenen Nische. Grob gesagt, wenn es Hunderttausende von 5,25-Zoll-Laufwerken und Millionen von 5,25-Zoll-Disketten gibt, was bringt es dann im Moment, an den viel marginaleren 8-Zoll-Geräten festzuhalten?

Programmiersprachen als technologische Produkte


Auf den ersten Blick lassen sich ähnliche Analogien in der Art und Weise erkennen, wie Programmiersprachen zum Mainstream kamen. Und die vielleicht auffälligsten Beispiele hierfür sind die Sprachen Java und Go.


Die Java-Sprache entstand durch den Versuch, „richtiges C ++“ zu erstellen. Aber die resultierende Sprache würde kaum jemand brauchen, weil Er war sehr elend und vor allem langsam und gefräßig. Auf dem damaligen Desktop hatte Java trotz des rasanten Wachstums der PC-Leistung zu dieser Zeit keine Chance.


Java trat jedoch aus einer völlig anderen Perspektive in den „Markt“ ein, und zwar durch eine Nische, in der damals niemand wirklich arbeiten konnte: über das Internet und Browser-Applets (alles war etwas komplizierter und es gab immer noch einen Markt für JavaCard- und STK-Applets, aber wir werden nicht gehen in der Wildnis). Das Internet war ein sehr heißes Thema, und dann gab es einfach nichts zu tun, um dynamische Websites / Seiten zu erstellen (JavaScript erschien nach der Ankündigung von Java). Und da außer Java nichts zu verwenden war, wurde Java verwendet. Nun, und dann, als es Kinderkrankheiten entwickelte und überwand, ging Java in andere Bereiche und verdrängte von dort aus andere Technologien. Obwohl sie sich auf demselben Desktop befand, konnte sie sich kein bedeutendes Stück vom Kuchen beißen.


Die Go-Sprache konnte kaum jemanden für traditionelle Nischen interessieren, in denen C, C ++, Java, C #, Python, Ruby usw. bereits lebten. Die Entwicklung von Produkten für das Internet hat jedoch eine weitere Nische hervorgebracht - RESTful Services. Für die Entwicklung, bei der Java ein Overkill war, ist C ++ zu kompliziert und gefährlich, Python / Ruby und andere Dynamiken sind zu langsam. Und jetzt wird eine der elendesten in den Ausdrucksformen der im 21. Jahrhundert entwickelten Sprachen fast zu einer Silberkugel für diese angewandte Nische. Von wo aus wird es sich vielleicht im Laufe der Zeit anderswo ausbreiten (was mich angesichts des allgemeinen Qualifikationsniveaus der jungen Entwicklergeneration persönlich nicht überrascht).


Man hat also das Gefühl, dass die Programmierung bei Sprachen die gleiche sein sollte wie bei anderen technologischen Produkten: Die Entstehung neuer Sprachen führt entweder zu einem fast vollständigen Verschwinden oder dazu, dass frühere Sprachen in separate Randnischen verdrängt werden. Gleichzeitig werden alte Sprachen im Zuge ihrer Entwicklung im Rahmen des Wettbewerbsumfelds leistungsfähiger und ausdrucksvoller für billigere Lösungen für ihre typischen Aufgaben. Infolgedessen werden alte Sprachen immer umfangreicher und komplexer. Und weniger attraktiv für den Einsatz außerhalb der Nischen, die sie bereits besetzt haben.


Daher sollte sich der im Innovator's Dilemma beschriebene Lebenszyklus eines technologischen Produkts auch auf Programmiersprachen erstrecken.


Aber mit Programmiersprachen ist nicht alles so einfach


Bei dem im Innovator's Dilemma beschriebenen Schema der Marktdurchdringung neuer technologischer Produkte ist einer der wichtigsten Gründe für Benutzer, von alten Mainstream-Produkten zu neuen zu wechseln, entweder einfach eine signifikante Reduzierung der Betriebskosten oder der Erwerb zuvor verfügbarer Möglichkeiten + eine Verringerung der Betriebskosten.


Die Entwicklung von PCs und das Wachstum ihrer Leistung machen den Besitz einer PC-Flotte billiger als den Besitz eines oder mehrerer Mini-Computer. Infolgedessen sind Drei-Zoll-Disketten pro Kapazitätseinheit billiger als 5,25 Zoll (unter Berücksichtigung einer höheren Zuverlässigkeit und anderer Faktoren). Die digitale Fotografie ist letztendlich pro Bild billiger als Filme. Und so weiter.


Mit dem Übergang von einem Produkt zu einem anderen sind jedoch zwei weitere wichtige Indikatoren verbunden: Dies sind die Kosten / Komplexität des Übergangs selbst sowie die Geschwindigkeit, mit der Sie zu einem neuen Produkt wechseln können. Diese Indikatoren können in Geld geschätzt werden. Und wenn der Nutzen des Übergangs zu einem neuen Produkt vorhanden ist, wird der Übergang durchgeführt. Vielleicht nicht schnell, aber durchgeführt.


Und hier stellt sich heraus, dass die Kosten für den Wechsel von einer Programmiersprache zu einer anderen viel höher sind als beim Wechsel von einem Mini-Computer zu einem PC, von 8 "Disketten auf 5,25" oder von HDD zu SSD. Da das Ändern einer Programmiersprache normalerweise ein vollständiges Umschreiben eines Softwareprodukts ist. Oft von Grund auf neu.


Und was bedeutet Umschreiben? Gehalt eines neuen Teams von Programmierern, die die bereits im Produkt verfügbaren Funktionen wiederholen müssen. Und selbst wenn Sie mit dem neuen PL die Größe des Teams halbieren können, bedeutet dies dennoch erhebliche Kosten. Wenn 10 Millionen US-Dollar für die Entwicklung der alten Version des Produkts ausgegeben wurden, sind für das Umschreiben mindestens 5 Millionen US-Dollar erforderlich.


Noch wichtiger ist jedoch, dass das umgeschriebene Produkt nicht sofort angezeigt wird. Es braucht Zeit. Lange Zeit. Unter der Annahme, dass Sie mit der neuen Sprache doppelt so schnell Arbeitscode schreiben können, dauert das Umschreiben eines Produkts, dessen Entwicklung einer alten Version 5 Jahre gedauert hat, nur 2,5 Jahre.


Es stellt sich heraus, dass Sie jetzt anfangen müssen, viel Geld zu investieren, um eine Kopie von etwas zu erhalten, das schon lange funktioniert und jetzt Geld bringt.


Und noch eine Seite dieser Medaille muss erwähnt werden: Wenn ein Softwareprodukt unter sich ändernden Bedingungen betrieben wird, wird das Produkt zwangsläufig finalisiert oder verarbeitet, um den modernen Anforderungen gerecht zu werden. Gleichzeitig hat das Unternehmen nicht die Möglichkeit, anderthalb Jahre zu warten, bis eine neue Version des Produkts auf einem neuen PL erscheint. In absehbarer Zeit sind in der Regel neue Funktionen erforderlich. Oft gestern.


Daher steigen beim Umschreiben die Kosten: Sie müssen gleichzeitig eine neue Version schreiben und die alte entwickeln.


Meiner Meinung nach erklärt genau dies, warum neue Sprachen "gefeuert" werden, vor allem bei der Entwicklung neuer Anwendungen für eine neue Anwendungsnische. Aber alte Sprachen aus zuvor besetzten Gebieten zu verdrängen ist schon viel langsamer. Fortran und Cobol können wahrscheinlich als die auffälligsten Beispiele angesehen werden. Die darauf geschriebene Software ist nicht nur noch in Betrieb, sondern sie schreiben auch weiterhin neuen Code in diesen Sprachen. Und diese Sprachen selbst entwickeln sich weiter.


Und es scheint mir, dass einer der schlimmsten Träume von Besitzern von Softwareprodukten in Cobol darin besteht, das Produkt in Java oder C # umzuschreiben;)


Und noch ein wichtiger Faktor: die Entwicklung der IT selbst


Ein weiterer Punkt, auf den ich aufmerksam machen möchte, ist die Tatsache, dass die IT vor nicht allzu langer Zeit existiert und die Geschichte der Entwicklung der Hochsprachen noch kürzer ist. Es ist unwahrscheinlich, dass der erste Teil dieser Geschichte uns eine solide Unterstützung für unsere Diskussion darüber bietet, wie einige Sprachen andere ersetzen. Die 1950er und 1960er Jahre waren Jahre des Experimentierens. Darüber hinaus wurden die Jahre, in denen der Computermarkt selbst segmentiert und ein wesentlicher Teil der Software für bestimmte Computer und Betriebssysteme geschrieben wurde, ohne besondere Anforderungen an die Portabilität geschrieben. Die Anzahl der vorhandenen Softwareentwickler sowie die Bandbreite der Bereiche, in denen Computer weit verbreitet sind, können nicht mit dem aktuellen Stand der Dinge verglichen werden.


Meiner Meinung nach haben sich die Dinge in den 1970er Jahren grundlegend geändert, und seit den 1980er Jahren haben wir das Auftreten von Atomwaffen gesehen, die bereits sowohl auf den praktischen Erfahrungen der vergangenen Jahre als auch auf den Ergebnissen theoretischer Studien beruhen. Für mich sind genau die 1980er (und möglicherweise die späten 1970er) der Beginn der Ära der Programmiersprachen, die auf die Herstellung von Software im industriellen Maßstab abzielen. Denn hier sehen wir Modula-2, SmallTalk, Ada, C ++, Eiffel, Objekterweiterungen von Pascal, Objective-C, Perl.


Deshalb werde ich weiter von genau dem ausgehen, was in dieser Ära der industriellen Atomwaffen erschienen ist.


Übrigens


Als ich die Sprachen auflistete, die in den 1980er Jahren erschienen, erinnerte ich mich an den von Niklaus Wirth entwickelten PL: zuerst Pascal, dann Modula / Modula-2, dann Oberon.


Am Beispiel dieser Sprachen kann man sehen, wie die Erfahrung ihres Autors zum Erscheinen von Werkzeugen führt, die die Mängel früherer Versuche berücksichtigen und die neuen Anforderungen ihrer Zeit erfüllen.


Dieselben Sprachen zeigen aber auch, wie wichtig es für Benutzer von YaP ist, im Rahmen der einmal gewählten Sprache zu bleiben. Der Übergang von Pascal zu Modula-2 war. Aber keineswegs massiv. Und trotz der Tatsache, dass Modula-2 mehr oder weniger aktiv eingesetzt wurde, wurde es nicht so populär wie die Erben von Pascal, insbesondere Delphi. Und soweit ich mich erinnere, war überhaupt kein Übergang nach Oberon zu beobachten.


Die beliebte Programmiersprache kann nicht einfach ersetzt werden. Und was ist damit?


Die Hauptbotschaft in meiner vorherigen Diskussion ist also, dass diese Sprache nicht vollständig durch andere Programmiersprachen ersetzt werden kann, wenn eine Sprache mehr oder weniger weit verbreitet ist und mit ihrer Hilfe viele verschiedene Softwareprodukte erstellt wurden, die im täglichen Gebrauch sind. Besonders in kurzer Zeit.


Erfolgreiche Programmiersprachen sind dazu verdammt, jahrelang weiter verwendet zu werden. Und am wahrscheinlichsten zu entwickeln.


Und die Entwicklung einer Programmiersprache beinhaltet die Erweiterung der Sprache um neue Funktionen. Welches ist unvermeidlich, weil Fortschritt steht nicht still. Die Leute finden bequemere Wege, um bekannte Probleme zu lösen. Konfrontiert mit neuen Aufgaben, die zusätzliche Ausdrucksfähigkeiten von Programmiersprachen erfordern. Und da die Programmiersprache nur ein Werkzeug ist, arbeiten die Leute daran, ihr Werkzeug zu verbessern.


Dies bedeutet, dass weit verbreitete Programmiersprachen einfach dazu verdammt sind, im Laufe ihrer Entwicklung immer umfangreicher und komplexer zu werden und Möglichkeiten zu erhalten, die zunächst nicht einmal diskutiert wurden.


Grundsätzlich hat Bjarn Straustrup vor langer Zeit darüber gesprochen. Und selbst das, was ich selbst seit fast dreißig Jahren beobachte, bestätigt die Worte von Straustrup. Angenommen, modernes Java unterscheidet sich bereits stark von Java 1.0 von 1995. Die C # -Sprache zeigt eine noch beeindruckendere Entwicklung von einem erfolgreichen Klon des ersten Java zu der vielleicht ausdrucksstärksten Mainstream-Sprache, die für hinduistische Programmierer geeignet ist (unabhängig von ihrer Nationalität).


Aber das auffälligste Beispiel für mich ist immer noch die Go-Sprache. Was sie bereits im 21. Jahrhundert begannen, indem sie absichtlich eine Reihe von Dingen wegwarfen, die sich in den Jahrzehnten der weit verbreiteten Verwendung in verschiedenen Kernmaterialien bewährt haben. Und welcher Dank, einschließlich dieser, ist populär geworden. Trotzdem fordert das Leben seinen Tribut und Go ist gezwungen, etwas hinzuzufügen, das die Autoren ursprünglich absichtlich abgelehnt haben - Tools für die allgemeine Programmierung (auch bekannt als Vorlagen / Generika) .


Daher entwickeln sich beliebte Programmiersprachen weiter, um ihre Fähigkeiten zu erweitern. Und das bedeutet Komplikationen. Da müssen neue Funktionen hinzugefügt werden, um den bereits geschriebenen Code nicht ernsthaft zu beschädigen. Denn die epische Geschichte mit Python der zweiten und dritten Version wurde ein gutes Beispiel, das nur wenige zu wiederholen wagen.


Ist es so schlimm


Die negative Seite der ständig zunehmenden Komplexität von Programmiersprachen (insbesondere einer Sprache wie C ++) scheint offensichtlich zu sein: Die Eingabeschwelle ist zu hoch. Es muss zu viel Zeit für das Erlernen einer Sprache aufgewendet werden, um in einem akzeptablen Zeitrahmen einen Code von akzeptabler Qualität herauszugeben. Was macht die Entwicklung in einer komplexen Programmiersprache sowohl teuer als auch riskant? Was passiert, wenn ein oder mehrere qualifizierte Entwickler das Projekt verlassen? Wie schnell und einfach wird es sein, einen Ersatz zu finden? Schwierige Fragen.


Da andererseits eine Programmiersprache dasselbe Werkzeug zum Aufzeichnen der Absichten einer bestimmten Person ist, wie beispielsweise mathematische Ausdrücke, ist es angebracht, eine Analogie zur Mathematik zu ziehen.


In der Schule beginnen wir Mathematik zu studieren, beginnend mit den einfachsten arithmetischen Operationen. Dann gehen wir zu komplexeren Dingen über: Brüche, Grade und Wurzeln. Dann gehen wir weiter in Richtung der Logarithmen. Dann nehmen wir eine kleine Integralrechnung. Ähnliches gilt für die Geometrie.


Infolgedessen verfügt ein Absolvent einer normalen High School über einen bestimmten mathematischen Apparat, der für eine einzelne Person möglicherweise überflüssig ist. Ich werde mich nicht irren, wenn ich davon ausgehe, dass viele nach der Schule nie etwas mit Logarithmen berechnen oder Integrale nehmen mussten.


Trotzdem kann der mathematische Apparat, der in der High School beherrscht wird, nicht mit der Tatsache verglichen werden, dass Universitätsstudenten dann Kurse in höherer Mathematik erhalten. Vor allem, wenn es sich um einen Studenten einer mathematischen oder physikalischen Fakultät handelt (und nicht nur ernsthaft höhere Mathematik in vielen Fachgebieten herunterlädt).


Aber es fällt niemandem ein, die Mathematik dafür verantwortlich zu machen, dass es umso schwieriger ist, je tiefer man in sie eintaucht. Da eine Person mit einem Tätigkeitsbereich konfrontiert ist, in dem sie TFKP benötigt, kann nichts unternommen werden, und TFKP muss studieren. Wie schwierig es auch sein mag. Nun ja, es ist normal, dass nicht jeder Erfolg hat.


Eigentlich das gleiche mit Programmiersprachen.


Wenn Sie relativ einfache Probleme lösen müssen, haben Sie die Wahl: Entweder verwenden Sie eine einfachere Programmiersprache oder Sie verwenden eine begrenzte Teilmenge einer komplexeren Sprache. Wenn Sie jedoch vor einer schwierigen Aufgabe (oder bestimmten Bedingungen für deren Lösung) stehen, haben Sie möglicherweise überhaupt keine solche Wahl: Die Komplexität der Lösung dieses Problems in einer „einfachen“ Sprache ist möglicherweise zu groß.


Apropos Aufgaben


, (.. , , , .., ..), , .


. , .. , . , , . , , , . , , , .


, , Go, Python, Ruby PHP, , , , . , , , , . .


, , , , . , 25 GUI , . , 25 .


, , , . , / , , .


, , , , , C++, Scala Haskell. , , . , , Go C.


. . , , : , , . , - C++, . .., -, C++ . , -, C++ , , .


Insgesamt


, ? :


  • -, . , , , « », 1960-1970- , . , . , . ( ). - X, , X . , C. , . . , — . ;
  • -, , 40 , . , , . , .. , , ;
  • -, . - Ruby/Python, - Go, - Java. - Rust-, C++, Scala Haskell-. , . - , « », . Und das ist normal.

, , - : , - . - . , ( , , ). , - , , : , , -. ;)

Source: https://habr.com/ru/post/de463745/


All Articles