Reinste funktionale Programmiersprache (n)? [geschlossen]


20

Ich bin daran interessiert, funktionale Programmierung besser zu lernen. Es liegt auf der Hand, dass ich mich dazu zwingen sollte, eine möglichst reine funktionale Programmiersprache zu verwenden. Daher bitte ich hier mehr oder weniger darum, funktionale Programmiersprachen nach ihrer Reinheit zu ordnen.

Es scheint mir, dass es praktischer wäre, Lisp oder Clojure (oder Scheme oder Scala usw.) zu lernen, aber was ich kürzlich gehört habe, ist es sehr schwer, Haskell zu schlagen, wenn er jemandem Prinzipien der funktionalen Programmierung beibringt. Da ich mir noch nicht sicher bin, frage ich Sie: Welche ist die reinste funktionale Programmiersprache? Eine Bestellung wäre toll, wenn mehrere um den großartigen Titel der reinsten funktionalen Programmiersprache konkurrieren.


2
Ich habe Miranda an der Universität gelernt, deshalb bin ich verärgert, aber ich würde Haskel jedem empfehlen, der in eine funktionale Sprache eintauchen möchte, ohne von Unreinheit abgelenkt zu werden . * 8 ')
Mark Booth

2
Nachdem Sie das Erlernen der funktionalen Programmierung abgeschlossen haben, sollten Sie auch die statische Programmierung mit einem expressiven Typensystem erlernen. In der kombinierten Kategorie (sowohl funktional als auch typisiert) schlage ich vor: Coq> Haskell> OCaml> Scala> Andere. Es gibt einige weniger beliebte Alternativen, die zwischen Coq und Haskell passen (wie Epigram und Agda). Haskell vermisst das ausdrucksstarke Modulsystem von OCaml.
Lukstafi

Antworten:


28

Es gibt keine Skala für die Beurteilung des Reinheitsgrades von funktionalen Sprachen. Wenn die Sprache Nebenwirkungen zulässt, ist sie unrein, ansonsten ist sie rein. Nach dieser Definition sind Haskell, Mercury, Clean usw. reine Funktionssprachen. wohingegen Scala, Clojure, F #, OCaml usw. unreine sind.

EDIT: Vielleicht hätte ich sagen sollen: "Wenn die Sprache keine Nebenwirkungen zulässt, ohne das Typensystem zu informieren , ist sie rein. Sonst ist sie unrein."


6
Nicht ganz: Haskell lässt Nebenwirkungen zu ( IOMonade). Es ist nur so, dass Code, der Nebenwirkungen verursacht, eindeutig als solcher gekennzeichnet ist. Ich denke nicht, dass es nützlich ist, von reinen / unreinen Sprachen zu sprechen (Haskell lässt Sie zwingend programmieren! Keuchen!), Aber es kann trotzdem nützlich sein, von Codestücken (Funktion, Modul, Programm, was auch immer) als rein zu sprechen /unrein.
Frank Shearar

8
@Frank: Ja, Haskell lässt Nebenwirkungen zu, aber es ist mehr als nur das Markieren von Code, der Nebenwirkungen verursacht. Es behält auch referenzielle Transparenz bei, wenn nebenwirkungsverursachender Code verwendet wird, und das ist es, was es rein macht - zumindest gemäß der Definition von Reinheit durch viele Menschen. Das stimmt natürlich nicht mit der Definition von "Keine Nebenwirkungen erlaubt" von missingfaktor überein.
SEPP2K,

4
@Frank Shearar aber es ist nützlich in einer solchen Weise zu sprechen , weil die IOMonade ist rein. Es ist die Laufzeitbibliothek, die nicht Ihr Code ist, da Ihre mainFunktion im Grunde ein riesiger Zustandstransformator ist. (Etwas wie main :: World -> Worldhinter den Kulissen)
Alternative

1
Meine Sprache hat auch referentielle Transparenz. Sie schreiben nur program = "some C code", und dann kümmert sich die Laufzeitumgebung um den C-Code. :-)
Daniel Lubarov

3
Da das Ausdrucken auf dem Bildschirm ein Nebeneffekt ist, sind wirklich reine Funktionsprogramme ziemlich langweilig.

15

Da Lernen Ihr Ziel ist und Sie keine Programme an sich schreiben , können Sie nichts Reineres als Lambda Calculus erreichen .

Lambda Calculus gab es schon, bevor Computer erfunden wurden. Es bedurfte einiger erfahrener Logiker, um herauszufinden, wie man Subtraktionen durchführt (für eine Weile wurde die Theorie aufgestellt, dass nur Addition und Multiplikation möglich sind).

Wenn Sie lernen, wie Boolesche Werte und Zahlen ifaus scheinbar nichts erfunden werden können, füllen Sie nicht mehr Benzin in Ihren Tank, aber Ihr Tank wird dadurch viel größer.


Zugegeben, es gibt wahrscheinlich nichts Reineres als das, aber wir überschreiten die mathematische Grenze ein wenig zu sehr. Ich möchte immer noch das Programmieren üben und eine neue Sprache lernen, während ich die Funktionsprinzipien aufnehme. Ich stimme jedoch zu, dass es möglicherweise interessant ist, die Lambda-Rechnung zu studieren, um die Grundlagen des Funktionsparadigmas besser zu verstehen (und einen größeren Tank zu haben).
Joanis

2
Einen Proof zu schreiben bedeutet, ein Programm zu schreiben, und ein Programm zu schreiben bedeutet, einen Proof zu schreiben. Wenn Sie die Curry-Howard-Isomorphie kennenlernen, werden Sie feststellen, dass Sie vor zehntausend Zeilen diese mathematische Barriere überschritten haben.
Macneil

1
Es gibt keine einfache Darstellung von -1.
Daniel Lubarov

2
@Mason Wheeler: λ-Kalkül allein hat keine Zahlen oder wirklich irgendeine Art von Daten außer Lambda-Abstraktionen. Sofern nicht anders angegeben, bedeutet jeder, der in der λ-Rechnung von Zahlen spricht, wahrscheinlich die Kodierung von Church für natürliche Zahlen , wobei eine Zahl n durch die Zusammensetzung der n-fachen Funktion dargestellt wird. Das heißt, ich bin zweifelhaft, es war so viel Mühe, die Subtraktion herauszufinden, wenn jemand es tatsächlich versucht hat. Ich habe es an einem Nachmittag alleine ausgearbeitet, nur mit der Definition von Addition und dem Wissen, dass Subtraktion möglich ist.
CA McCann

1
Die Subtraktion mit Church-Zahlen ist einfach, sobald Sie eine Zerlegung nach Fällen erhalten haben. Den ganzen (abgeleiteten) Kalkül aufbauen, um negative Zahlen zu unterstützen, eher mehr Arbeit.
Donal Fellows

6

Unreine Sprachen unterscheiden sich im Prinzip nicht wirklich von den bekannteren imperativen Sprachen, zumal jetzt, da viele funktionale Tricks kopiert wurden. Was ist anders ist der Stil - wie Sie Probleme lösen.

Unabhängig davon, ob Sie Haskell als rein oder die IO-Monade als Verunreinigung betrachten, der Haskell-Stil ist eine extreme Form dieses Stils und es lohnt sich, ihn zu lernen.

Die Haskell IO-Monade leitet sich aus der mathematischen Theorie der (natürlich) Monaden ab. Für imperative Programmierer halte ich es jedoch für sinnvoller, rückwärts zu Monaden zu gelangen.

Phase eins - Eine reine funktionale Sprache kann als Ergebnis leicht einen großen Zeichenfolgenwert zurückgeben. Diese große Zeichenfolge kann der Quellcode eines zwingenden Programms sein, das auf rein funktionale Weise aus einigen anforderungsspezifischen Parametern abgeleitet wird. Sie können dann einen Compiler auf "höherer Ebene" erstellen, der Ihren Codegenerator ausführt und dann den generierten Code automatisch in den Compiler für imperative Sprachen einspeist.

Phase zwei - anstatt Text-Quellcode zu generieren, generieren Sie einen stark typisierten abstrakten Syntaxbaum. Ihr imperativsprachiger Compiler wird in Ihren "übergeordneten" Compiler aufgenommen und akzeptiert den AST direkt als Quellcode. Dies ist viel näher an dem, was Haskell tut.

Dies ist jedoch immer noch umständlich. Zum Beispiel haben Sie zwei verschiedene Arten von Funktionen - die, die während der Code-Generierungsphase ausgewertet werden, und die, die ausgeführt werden, wenn das generierte Programm ausgeführt wird. Es ist ein bisschen wie die Unterscheidung zwischen Funktionen und Vorlagen in C ++.

Machen Sie also für Phase 3 die beiden gleich - dieselbe Funktion mit derselben Syntax kann teilweise während der "Codegenerierung" ausgewertet oder vollständig ausgewertet oder überhaupt nicht ausgewertet werden. Verwerfen Sie außerdem alle Schleifenkonstruktions-AST-Knoten zugunsten der Rekursion. Verwerfen Sie die Idee von AST-Knoten tatsächlich als eine spezielle Art von Daten - haben Sie keine AST-Knoten mit "Literalwert", sondern nur Werte usw.

Dies ist so ziemlich das, was die E / A-Monade tut - der Bind-Operator ist eine Möglichkeit, "Aktionen" zu erstellen, um Programme zu bilden. Es ist nichts Besonderes - nur eine Funktion. Viele Ausdrücke und Funktionen können während der "Codegenerierung" ausgewertet werden, aber bei denen, die von E / A-Nebenwirkungen abhängen, muss die Auswertung bis zur Laufzeit verzögert werden - nicht nach einer speziellen Regel, sondern als natürliche Folge der Datenabhängigkeiten in Ausdrücke.

Monaden im Allgemeinen sind nur Verallgemeinerungen - sie haben die gleiche Schnittstelle, implementieren die abstrakten Operationen jedoch unterschiedlich. Statt eine Beschreibung des imperativen Codes zu erhalten, werden sie stattdessen zu etwas anderem ausgewertet. Dasselbe Interface bedeutet, dass Sie einige Dinge mit Monaden tun können, ohne sich darum zu kümmern, welche Monade sich als nützlich herausstellt.

Diese Beschreibung wird zweifellos puristische Köpfe explodieren lassen, aber für mich erklärt sie einige der wahren Gründe, warum Haskell interessant ist. Es verwischt die Grenze zwischen Programmierung und Metaprogrammierung und verwendet die Werkzeuge der funktionalen Programmierung, um die imperative Programmierung neu zu erfinden, ohne dass eine spezielle Syntax erforderlich ist.

Eine Kritik an C ++ - Vorlagen ist, dass es sich um eine Art kaputte rein funktionale Subsprache in einer imperativen Sprache handelt. Um dieselbe Grundfunktion zur Kompilierungszeit und nicht zur Laufzeit zu evaluieren, muss sie mit einem völlig anderen Stil erneut implementiert werden der Codierung. In Haskell muss die Verunreinigung in ihrem Typ als solche gekennzeichnet sein, aber genau dieselbe Funktion kann sowohl im Sinne einer Metaprogrammierung als auch im Sinne einer Laufzeit-Nicht-Metaprogrammierung in demselben Programm bewertet werden - es gibt keine harte Linie zwischen Programmierung und Metaprogrammierung.

Das heißt, es gibt einige Metaprogrammierungsfunktionen, die Standard Haskell nicht ausführen kann, da Typen (und möglicherweise ein paar andere Funktionen) keine erstklassigen Werte sind. Es gibt jedoch Sprachvarianten, die versuchen, dies zu beheben.

Viele Dinge, die ich über Haskell gesagt habe, können in unreinen funktionalen Sprachen angewendet werden - und manchmal sogar in imperativen Sprachen. Haskell ist anders, weil Sie keine andere Wahl haben, als diesen Ansatz zu wählen - er zwingt Sie im Grunde, diesen Arbeitsstil zu erlernen. Sie können "C in ML schreiben", aber Sie können nicht "C in Haskell schreiben" - zumindest nicht, ohne zu lernen, was unter der Haube vor sich geht.


Vielen Dank! Ich habe mich gefragt, ob die Generizität von C ++ - Vorlagen in Funktionen selbst vereinheitlicht werden könnte. Es sieht so aus, als würde Haskell dies tun. Wichtig ist jedoch, ob dies in einer kompilierten Sprache implementiert werden kann , sodass dieselbe Engine, die während der Kompilierungszeit generische Funktionen aus Vorlagen erstellt, diese auch während der Laufzeit auswerten kann ...
Milind R

5

Ich persönlich kategorisiere Sprachen in drei Ebenen funktionaler Reinheit:

  • Reine Funktionssprachen - dh solche, die Ihr gesamtes Programm als reine Funktion behandeln und die Veränderbarkeit nur durch Interaktion mit der Laufzeit bewältigen - sind wahrscheinlich das kanonische Beispiel für Haskell

  • Unreine funktionale Sprachen - dh solche, die einen funktionalen Stil betonen, aber Nebenwirkungen zulassen. Clojure gehört eindeutig zu dieser Kategorie (es ermöglicht eine kontrollierte Mutation als Teil seines STM-Frameworks), auch OCaml oder F #

  • Multiparadigmasprachen - dies sind in erster Linie keine funktionalen Sprachen, können aber einen funktionalen Stil durch die Verwendung erstklassiger Funktionen unterstützen. Scala ist hier ein gutes Beispiel. Ich würde auch Common Lisp in diese Kategorie aufnehmen, und Sie könnten sogar einschließen Sprachen wie JavaScript .

In Ihrer Situation würde ich vorschlagen, zuerst Haskell und dann Clojure zu lernen. Dies war, was ich getan habe und es hat sehr gut für mich funktioniert! Haskell ist wunderschön und lehrt Sie die reinsten Funktionsprinzipien. Clojure ist viel pragmatischer und hilft Ihnen dabei, eine Menge zu erledigen, während Sie im Herzen dennoch sehr funktional sind.

Ich zähle die dritte Kategorie nicht wirklich als funktionale Sprachen (obwohl ich nach dem Erlernen von Haskell und Clojure oft die funktionalen Techniken nutze, wenn ich sie benutze!)


In sehr engen Grenzen verfügt sogar C über funktionale Fähigkeiten. (Die Haupteinschränkung liegt in der Laufzeitsynthese von Funktionen, was in C wirklich horrend ist, so dass die meisten Leute behaupten, dass dies nicht möglich ist.)
Donal Fellows,

2
@Donal Fellows: Im Grenzbereich von Albernheit bis Unendlichkeit ist jede Turing-vollständige Sprache funktionsfähig: Man muss lediglich einen Lisp-Interpreter in der Sprache implementieren und diesen dann verwenden. :]
CA McCann

Paradigmen sind bedeutungslos, wenn Sie den Standpunkt vertreten, dass alles vollständig ist und daher alles andere emulieren kann. Wenn Sie über Paradigmen nachdenken, müssen Sie sich darauf konzentrieren, was die Sprache idiomatisch macht und was sie entmutigt / verhindert. Um aus meiner Sicht als funktionale Sprache zu gelten, müssen Mutationen und Nebenwirkungen unidiomatisch (für unreine FP) oder verboten (für reine FP) sein. Das heißt, C ist nicht funktionsfähig. Weder sind Multi-Paradigma-Sprachen wie Scala.
Mikera

@mikera: Ich finde Ihre Antwort sehr interessant: Wenn Scala nicht funktionsfähig ist, sondern nur ein Multiparadigma, wie beurteilen Sie die funktionalen Programmierfunktionen in C ++ 11, C # oder sogar Java (die geplante Einführung von Lambdas)?
Giorgio

@ Giorgio: Ich denke, funktionale Funktionen in allen von Ihnen genannten Sprachen sind wahrscheinlich eine gute Idee, sie machen sie einfach nicht zu "funktionalen Sprachen". Um es von der entgegengesetzten Seite zu betrachten: Die Tatsache, dass Sie monadische E / A im imperativen Stil ausführen können, macht Haskell nicht zu einer imperativen Sprache :-). Sprachen mit mehreren Paradigmen sind im Grunde genommen das Ergebnis, wenn Sie Features aus vielen verschiedenen Paradigmen zusammenführen und nicht in erster Linie einen bestimmten Stil festlegen. IMHO C ++, C # und Java bewegen sich alle in Richtung Multiparadigma, sind aber noch nicht ganz da, da das klassenbasierte OOP immer noch dominant ist.
mikera

3

Wenn eine reine Funktionssprache solche ist, die nur reine Funktionen hat (Routinen, die keine Nebenwirkungen haben), dann ist es ein wenig sinnlos, weil sie keine Eingaben lesen oder Ausgaben schreiben kann;)

Da dies wirklich zum Lernen gedacht ist, denke ich, ist Isolation nicht unbedingt der richtige Weg. Funktionale Programmierung ist ein Paradigma. Es ist wichtig zu verstehen, welche Paradigmen für welche Probleme geeignet sind und, was noch wichtiger ist, wie sie am besten kombiniert werden können.

Ich werde es jetzt sagen: Programmiermoden sind dumm und kontraproduktiv. Das Einzige, was zählt, ist, dass Ihr Programm kurz, einfach zu schreiben, einfach zu warten und korrekt funktioniert. Wie Sie dies erreichen, hat nichts mit Programmiermoden zu tun. - Richard Jones

Anders als das, wenn Sie nach "Reinheit" suchen, möchten Sie vielleicht einen Blick auf Pure werfen . Beachten Sie jedoch, dass das Aufrufen von C-Routinen äußerst einfach ist und die Funktionen unsauber (aber auch sehr leistungsfähig) macht.


3

Keine ganz ernste Antwort, aber Unlambda muss ein Konkurrent sein. Mehr "pure Funktionalität" als SKI-Kombinatoren gibt es nicht.


4
Wahnsinn! Ketzerei! Was für eine reine Sprache hat Absurditäten wie Kombinatoren mit Nebenwirkungen? Nein nein Was Sie wirklich hier wollen , ist faul K .
CA McCann

2

Erlang, Haskell, Schema, Scala, Clojure und F #

Diese Frage würde wahrscheinlich am besten Sie , wie Sie bei der Suche helfen , gut .


Vielen Dank, interessante Frage in der Tat. Ich habe mit PLT-Scheme / Racket angefangen, aber ich habe mir SICP noch nicht angesehen ... Real World Haskell sieht auch für mich ziemlich interessant aus.
Joanis

Schema fördert einen funktionalen Stil, aber es hat set!(unter anderem) ...
Daniel Lubarov

Ich schlage OCaml vor, weil es mein Favorit ist. Und es hat ein Modulsystem, das Haskell und F # vermissen.
Lukstafi

@ lukstafi haskell hat sich in den letzten 3 Jahren vielleicht geändert (ich weiß, es ist verrückt geworden), aber es gibt definitiv Module in haskell.
Sara

@kai Mit einem Modulsystem meine ich Module, die von anderen Modulen parametrisiert wurden (eine "Lambda-Rechnung" von Modulen).
Lukstafi
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.