Ist funktionale Programmierung nicht nur eine verschleierte imperative Programmierung?

8

Ein YouTube - Video war ich gerade erklärte die Unterschiede zwischen Imperativen und Funktionale Programmierung durch den Nachweis , wie die Zahlen aus 1zu 10jeweils in Java und in Haskell aufsummiert.

In Java müssen Sie jeden Schritt explizit angeben und das Ergebnis jedes Schritts einer Variablen zuweisen - etwa wie folgt

int total = 0;
     for (int i = 1; i <= 10; i++){
         total = total + i;
     }
return total;

In Haskell kann man einfach sagen:

sum(1..10)

Meine Frage ist: Im Hintergrund einer funktionalen Sprache ist offensichtlich etwas los, und dass etwas eine Art imperativer Prozess sein muss. Es scheint, dass funktionale Sprachen wirklich nur eine Art von APIs für imperative Sprachen sind. Zum Beispiel kann ich einen Teil einer funktionalen Sprache erstellen, indem ich eine Methode sum(int start, int end)in Java definiere. Habe ich genau dort wirklich eine neue Art von Sprache erstellt oder habe ich nur eine Reihe von imperativen Methodenaufrufen definiert, die imperative Anweisungen vor Ihnen verbergen?

Ich hoffe, es ist klar, was ich nur schwer verstehen kann.

CodyBugstein
quelle
1
Aus genau dem Grund, den Sie angeben, ist das Beispiel der Summenfunktion nicht gut.
David Richerby
Deshalb wird es als Stil bezeichnet , im Gegensatz zu einem neuen Computertyp.
user253751

Antworten:

10

Wenn wir den syntaktischen Zucker auf der Vorderseite und die Codegenerierung auf der Rückseite abziehen und vergleichen, was dazwischen passiert, wenn der Quellcode in einen laufenden Code für zwingende Sprachen wie C oder Java mit funktionalen Sprachen wie ML oder OCaml konvertiert wird, werden wir im Allgemeinen feststellen die folgenden Unterschiede in was, warum und wie.

Veränderlich gegen unveränderlich

Bei der funktionalen Programmierung werden in der Regel unveränderliche Werte verwendet, sodass wir uns keine Sorgen machen müssen, wenn sich ein Wert außerhalb unserer aktuellen Funktion ändert. Bei richtiger Anwendung werden alle Probleme im Zusammenhang mit Nebenwirkungen beseitigt .

Fokus: Daten versus Funktion.

Wenn man an eine zwingende Codierung denkt, denkt man zuerst an Datenstrukturen und dann an die Methoden, die sie benötigen. Wenn man mit funktionaler Programmierung arbeitet, denkt man zuerst an die benötigten Funktionen und stellt dann die benötigten Datentypen her . Die meisten Datentypen sind entweder Lazy List (Think Stream oder Infinite List) oder diskriminierte Gewerkschaften . Wenn die diskriminierte Vereinigung rekursiv ist, haben Sie sofort einen Baum oder eine Grafik erstellt, ohne den gesamten Laufcode schreiben zu müssen.

Generika / Parametrischer Polymorphismus

Dieser ist interessant, und wenn ich meine Fakten richtig habe, wurde er mit funktionaler Programmierung erfunden und dann in die imperative Programmierung übertragen. Wenn Sie Generika mögen, bedanken Sie sich bei den Designern der funktionalen Sprache.

Referenzielle Transparenz / Parallelisierung

Aufgrund der referenziellen Transparenz kann Funktionscode einfacher auf paralleles Rechnen portiert werden .

Funktion / Komposition höherer Ordnung

Da Funktionen neue Funktionen erstellen und Funktionen zurückgeben können, ist das Erstellen neuer Funktionen auf anderen Funktionen genauso einfach wie das Erstellen neuer Ausdrücke, anstatt ganz neue Methoden zu schreiben. Dies führt zu Morphismen, die sehr nützlich sind, wenn das Problem, das Sie lösen, mit Mathematik erklärt werden kann. Mit der funktionalen Programmierung ist es viel einfacher, Set-Transformationen durchzuführen, an SQL und Updates zu denken. Wie Wandering Logic feststellte, zeichnen sich hier funktionale Programmiersprachen aus.

Typisierung: Statisch versus Inferenz .

Da die Typen abgeleitet werden, anstatt vom Programmierer während des Schreibens festgelegt zu werden, können mehr Überprüfungen durchgeführt werden, um die Richtigkeit des Codes sicherzustellen, und häufig werden die Funktionen generisch gemacht, anstatt von einem festgelegten Typ zu sein.

Pattern Matching vs switch-Anweisung

Wenn Sie Matching mit diskriminierten Gewerkschaften kombinieren, wird Ihr Matching überprüft, um sicherzustellen, dass Sie jedes Ergebnis abgedeckt haben. Wie oft hatten Sie einen Laufzeitfehler, weil Sie einen Fall mit einer switch-Anweisung verpasst haben.

Guy Coder
quelle
„Wenn man mit funktionaler Programmierung arbeitet, denkt man zuerst darüber nach, welche Funktionen benötigt werden, und stellt dann die benötigten Datentypen her“: hängt davon ab, was modelliert wird. Insbesondere rekursive Datentypen sind in FP ebenso ein Hot Spot wie Funktionen.
Hibou57
„Wenn Sie Matching mit diskriminierten Gewerkschaften kombinieren, wird Ihr Matching überprüft, um sicherzustellen, dass Sie jedes Ergebnis abgedeckt haben.“ Während (und als Randnotiz) werden auch Fallaussagen mit Ada überprüft, was unbedingt erforderlich ist. Imperativ zu sein, schließt Musterabgleich oder Abdeckungsprüfung nicht aus.
Hibou57
@ Hibou57 Nette Kommentare. Ihre Antwort würde mich interessieren.
Guy Coder
Ich bin damit einverstanden, dass man zuerst in Daten denken kann, bevor Funktionen wie von Hibou57 angegeben. Lassen Sie mich das etwas näher erläutern. Ich neige dazu, funktionale Sprachen für das zu verwenden, was sie können, und zwingende Sprachen für das, was sie können, und Logiksprachen für das, was sie können, usw. zu verwenden, und habe kein Problem damit, zwei oder mehr Sprachen zu verwenden für ein Problem die Aufteilung der Aufgabe auf die Sprachen. Wenn ich also eine funktionale Sprache verwende, denke ich zuerst an Funktionen und wenn ich persistente Daten benötige, denke ich zuerst an Daten, und Logik verwende ich eine logische Sprache usw.
Guy Coder
1
Für die Ada-Fallbeschreibung hier die Referenz: 5.4 Fallerklärungen (für die letzte Überarbeitung bis zu diesem Datum). Beachten Sie insbesondere den Wortlaut über „Deckung“ / „Deckung“ und dergleichen.
Hibou57
9

Eine funktionale Programmierung Sprache ist bemerkenswert für das, was es verbietet . Es verbietet das Ändern einer vorhandenen Variablen oder Datenstruktur. Sie können in einigen wichtigen Programmiersprachen in einem "funktionalen Stil" programmieren, aber die Sprache schützt Sie nicht davor, versehentlich eine vorhandene Variable oder Datenstruktur zu ändern. Hier ist zum Beispiel eine rekursive, funktionale Version von sumin Java:

int sum(int start, int end) {
  if (start > end) return 0;
  else {
    int total = start + sum(start+1, end);
    return total;
  }
}

Funktionale Programmiersprachen zeichnen sich dadurch aus, dass sie alle "erstklassige" Funktionen haben. Das heißt: Eine Funktion kann wie eine Ganzzahl oder eine Datenstruktur als Wert verwendet werden. Einige imperative Sprachen haben auch erstklassige Funktionen (insbesondere C und C ++), aber in Java müssen Sie es mit einem Objekt mit einer einzigen Methode (normalerweise aufgerufen apply()oder so) fälschen. Mit erstklassigen Funktionen können wir die sumFunktion auf a verallgemeinern Funktion, die jeden Bediener reduziert:

interface IntegerOperator {
  int apply(int a, int b);
}

int sum(IntegerOperator op, int start, int end) {
  if (start > end) return 0;
  else {
    int total = op.apply(start, sum(op, start+1, end))
    return total;
  }
}

class AdditionOperator implements IntegerOperator {
  int apply(int a, int b) { return a+b; }
}

int total = sum(new AdditionOperator(), 1, 10);

Wenn ich in einer funktionalen Programmiersprache schreiben würde, würde ich wissen, dass jede Funktion wirklich eine Funktion ist (sie gibt jedes Mal den gleichen Wert zurück, wenn sie mit denselben Parametern aufgerufen wird), also würde ich das wissen in:

int total1 = sum(new ReallyStrangeOperator(), 1, 10);
int total2 = sum(new ReallyStrangeOperator(), 1, 10);

total1und total2haben den gleichen Wert (so könnte ich zum Beispiel den zweiten Aufruf von optimieren sum), während ich in einer imperativen Programmiersprache keine Ahnung habe, ob ReallyStrangeOperator(1,2)jedes Mal der gleiche Wert zurückgegeben wird oder nicht.

Wanderlogik
quelle
2
Danke für die wirklich informative Antwort. Ich habe den anderen akzeptiert, aber ich hätte Ihren genauso gut akzeptieren und diesen Kommentar in den anderen schreiben können.
CodyBugstein