In komplexen Codebasen sind komplexe Wechselwirkungen von Nebenwirkungen das Schwierigste, über das ich nachdenken kann. Ich kann nur persönlich sprechen, wenn mein Gehirn funktioniert. Nebenwirkungen und anhaltende Zustände und mutierende Eingaben usw. veranlassen mich, über das "Wann" und "Wo" nachzudenken, und nicht nur über das "Was", das in jeder einzelnen Funktion geschieht.
Ich kann mich nicht nur auf "was" konzentrieren. Ich kann nach gründlichem Testen einer Funktion, die Nebenwirkungen verursacht, nicht den Schluss ziehen, dass sie mit ihrem Code einen Hauch von Zuverlässigkeit verbreitet, da Aufrufer sie möglicherweise immer noch missbrauchen, indem sie sie zur falschen Zeit, aus dem falschen Thread, im falschen aufrufen Bestellung. In der Zwischenzeit ist es so gut wie unmöglich, eine Funktion, die keine Nebenwirkungen hervorruft und bei einer Eingabe nur eine neue Ausgabe zurückgibt (ohne die Eingabe zu berühren), auf diese Weise zu missbrauchen.
Aber ich bin ein pragmatischer Typ, denke ich, oder versuche es zumindest, und ich denke nicht, dass wir alle Nebenwirkungen auf ein Minimum reduzieren müssen, um über die Richtigkeit unseres Codes nachzudenken (zumindest) Ich würde es sehr schwierig finden, dies in Sprachen wie C) zu tun. Ich finde es sehr schwierig, über die Korrektheit zu urteilen, wenn wir eine Kombination aus komplexen Kontrollabläufen und Nebenwirkungen haben.
Komplexe Steuerungsabläufe sind für mich diejenigen, die grafischer Natur sind, oft rekursiv oder rekursiv (Ereigniswarteschlangen, die z. B. Ereignisse nicht direkt rekursiv aufrufen, sondern "rekursiv" sind) und möglicherweise Dinge tun Beim Durchlaufen einer tatsächlich verknüpften Diagrammstruktur oder beim Verarbeiten einer inhomogenen Ereigniswarteschlange, die eine eklektische Mischung von zu verarbeitenden Ereignissen enthält, die uns zu den unterschiedlichsten Teilen der Codebasis führen und unterschiedliche Nebenwirkungen auslösen. Wenn Sie versuchen würden, alle Stellen herauszuarbeiten, an denen Sie letztendlich im Code landen, würde dies einem komplexen Diagramm ähneln und möglicherweise Knoten in dem Diagramm enthalten, von denen Sie nie erwartet hätten, dass sie in diesem bestimmten Moment vorhanden waren und dass sie alle vorhanden sind Nebenwirkungen verursachen,
Funktionale Sprachen können äußerst komplexe und rekursive Kontrollabläufe haben, aber das Ergebnis ist in Bezug auf die Korrektheit so einfach zu verstehen, da dabei nicht alle möglichen eklektischen Nebenwirkungen auftreten. Nur wenn komplexe Kontrollabläufe auf eklektische Nebenwirkungen stoßen, empfinde ich es als kopfschmerzanregend, zu versuchen, die Gesamtheit der Vorgänge zu erfassen und festzustellen, ob sie immer das Richtige bewirken.
Wenn ich solche Fälle habe, finde ich es oft sehr schwierig, wenn nicht unmöglich, sehr sicher zu sein, dass ein solcher Code korrekt ist, geschweige denn, dass ich Änderungen an diesem Code vornehmen kann, ohne auf etwas Unerwartetes zu stoßen. Die Lösung für mich ist entweder, den Kontrollfluss zu vereinfachen oder die Nebenwirkungen zu minimieren / zu vereinheitlichen (unter Vereinheitlichung verstehe ich, dass in einer bestimmten Phase des Systems nur eine Art von Nebenwirkung auf viele Dinge einwirkt, nicht zwei oder drei oder a Dutzend). Ich brauche eines dieser beiden Dinge, damit mein simples Gehirn sich sicher fühlen kann, dass der vorhandene Code korrekt ist und die von mir eingeführten Änderungen korrekt sind. Es ist ziemlich einfach, sich auf die Richtigkeit des Codes zu verlassen, der Nebenwirkungen hervorruft, wenn die Nebenwirkungen zusammen mit dem Kontrollfluss einheitlich und einfach sind.
for each pixel in an image:
make it red
Es ist ziemlich einfach, über die Korrektheit eines solchen Codes nachzudenken, aber hauptsächlich, weil die Nebenwirkungen so gleichmäßig sind und der Kontrollfluss so einfach ist. Angenommen, wir hatten Code wie folgt:
for each vertex to remove in a mesh:
start removing vertex from connected edges():
start removing connected edges from connected faces():
rebuild connected faces excluding edges to remove():
if face has less than 3 edges:
remove face
remove edge
remove vertex
Dann ist dies ein lächerlich stark vereinfachter Pseudocode, der normalerweise viel mehr Funktionen und verschachtelte Schleifen und viel mehr Dinge beinhalten würde (Aktualisierung mehrerer Texturabbildungen, Knochengewichte, Auswahlzustände usw.), aber selbst der Pseudocode macht dies so schwierig Grund für die Richtigkeit wegen des Zusammenspiels des komplexen grafischen Kontrollflusses und der auftretenden Nebenwirkungen. Eine Strategie zur Vereinfachung besteht darin, die Verarbeitung aufzuschieben und sich jeweils nur auf eine Art von Nebeneffekten zu konzentrieren:
for each vertex to remove:
mark connected edges
for each marked edge:
mark connected faces
for each marked face:
remove marked edges from face
if num_edges < 3:
remove face
for each marked edge:
remove edge
for each vertex to remove:
remove vertex
... etwas in diesem Sinne als eine Iteration der Vereinfachung. Das bedeutet, dass wir die Daten mehrmals durchlaufen, was definitiv einen Rechenaufwand bedeutet, aber wir stellen häufig fest, dass wir den resultierenden Code leichter multithreaden können, da die Nebeneffekte und Kontrollabläufe diese einheitliche und einfachere Natur angenommen haben. Darüber hinaus kann jede Schleife cachefreundlicher gestaltet werden, als den verbundenen Graphen zu durchlaufen und dabei Nebenwirkungen hervorzurufen (Beispiel: Verwenden Sie ein paralleles Bit-Set, um zu markieren, was durchlaufen werden muss, damit wir die verzögerten Durchläufe in sortierter Reihenfolge ausführen können mit Bitmasken und FFS). Aber am wichtigsten ist, dass ich finde, dass die zweite Version in Bezug auf Korrektheit und Änderung viel einfacher zu überlegen ist, ohne Fehler zu verursachen. Damit'
Und schließlich müssen irgendwann Nebenwirkungen auftreten, sonst hätten wir nur Funktionen, die Daten ausgeben, die nirgendwo hingehen können. Oft müssen wir etwas in eine Datei aufnehmen, etwas auf einem Bildschirm anzeigen, die Daten über einen Socket übertragen, etwas in dieser Art, und all diese Dinge sind Nebenwirkungen. Aber wir können definitiv die Anzahl der überflüssigen Nebenwirkungen reduzieren, die auftreten, und auch die Anzahl der Nebenwirkungen, die auftreten, wenn die Kontrollabläufe sehr kompliziert sind, und ich denke, es wäre viel einfacher, Fehler zu vermeiden, wenn wir dies tun würden.