STDOUT und seine Verunreinigung

10

Ich habe viele Bücher und Artikel über funktionale Programmierung gelesen und schäme mich immer noch, einige sehr grundlegende Konzepte nicht sicher verstehen zu können.

Eine der Hauptideen der funktionalen Programmierung ist, dass dieselbe Eingabe immer dieselbe Ausgabe erzeugen sollte. Daher könnte beispielsweise das Abfragen von Datenbanken oder das Schreiben von Dateien per Definition nicht in einem rein funktionalen Stil erfolgen. Das ist zum Beispiel einer der Gründe, warum wir Monaden brauchen.

Die Frage ist - warum betrachten wir die STDOUT-Ausgabe als etwas Unreines? Ja, jeder Dateihandler ist riskant - wir können nie sicher sein, dass Daten immer geschrieben werden. Aber was ist mit STDOUT? Warum sollten wir es als etwas Unzuverlässiges betrachten? Ist es unzuverlässiger, dass die Bewertung selbst? Ich meine, wir können immer den Abzug betätigen und damit die Berechnung unterbrechen.

shabunc
quelle

Antworten:

6

Daher könnte beispielsweise das Abfragen von Datenbanken oder das Schreiben von Dateien per Definition nicht in einem rein funktionalen Stil erfolgen. Das ist zum Beispiel einer der Gründe, warum wir Monaden brauchen.

Niemand "braucht" Monaden, das ist nur eine Möglichkeit, Dinge zu beschreiben. In der Tat ist es wahrscheinlich nicht einmal der beste Weg. Eine Form der Effekt-Typisierung , Eindeutigkeitstypen oder ein System, das auf einer vollständigen linearen Logik basiert, scheinen theoretisch überzeugender zu sein, sind jedoch radikalere Abweichungen von bekannten Typsystemen und komplizierter auszudrücken. Monadic IO, wie es in Haskell zu finden ist, ist ein Kompromiss zwischen Benutzerfreundlichkeit und Einfachheit, da es im Wesentlichen eine vollständig zwingende Programmierung auf eine Weise modelliert, die problemlos mit dem vorhandenen ML-Typ-System koexistiert, das bereits in der Sprache verwendet wird.

Die Frage ist - warum betrachten wir die STDOUT-Ausgabe als etwas Unreines? Ja, jeder Dateihandler ist riskant - wir können nie sicher sein, dass Daten immer geschrieben werden. Aber was ist mit STDOUT? Warum sollten wir es als etwas Unzuverlässiges betrachten? Ist es unzuverlässiger, dass die Bewertung selbst? Ich meine, wir können immer den Abzug betätigen und damit die Berechnung unterbrechen.

Es ist nicht und wir nicht. Die Eingabe in und Ausgabe von dem Programm als Ganzes kann einfach als Argumente angesehen werden und ergibt sich aus der Behandlung des gesamten Programms als eine große reine Funktion. Solange es dasselbe an stdout druckt, wenn Sie dasselbe von stdin füttern, ist es immer noch eine reine Funktion. Vor der Einführung der monadischen E / A verwendete Haskell ein Stream-basiertes E / A-System, das reine Lazy-Streams für die Eingabe und Ausgabe verwendete. Es hat es fallen lassen, weil es anscheinend schmerzhaft war, es zu benutzen, was Ihnen eine Vorstellung davon geben kann, warum Sie von so etwas noch nichts gehört haben. :]

Betrachten Sie die minimalistische esoterische Sprache Lazy K :

Lazy K ist eine durch Müll gesammelte, referenziell transparente funktionale Programmiersprache mit einem einfachen Stream-basierten E / A-System.

Was Lazy K von anderen solchen Sprachen unterscheidet, ist das fast völlige Fehlen anderer Merkmale. Es bietet beispielsweise kein integriertes polymorphes Hindley-Milner-System. Es wird nicht mit einer umfangreichen Standardbibliothek geliefert, die plattformunabhängige GUI-Programmierung und Bindungen an andere Sprachen unterstützt. Eine solche Bibliothek konnte auch nicht geschrieben werden, da Lazy K unter anderem keine Möglichkeit bietet, andere Funktionen als integrierte Funktionen zu definieren oder auf diese zu verweisen. Diese Unfähigkeit wird durch einen passenden Mangel an Unterstützung für Zahlen, Zeichenfolgen oder andere Datentypen ergänzt. Trotzdem ist Lazy K Turing-komplett.

(...)

Lazy K-Programme leben im gleichen zeitlosen platonischen Bereich wie mathematische Funktionen, was die Unlambda-Seite "den gesegneten Bereich des reinen untypisierten Lambda-Kalküls" nennt. So wie die Garbage Collection den Prozess der Speicherverwaltung vor dem Programmierer verbirgt, verbirgt die referenzielle Transparenz den Prozess der Auswertung. Die Tatsache, dass einige Berechnungen erforderlich sind, um ein Bild des Mandelbrot-Sets anzuzeigen oder um ein Lazy K-Programm "auszuführen", ist ein Implementierungsdetail. Das ist die Essenz der funktionalen Programmierung.

(...)

Wie gehe ich mit Ein- und Ausgaben in einer Sprache ohne Nebenwirkungen um? In gewissem Sinne sind Eingabe und Ausgabe keine Nebenwirkungen. es sind sozusagen Front- und Backeffekte. So ist es in Lazy K, wo ein Programm einfach als eine Funktion vom Raum möglicher Eingaben zum Raum möglicher Ausgaben behandelt wird.

Ich bezweifle, dass Sie eine rein funktionale Sprache finden werden!


Beachten Sie jedoch, dass das oben Gesagte nur gilt, wenn Sie im Wesentlichen die Eingabe und Ausgabe einer reinen Funktion übernehmen und sie in irgendeiner Weise "extern" mit stdin / stdout verbinden. Es gibt einen großen Unterschied zwischen dem und dem Zugriff auf die realen E / A-Grundelemente auf Systemebene. Die Implementierungsdetails des Lesens und Schreibens in die Ströme können Verunreinigungen verlieren, wenn sie nicht sorgfältig gekapselt werden.

Ich gehe davon aus, dass dies der Hauptgrund dafür ist, dass Sie dies nicht direkt in Haskell tun können - die vernünftigen Anwendungsfälle sind im Vergleich zur Verwendung von monadischen E / A schlank, und für letztere bietet der Zugriff auf die reale Sache viele Vorteile. Ich glaube, deshalb werden zum Beispiel die Befehlszeilenargumente an das Programm nicht einfach als Argumente übergeben main, obwohl es intuitiv zu sein scheint.

Sie können jedoch eine minimale Version von so etwas in einem bestimmten Programm wiederherstellen - erfassen Sie einfach die Argumente als reine Werte und verwenden Sie dann die interactFunktion für den Rest Ihres Programms.

CA McCann
quelle
Sir, ich muss gestehen, ich freue mich über jede Ihrer Antworten auf einem der Stapel. Sie sollten auf jeden Fall ein Buch Haskell schreiben und ich mache keine Witze.
Shabunc
@ Shabunc: Ich habe mich manchmal gefragt, wie nahe die Summe meiner Antworten auf SO bereits an der Größe eines Buches liegt ...
CA McCann
Können Sie ein Beispiel für ein System geben, das auf vollständiger linearer Logik basiert? Das scheint interessant, wenn es existiert.
Konfigurator
@configurator: Beachten Sie, wie ich für die anderen auf bestimmte Sprachen verlinkt habe, aber eine Wikipedia-Seite für lineare Logik? Wenn ich ein Beispiel hätte, hätte ich es leider gegeben. : [Ich habe nur von Teilprototypen und experimentellen Systemen aus der CS-Forschung gehört. Wenn Sie sich eingehender damit befassen möchten, finden Sie hier ein relativ zugängliches Material zu linearen Systemen , mit dem Sie möglicherweise beginnen können.
CA McCann
3

Während Reinheit in einem funktionalen Programm ein würdiges Ziel ist, ist die Realität, dass jedes nicht triviale, nützliche Programm aus den von Ihnen genannten Gründen eine gewisse Verunreinigung (oder "Nebenwirkungen") aufweist.

Völlig reine Programme sind per Definition eine versiegelte Black Box und im Wesentlichen uninteressant.

Die Funktionssprache Haskell behebt dieses Problem, indem sie Nebenwirkungen wie die Ausgabe in Monaden isoliert . Die Monade behält einen rein funktionalen Programmierstil bei und ermöglicht dennoch die Ausgabe.

Robert Harvey
quelle
Sicher, du hast recht. Aber ich verstehe, warum 100% Reinheit Utopie ist. Ich frage ist nur über STDOUT.
Shabunc
1
STDOUT ist wie jeder andere ein Nebeneffekt. Intern würde die Monade alle erforderlichen Fehlerprüfungen durchführen.
Robert Harvey
Ja, darum geht es bei dieser Frage - warum wird sie wie jede andere als Nebenwirkung angesehen?
Shabunc
2
Alles, was die Außenwelt verändert, wird als Nebeneffekt angesehen.
Robert Harvey
1

Das Schreiben in STDOUT kann fehlschlagen, wenn Sie nicht mit einem Endgerät verbunden sind oder wenn Sie (aus irgendeinem Grund) den Dateideskriptor dafür geschlossen haben.

Außerdem ist STDOUT nicht immer "der Konsolenbildschirm". Manchmal wird es an ein anderes Programm weitergeleitet. Manchmal ist das Rohr gebrochen.

Yam Marcovic
quelle
0

Es hilft, wenn Sie an Reinheit denken, indem Sie "den Zustand der Außenwelt verändern". Dies kann das Schreiben in eine Konsole, eine Protokolldatei, das Auswerfen der CD oder das "Starten der Raketen" umfassen.

Dies kann auch ein Problem bei der gleichzeitigen Ausführung sein. Wenn Sie wissen, dass eine Funktion keine Nebenwirkungen hat, können Sie leicht eine Parallelität veranlassen, da Sie nachweisen können, dass es keine Rennbedingungen oder ähnliches geben kann.

Zachary K.
quelle
Ändert den Zustand der Außenwelt oder hängt vom Zustand der Außenwelt ab. Weitere Informationen finden Sie in dieser Frage .
MatrixFrog