Ist funktionale Programmierung eine Obermenge objektorientierter?


26

Je mehr Funktionen ich programmiere, desto mehr kommt eine zusätzliche Abstraktionsebene hinzu, die so aussieht, als würde die Ebene einer Zwiebel die vorherigen Ebenen umfassen.

Ich weiß nicht, ob dies zutrifft, und kann jemand erklären, wie funktional eine der Prinzipien ist oder nicht: Kapselung, Abstraktion, Vererbung, Polymorphismus

Ich denke, wir können alle sagen, ja, es hat die Kapselung über Tupel, oder zählen Tupel technisch als Faktum der "funktionalen Programmierung" oder sind sie nur ein Nutzen der Sprache?

Ich weiß, dass Haskell die "Schnittstellen" -Anforderung erfüllen kann, bin mir aber auch nicht sicher, ob es sich bei der Methode um eine funktionale Tatsache handelt? Ich vermute, dass die Tatsache, dass Funktoren eine mathematische Basis haben, man sagen könnte, dass diese eine definitive Erwartung von Funktionalität sind, vielleicht?

Bitte erläutern Sie, wie funktional Ihrer Meinung nach die 4 Prinzipien von OOP erfüllt oder nicht erfüllt.

Bearbeiten: Ich verstehe die Unterschiede zwischen dem funktionalen Paradigma und dem objektorientierten Paradigma sehr gut und stelle fest, dass es heutzutage viele multiparadigmische Sprachen gibt, die beides können. Ich bin wirklich nur auf der Suche nach Definitionen, wie direkt fp (denke puristisch, wie haskell) eines der 4 aufgelisteten Dinge tun kann oder warum es keines von ihnen tun kann. dh "Kapselung kann mit Verschlüssen erfolgen" (oder wenn ich mich in dieser Annahme irre, geben Sie bitte an, warum).


7
Diese 4 Prinzipien "machen" OOP nicht. OOP "löst" diese einfach durch Verwendung von Klassen, Klassensuche und deren Instanzen. Aber auch ich hätte gerne eine Antwort, wenn es Möglichkeiten gibt, diese in der funktionalen Programmierung zu erreichen.
Euphorischer

2
@Euphoric Abhängig von der Definition macht es OOP.
Konrad Rudolph

2
@KonradRudolph Ich weiß, dass viele Leute diese Dinge und die Vorteile, die sie mit sich bringen, als einzigartige Eigenschaften von OOP bezeichnen. Unter der Annahme, dass "Polymorphismus" "Subtyp-Polymorphismus" bedeutet, kann ich davon ausgehen, dass die beiden letzteren integraler Bestandteil von OOP sind. Aber ich habe noch keine nützliche Definition von Verkapselung und Abstraktion gefunden, die entschieden Nicht-OOP-Ansätze ausschließt. Sie können Details auch in Haskell ausblenden und den Zugriff darauf einschränken. Und die Haskell hat auch einen Ad-hoc-Polymorphismus, nur keinen Subtyp-Polymorphismus - die Frage ist, ob das "Subtyp" -Bit eine Rolle spielt.

1
@KonradRudolph Das macht es nicht mehr akzeptabel. Wenn überhaupt, ist es ein Anreiz, sich zu verstärken und denjenigen, die es verbreiten, einen Grund zu geben, es zu überdenken.

1
Abstraktion ist für jede Programmierung von Bedeutung, zumindest für jede Programmierung, die über den rohen Maschinencode hinausgeht. Die Kapselung gab es schon lange vor OOP und ist für die funktionale Programmierung von wesentlicher Bedeutung. Eine funktionale Sprache muss keine explizite Syntax für Vererbung oder Polymorphismus enthalten. Ich denke, das summiert sich zu "Nein".
Sdenham

Antworten:


44

Funktionale Programmierung liegt nicht über OOP. Es ist ein völlig anderes Paradigma. Es ist möglich, OOP in einem funktionalen Stil durchzuführen (F # wurde genau für diesen Zweck geschrieben), und am anderen Ende des Spektrums gibt es Dinge wie Haskell, die die Prinzipien der Objektorientierung explizit ablehnen.

Sie können die Kapselung und Abstraktion in jeder Sprache ausführen, die für die Unterstützung von Modulen und Funktionen fortgeschritten ist. OO bietet spezielle Mechanismen für die Verkapselung, die OO jedoch nicht inhärent sind. Der Punkt von OO ist das zweite Paar, das Sie erwähnt haben: Vererbung und Polymorphismus. Das Konzept ist formal als Liskov-Substitution bekannt, und ohne Sprachunterstützung für objektorientierte Programmierung ist es nicht zu bekommen. (Ja, es ist möglich, es in einigen Fällen zu fälschen, aber Sie verlieren viele der Vorteile, die OO auf den Tisch bringt.)

Die funktionale Programmierung konzentriert sich nicht auf die Liskov-Substitution. Es konzentriert sich auf die Erhöhung der Abstraktionsebene und auf die Minimierung der Verwendung von veränderlichen Zuständen und Routinen mit "Nebenwirkungen", was ein Begriff ist, den funktionale Programmierer gerne verwenden, um Routinen zu erstellen, die tatsächlich etwas tun (anstatt einfach etwas zu berechnen) unheimlich. Aber auch hier handelt es sich um völlig separate Paradigmen, die je nach Sprache und Können des Programmierers zusammen verwendet werden können oder nicht.


1
Nun, Vererbung (in den außergewöhnlich seltenen Fällen, in denen sie benötigt wird) ist über Komposition erreichbar und sauberer als die Vererbung auf Typebene. Polymorphismus ist natürlich, insbesondere in Gegenwart von polymorphen Typen. Aber natürlich stimme ich zu, dass FP nichts mit OOP und seinen Prinzipien zu tun hat.
SK-logic

Es ist immer möglich, es zu fälschen - Sie können Objekte in einer beliebigen Sprache implementieren. Ich bin mit allem anderen einverstanden :)
Eliot Ball

5
Ich glaube nicht, dass der Begriff "Nebenwirkungen" von funktionalen Programmierern geprägt wurde (oder hauptsächlich verwendet wird).
3.

4
@ sepp2k: Er hat nicht gesagt, dass sie den Begriff erfunden haben, sondern dass sie ihn in etwa dem Ton verwenden, den man normalerweise verwendet, um sich auf Kinder zu beziehen, die sich weigern, ihren Rasen zu verlassen.
Aaronaught

16
@Aaronaught es sind nicht die Kinder auf meinem Rasen, die mich stören, es sind ihre blutigen Nebenwirkungen! Wenn sie einfach aufhören würden, überall auf meinem Rasen zu mutieren, hätte ich nichts dagegen.
Jimmy Hoffa

10

Ich finde die folgende Intuition nützlich, um OOP und FP zu vergleichen.

Anstatt FP als Obermenge von OOP zu betrachten, stellen Sie sich OOP und FP als zwei alternative Betrachtungsweisen für ein ähnliches zugrunde liegendes Berechnungsmodell vor, in dem Sie Folgendes haben:

  1. Eine Operation, die ausgeführt wird,
  2. einige Eingabeargumente für die Operation,
  3. einige feste Daten / Parameter, die die Definition der Operation beeinflussen können,
  4. einen Ergebniswert und
  5. möglicherweise eine Nebenwirkung.

In OOP wird dies von erfasst

  1. Eine Methode, die ausgeführt wird,
  2. die Eingabeargumente einer Methode,
  3. das Objekt, für das die Methode aufgerufen wird, das einige lokale Daten in Form von Elementvariablen enthält,
  4. der Rückgabewert der Methode (möglicherweise ungültig),
  5. die Nebenwirkungen der Methode.

In FP wird dies von erfasst

  1. Ein Abschluss, der ausgeführt wird,
  2. die Eingangsargumente des Abschlusses,
  3. die erfassten Variablen des Verschlusses,
  4. der Rückgabewert des Abschlusses,
  5. die möglichen Nebenwirkungen des Verschlusses (in reinen Sprachen wie Haskell geschieht dies auf sehr kontrollierte Weise).

Mit dieser Interpretation kann ein Objekt als eine Sammlung von Abschlüssen (seine Methoden) angesehen werden, die alle dieselben nicht lokalen Variablen erfassen (die Mitgliedsvariablen des Objekts, die allen Abschlüssen in der Sammlung gemeinsam sind). Diese Ansicht wird auch dadurch gestützt, dass in objektorientierten Sprachen Verschlüsse häufig als Objekte mit genau einer Methode modelliert werden.

Ich denke, die unterschiedlichen Sichtweisen ergeben sich aus der Tatsache, dass die objektorientierte Sicht auf die Objekte (die Daten) zentriert ist, während die funktionale Sicht auf die Funktionen / Abschlüsse (die Operationen) zentriert ist.


8

Es hängt davon ab, wen Sie nach einer Definition von OOP fragen. Wenn Sie fünf Personen fragen, erhalten Sie wahrscheinlich sechs Definitionen. Wikipedia sagt :

Versuche, eine Konsensdefinition oder -theorie hinter Objekten zu finden, haben sich als wenig erfolgreich erwiesen

Wenn also jemand eine sehr eindeutige Antwort gibt, nehmen Sie sie mit einem Körnchen Salz.

Wie gesagt, es ist ein gutes Argument gemacht werden, dass, ja, FP ist ein Superset der OOP als Paradigma. Insbesondere Alan Kays Definition des Begriffs objektorientierte Programmierung widerspricht dieser Auffassung nicht ( Kristen Nygaards jedoch ). Alles, was Kay wirklich interessierte, war, dass alles ein Objekt ist und dass die Logik durch das Weitergeben von Nachrichten zwischen Objekten implementiert wird.

Vielleicht interessanter für Ihre Frage: Klassen und Objekte können als Funktionen und Abschlüsse betrachtet werden, die von Funktionen (die gleichzeitig als Klassen und Konstruktoren fungieren) zurückgegeben werden. Dies kommt der prototypbasierten Programmierung sehr nahe, und tatsächlich ermöglicht JavaScript genau dies.

var cls = function (x) {
    this.y = x;
    this.fun = function () { alert(this.y); };
    return this;
};

var inst = new cls(42);
inst.fun();

(Natürlich erlaubt JavaScript das Mutieren von Werten, was in der rein funktionalen Programmierung illegal ist, aber in einer strengen Definition von OOP auch nicht erforderlich ist.)

Die wichtigere Frage ist jedoch: Ist dies eine sinnvolle Klassifizierung von OOP? Ist es hilfreich, es als Teilmenge der funktionalen Programmierung zu betrachten? Ich denke, dass dies in den meisten Fällen nicht der Fall ist.


1
Ich denke, es könnte sinnvoll sein, zu überlegen, wo die Grenze gezogen werden soll, wenn ich die Paradigmen wechsle. Wenn gesagt wird, dass es in fp keine Möglichkeit gibt, einen subtypischen Polymorphismus zu erzielen, werde ich nie versuchen, mit fp etwas zu modellieren, das gut dazu passt. Wenn es jedoch möglich ist, kann ich mir die Zeit nehmen, um einen guten Weg zu finden (obwohl ein guter Weg möglicherweise nicht möglich ist), wenn ich stark in einem fp-Raum arbeite, aber subtypischen Polymorphismus in einigen Nischenräumen haben möchte. Wenn der Großteil des Systems jedoch dazu passt, ist es natürlich besser, OOP zu verwenden.
Jimmy Hoffa

6

FP wie OO ist kein genau definierter Begriff. Es gibt Schulen mit unterschiedlichen, manchmal widersprüchlichen Definitionen. Wenn du nimmst, was sie gemeinsam haben, kommst du zu:

  • Funktionale Programmierung ist die Programmierung mit erstklassigen Funktionen

  • OO-Programmierung ist eine Programmierung mit Inklusionspolymorphismus in Kombination mit mindestens einer eingeschränkten Form von dynamisch aufgelöster Überladung. (Eine Randnotiz: In OO-Kreisen bedeutet Polymorphismus normalerweise Inklusionspolymorphismus , während FP-Schulen normalerweise parametrischen Polymorphismus bedeuten .)

Alles andere ist entweder an anderer Stelle vorhanden oder fehlt in einigen Fällen.

FP und OO sind zwei Werkzeuge zum Erstellen von Abstraktionen. Sie haben jeweils ihre eigenen Stärken und Schwächen (zum Beispiel haben sie eine andere bevorzugte Ausdehnungsrichtung im Ausdrucksproblem), aber keine ist an sich mächtiger als die andere. Sie können ein OO-System über einen FP-Kernel erstellen (CLOS ist ein solches System). Sie können ein OO-Framework verwenden, um erstklassige Funktionen abzurufen (siehe beispielsweise die Definition von Lambda-Funktionen in C ++ 11).


Ich denke, Sie meinen "erstklassige Funktionen" und nicht "Funktionen erster Ordnung".
Dan_waterworth

Errr ... C ++ 11-Lambda sind kaum erstklassige Funktionen: Jedes Lambda hat einen eigenen Ad-hoc-Typ (für alle praktischen Zwecke eine anonyme Struktur), der mit einem nativen Funktionszeigertyp nicht kompatibel ist. Und std::function, denen sowohl Funktionszeiger als auch Lambdas zugeordnet werden können, ist eindeutig generisch und nicht objektorientiert. Dies ist nicht verwunderlich, da die begrenzte Marke des Polymorphismus (Subtyp-Polymorphismus) der Objektorientierung weniger stark ist als der parametrische Polymorphismus (sogar Hindley-Milner, geschweige denn das vollständige System F-Omega).
Pyon

Ich habe nicht viel Erfahrung mit puristischen funktionalen Sprachen, aber wenn Sie Klassen mit einer statischen Methode in Closures definieren und sie an verschiedene Kontexte weitergeben können, würde ich sagen, dass Sie (vielleicht unbeholfen) mindestens auf halbem Weg sind dort auf funktionalen Stil Optionen. In den meisten Sprachen gibt es viele Möglichkeiten, strenge Parameter zu umgehen.
Erik Reppen

2

Nein; OOP kann als eine Obermenge der prozeduralen Programmierung angesehen werden und unterscheidet sich grundlegend vom funktionalen Paradigma, weil der Zustand in den Instanzfeldern dargestellt ist. Im Funktionsparadigma sind die Variablen Funktionen, die auf die konstanten Daten angewendet werden, um das gewünschte Ergebnis zu erhalten.

Tatsächlich können Sie die funktionale Programmierung als Teilmenge von OOP betrachten. Wenn Sie alle Ihre Klassen unveränderlich machen, können Sie davon ausgehen, dass Sie eine Art funktionale Programmierung haben.


3
Unveränderliche Klassen bilden keine Funktionen höherer Ordnung, Listenverständnisse oder Abschlüsse. Fp ist keine Untermenge.
Jimmy Hoffa

1
@Jimmy Hoffa: Sie können eine höhere oreder-Funktion leicht simulieren, indem Sie eine Klasse erstellen, die eine einzelne Methode hat, die Objekte eines ähnlichen Typs annimmt oder mehr, und auch ein Objekt dieses ähnlichen Typs zurückgibt (Typ, der eine Methode und keine Felder hat). . Listen-Comperhension hat nichts mit der Programmiersprache zu tun und ist kein Paradigma (Smalltalk unterstützt dies und ist OOP). Closures sind in C # vorhanden und werden auch in Java eingefügt.
m3th0dman

Ja, C # hat Closures, aber das liegt daran, dass es ein Multiparadigma ist. Closures wurden zusammen mit anderen fp-Stücken zu C # hinzugefügt (wofür ich auf ewig dankbar bin), aber ihre Anwesenheit in einer oop-Sprache macht sie nicht oop. Ein guter Punkt zur Funktion höherer Ordnung: Die Kapselung einer Methode in eine Klasse ermöglicht dasselbe Verhalten.
Jimmy Hoffa

2
Ja, aber wenn Sie Closures verwenden, um den Status zu ändern, würden Sie trotzdem in ein Funktionsparadigma programmieren? Der Punkt ist - das Funktionsparadigma handelt von Zustandsmangel, nicht von Funktionen höherer Ordnung, Rekursion oder Abschlüssen.
m3th0dman

interessanter gedanke zur definition von fp .. ich werde mehr darüber nachdenken müssen, danke für das teilen deiner beobachtungen.
Jimmy Hoffa

2

Antworten:

Wikipedia hat einen großartigen Artikel über funktionale Programmierung mit einigen der Beispiele, nach denen Sie fragen. @Konrad Rudolph hat bereits den Link zum OOP-Artikel bereitgestellt .

Ich denke nicht, dass ein Paradigma eine Supermenge des anderen ist. Es handelt sich um unterschiedliche Programmierperspektiven, und einige Probleme lassen sich besser aus einer Perspektive und andere aus einer anderen Perspektive lösen.

Ihre Frage wird durch alle Implementierungen von FP und OOP noch komplizierter . Jede Sprache hat ihre eigenen Macken, die für eine gute Antwort auf Ihre Frage relevant sind.

Zunehmend tangentiales Wandern:

Ich mag die Idee, dass eine Sprache wie Scala versucht, Ihnen das Beste aus beiden Welten zu bieten. Ich mache mir Sorgen, dass es Ihnen die Komplikationen beider Welten gibt.

Java ist eine OO-Sprache, aber in Version 7 wurde eine "Try-with-Resources" -Funktion hinzugefügt, mit der eine Art Abschluss imitiert werden kann. Hier imitiert es das Aktualisieren einer lokalen Variablen "a" in der Mitte einer anderen Funktion, ohne sie für diese Funktion sichtbar zu machen. In diesem Fall ist die erste Hälfte der anderen Funktion der Konstruktor ClosureTry () und die zweite Hälfte die Methode close ().

public class ClosureTry implements AutoCloseable {

    public static void main(String[] args) {
        int a = 1;
        try(ClosureTry ct = new ClosureTry()) {
            System.out.println("Middle Stuff...");
            a = 2;
        }
        System.out.println("a: " + a);
    }

    public ClosureTry() {
        System.out.println("Start Stuff Goes Here...");
    }

    /** Interface throws exception, but we don't have to. */
    public void close() {
        System.out.println("End Stuff Goes Here...");
    }
}

Ausgabe:

Start Stuff Goes Here...
Middle Stuff...
End Stuff Goes Here...
a: 2

Dies kann nützlich sein, um einen Stream zu öffnen, in den Stream zu schreiben und ihn zuverlässig zu schließen, oder um einfach zwei Funktionen so zu koppeln, dass Sie nicht vergessen, die zweite Funktion aufzurufen, nachdem Sie einige Arbeiten zwischen ihnen ausgeführt haben . Natürlich ist es so neu und ungewöhnlich, dass ein anderer Programmierer den try-Block entfernen könnte, ohne zu bemerken, dass er etwas kaputt macht. Daher ist es derzeit eine Art Anti-Pattern, aber interessant, dass dies möglich ist.

Sie können jede Schleife in den meisten imperativen Sprachen als Rekursion ausdrücken. Objekte und Variablen können unveränderlich gemacht werden. Verfahren können geschrieben werden, um Nebenwirkungen zu minimieren (obwohl ich behaupten würde, dass eine echte Funktion auf einem Computer nicht möglich ist - die Zeit, die für die Ausführung benötigt wird, und die verbrauchten Prozessor-, Festplatten- und Systemressourcen sind unvermeidbare Nebenwirkungen). Einige funktionale Sprachen können dazu gebracht werden, viele, wenn nicht sogar alle objektorientierten Operationen auszuführen. Sie müssen sich nicht gegenseitig ausschließen, obwohl einige Sprachen Einschränkungen aufweisen (z. B. dass keine Aktualisierung von Variablen zulässig ist), die bestimmte Muster verhindern (z. B. veränderbare Felder).

Für mich sind die nützlichsten Teile der objektorientierten Programmierung das Ausblenden (Einkapseln) von Daten, das Behandeln von Objekten mit ähnlichen Eigenschaften (Polymorphismus) und das Sammeln Ihrer Daten und Methoden, die diese Daten gemeinsam verarbeiten (Objekte / Klassen). Vererbung mag das Flaggschiff von OOP sein, aber für mich ist sie der am wenigsten wichtige und am wenigsten genutzte Teil.

Die nützlichsten Teile der funktionalen Programmierung sind Unveränderlichkeit (Token / Werte anstelle von Variablen), Funktionen (keine Nebenwirkungen) und Abschlüsse.

Ich denke nicht, dass es objektorientiert ist, aber ich muss sagen, dass eines der nützlichsten Dinge in der Informatik die Fähigkeit ist, eine Schnittstelle zu deklarieren, und dass dann verschiedene Teile der Funktionalität und Daten diese Schnittstelle implementieren. Ich mag es auch, ein paar veränderbare Daten zu haben, mit denen ich arbeiten kann. Ich glaube, ich fühle mich in ausschließlich funktionalen Sprachen nicht so wohl, obwohl ich versuche, die Veränderbarkeit und die Nebenwirkungen in all meinen Programmdesigns zu begrenzen.

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.