In letzter Zeit bin ich immer frustrierter geworden, dass es in den meisten modernen Programmiersprachen, mit denen ich gearbeitet habe (C / C ++, C #, F #, Ruby, Python, JS und mehr), kaum Sprachunterstützung gibt, um festzustellen, was Ein Unterprogramm reicht tatsächlich aus.
Betrachten Sie den folgenden einfachen Pseudocode:
var x = DoSomethingWith(y);
Wie bestimme ich, was der Aufruf von DoSomethingWith (y) tatsächlich bewirkt ? Wird es y mutieren oder wird es eine Kopie von y zurückgeben ? Hängt es vom globalen oder lokalen Zustand ab oder ist es nur von y abhängig ? Wird es den globalen oder lokalen Staat verändern? Wie wirkt sich die Schließung auf das Ergebnis des Anrufs aus?
In allen Sprachen, auf die ich gestoßen bin, kann fast keine dieser Fragen durch bloßes Betrachten der Signatur des Unterprogramms beantwortet werden, und es gibt fast nie Unterstützung für die Kompilierung oder Laufzeit. Normalerweise besteht die einzige Möglichkeit darin, dem Autor der API Ihr Vertrauen zu schenken und zu hoffen, dass die Dokumentations- und / oder Namenskonventionen zeigen, was die Unterroutine tatsächlich tut.
Meine Frage lautet: Gibt es heute Sprachen, die symbolisch zwischen diesen Arten von Szenarien unterscheiden und die Einschränkungen für die Kompilierungszeit für den Code festlegen, den Sie tatsächlich schreiben können?
(Natürlich gibt es in den meisten modernen Sprachen eine gewisse Unterstützung dafür, wie z. B. unterschiedliche Ebenen des Umfangs und der Schließung, die Trennung zwischen statischem Code und Instanzcode, Lambda-Funktionen usw. Aber zu oft scheinen diese miteinander in Konflikt zu geraten. Beispielsweise ist eine Lambda-Funktion normalerweise entweder rein funktional und gibt einfach einen Wert zurück, der auf Eingabeparametern basiert, oder mutiert die Eingabeparameter auf irgendeine Weise. In der Regel ist es jedoch möglich, über eine Lambda-Funktion auf statische Variablen zuzugreifen, was wiederum möglich ist Geben Sie Zugriff auf Instanzvariablen, und dann bricht alles auseinander.)
quelle
Antworten:
Ja, du willst dir Haskell ansehen. Es macht genau das, was Sie wollen. Alle Funktionen sind standardmäßig rein und können den Status nur mithilfe von Monaden ändern. Auch Haskell hat sehr starke statische Garantien für alle möglichen Dinge http://learnyouahaskell.com/
quelle
C und C ++ unterstützen zumindest einen Teil des Problems nur sehr begrenzt über das
const
Schlüsselwort. Dies allein kontrolliert zwar nicht die Reinheit, kann jedoch (insbesondere in C ++) verwendet werden, um dem Compiler mitzuteilen, dass eine bestimmte Datenstruktur nicht über einen bestimmten Zeiger geändert werden soll. Einige Compiler (z. B. gcc) bieten auch ein 'pure'-Attribut als Spracherweiterung, um die Reinheit vollständig durchzusetzen. (Siehe diese Frage für Details).Die Programmiersprache D unterstützt das explizite Deklarieren der Reinheit von Funktionen, und Compiler überprüfen und erzwingen die Reinheit (dh der Versuch, eine nicht reine Funktion aus einer reinen Funktion heraus aufzurufen, führt zu einem Compilerfehler).
Haskell ist völlig rein, das heißt, die Sprache selbst kann keine unreinen Funktionen ausdrücken, und es gibt kein Konzept einer "Routine". Alles, was mit reinen Funktionen allein nicht gelöst werden kann, wird an die (unreine) Laufzeit ausgelagert; Ein Programm mit Nebenwirkungen wird implementiert, indem (ausschließlich aus reinen Konstrukten) eine verzögerte Datenstruktur erstellt wird, die das Programmverhalten beschreibt, und diese dann zur Laufzeit übergeben wird. Die Haskell-Community experimentiert aktiv mit einem ganzen Zoo von Programmiersprachen, von denen einige rein und andere explizit rein sind.
Es mag noch mehr geben, aber das sind die, mit denen ich vertraut bin.
quelle
Felix hat anscheinend drei Kategorien:
Funktionen
Verfahren
Generatoren
quelle