Ich sehe viele Texte, insbesondere funktionale Programmiertexte, die behaupten, dass bestimmte CS-Konzepte "nicht komponieren" . Beispiele sind: Sperren komponieren nicht, Monaden komponieren nicht.
Es fällt mir schwer, genau die Bedeutung dieses Satzes zu finden. Wenn ich an Komposition denke, denke ich entweder an Funktionskomposition oder an Objektaggregation (wie in "Komposition vor Vererbung bevorzugen"), aber das scheint nicht der Sinn zu sein, in dem die Leute es hier verwenden.
Kann jemand erklären, was dieser Ausdruck bedeutet, wenn er in Ausdrücken wie den beiden obigen (dh Sperren und Monaden) verwendet wird?
Antworten:
Wenn Leute sagen "X komponiert nicht", bedeutet "komponieren" eigentlich nur "zusammensetzen", und was und wie man sie zusammensetzt, kann sehr unterschiedlich sein, je nachdem, was genau "X" ist.
Wenn sie "nicht komponieren" sagen, können sie auch ein paar leicht unterschiedliche Dinge bedeuten:
Ein Beispiel für # 1 sind Parser mit Scannern / Lexern. Möglicherweise hören Sie den Satz "Scanner / Lexer komponieren nicht". Das stimmt eigentlich nicht. Was sie bedeuten, ist "Parser, die eine separate Lexing-Stufe verwenden, komponieren nicht".
Warum sollten Sie Parser erstellen? Stellen Sie sich vor, Sie sind ein IDE-Anbieter wie JetBrains, die Eclipse Foundation, Microsoft oder Embarcadero und möchten eine IDE für ein Webframework erstellen. In der typischen Webentwicklung mischen wir oft Sprachen. Sie haben HTML-Dateien mit
<script>
Elementen, die ECMAScript und enthalten<style>
Elemente, die CSS enthalten. Sie haben Vorlagendateien, die HTML, eine Programmiersprache und eine Metasyntax für Vorlagensprachen enthalten. Sie möchten keine unterschiedlichen Syntaxmarker für "Python", "In eine Vorlage eingebettetes Python", "CSS", "CSS in HTML", "ECMASCript", "ECMAScript in HTML", "HTML", "HTML in" schreiben eine Vorlage ", und so weiter und so fort. Sie möchten einen Syntax-Textmarker für Python, einen für HTML und einen für die Vorlagensprache schreiben und die drei dann zu einem Syntax-Textmarker für eine Vorlagendatei zusammensetzen.Ein Lexer parst jedoch die gesamte Datei in einen Strom von Token, was nur für diese eine Sprache Sinn macht. Der Parser für die andere Sprache kann nicht mit den Tokens arbeiten, die der Lexer übergibt. Beispielsweise werden Python-Parser in der Regel so geschrieben, dass der Lexer die Einrückung nachverfolgt und Fälschungen
INDENT
undDEDENT
Token in den Token-Stream einfügt, sodass der Parser kontextfrei ist, obwohl Pythons Syntax dies nicht ist. Ein HTML-Lexer ignoriert jedoch Whitespace vollständig, da es in HTML keine Bedeutung hat.Ein scannerloser Parser, der lediglich Zeichen liest, kann den Zeichenstrom an einen anderen Parser weiterleiten, der ihn dann zurückgibt und so die Komposition erheblich vereinfacht.
Ein Beispiel für # 2 sind Zeichenfolgen mit darin enthaltenen SQL-Abfragen. Sie können zwei Zeichenfolgen verwenden, in denen jeweils eine syntaktisch korrekte SQL-Abfrage enthalten ist. Wenn Sie die beiden Zeichenfolgen jedoch verketten, ist das Ergebnis möglicherweise keine syntaktisch korrekte SQL-Abfrage. Deshalb haben wir Abfrage algebras wie
ARel
, was tun compose.Schlösser sind ein Beispiel für # 3. Wenn Sie zwei Programme mit Sperren haben und diese zu einem einzigen Programm kombinieren, haben Sie immer noch ein Programm mit Sperren, aber selbst wenn die beiden ursprünglichen Programme völlig korrekt und frei von Deadlocks und Rennen waren, muss das resultierende Programm dies nicht unbedingt haben Eigentum. Die korrekte Verwendung von Sperren ist eine globale Eigenschaft des gesamten Programms und wird beim Erstellen von Programmen nicht beibehalten. Dies unterscheidet sich von zum Beispiel Transaktionen, die tun compose. Ein Programm, das Transaktionen korrekt verwendet, kann mit einem anderen solchen Programm zusammengesetzt werden und ergibt ein kombiniertes Programm, das Transaktionen korrekt verwendet.
quelle
Composability bedeutet, dass Sie Programmkomponenten einfach und zuverlässig miteinander kombinieren können, um größere Komponenten und komplexere Funktionen zu erhalten.
Einige Dinge, die helfen, Komponenten zusammensetzbarer zu machen:
Idempotenz. Eine idempotente Funktion erzeugt immer dieselbe Ausgabe oder dieselben Nebenwirkungen, wenn sie mehrmals mit denselben Parameterwerten aufgerufen wird. Dies verbessert die Kompositionsfähigkeit, da das Ergebnis eines Funktionsaufrufs vorhersehbar ist.
Referentielle Transparenz. Ein referenziell transparenter Ausdruck wird immer zum gleichen Ergebnis ausgewertet. Dies verbessert die Kompositionsfähigkeit, da identische Ausdrücke gegeneinander ausgetauscht werden können und Ausdrücke unabhängig voneinander (dh in verschiedenen Threads) berechnet werden können, ohne Sperren zu verwenden.
Unveränderlichkeit. Der Status eines unveränderlichen Objekts kann nach seiner Erstellung nicht mehr geändert werden. Dies verbessert die Kompositionsfähigkeit, da Sie sich auf einen stabilen Wert des Objekts verlassen können, ohne sich Sorgen machen zu müssen, ob eine Funktion oder ein Objekt irgendwo den Status des Objekts geändert hat, nachdem es erstellt wurde.
Reinheit. Reine Funktionen haben keine Nebenwirkungen. Sie haben nur eine Eingabe und eine Ausgabe, wodurch sie komponierbarer sind, da Sie die Ausgabe einer Funktion in die Eingabe einer anderen Funktion einfügen können, ohne sich Gedanken darüber machen zu müssen, ob sich etwas außerhalb der Funktion geändert hat.
Sperren werden nicht komponiert, da sie ein externes Element sind, auf das Sie sich verlassen müssen, wenn Sie zwei Operationen miteinander kombinieren, die einen bestimmten Status gemeinsam haben, und dies aus den unterschiedlichsten Gründen, die mit der Komplexität der Verwendung von Sperren zusammenhängen.
Der Satz "Monaden komponieren nicht" ergibt für mich keinen Sinn. Der springende Punkt bei einer Monade ist es, einen Zustand wie die Tastatureingabe oder die Bildschirmausgabe in eine reinere, mathematische Form umzuwandeln, die in der Tat komponierbarer ist.
quelle
IO
Typ, der "zustandsbehaftete Aktionen" wie Tastatureingaben erfasst. Werte desIO
Typs zu tun, in der Tat, compose, aber es ist die ganze ArtIO
, die eine Monade ist. Dieser Typ kann nicht mit anderen Typen kombiniert werden, die Monaden wie beispielsweise der Listentyp sind. Das heißt, wir können nicht systematisch einen Typ erzeugen,IO . List
der sich sowohlIO
als auchList
gleichzeitig verhält . Ist das sinnvoll? Ich bin mir nicht sicher, ob ich es gut erklärt habe.