Funktionale Swift

Was vereint "Currying", "Monaden", "algebraische Datentypen"? Nicht nur die Tatsache, dass einige Entwickler versuchen, diese Wörter zu umgehen, sondern auch die funktionale Programmierung. Unter der sorgfältigen Anleitung von Jewgeni Elchev stürzten wir uns in ein funktionales Paradigma und verstanden fast alles. Haben Sie keine Angst vor der Zeit, lesen Sie das Transkript der zehnten Ausgabe des AppsCast- Podcasts.



Daniil Popov: Hallo allerseits. Heute ist unser Gast Evgeny Elchev aus dem sonnigen Krasnojarsk. Eugene, sag mir, was du machst und wie du zur funktionalen Programmierung gekommen bist?

Evgeny Elchev: Hallo allerseits. Ich bin ein iOS-Entwickler bei Redmadrobot, wie alle anderen auch, ich male Schaltflächen, manchmal schreibe ich Geschäftslogik.

Ich habe die funktionale Programmierung zuerst durch Artikel kennengelernt. Ich verstand den Punkt nicht ganz und dachte, es sei eine Art prozedurale Programmierung ohne Unterricht. Als ich einen der Artikel genauer las, stellte ich fest, dass ich falsch lag und fing an zu graben. Das soll nicht heißen, dass ich gerade zur funktionalen Programmierung gekommen bin, da echte Follower ihre Knochen dafür legen und in Haskell schreiben werden, wo immer möglich Monaden verwenden. Ich bin nur gestürzt und benutze es nur in der Produktion.

Daniil Popov: Also, die Monaden sind schon weg.

Evgeny Elchev: Schon schwierig?

Daniil Popov: Ich habe versucht, den gleichen Weg zu gehen, aber ich habe den Artikel geöffnet, die Wörter "Curry", "Monade" gesehen und ihn sofort geschlossen, weil ich dachte, ich wäre noch nicht würdig. Habe ich eine Chance

Evgeny Elchev: Natürlich. Sie können dies überhaupt nicht wissen.

In einfachen Worten über Funktionalismus


Daniil Popov: Geben wir eine einfache Definition für diejenigen, die noch nie von einem funktionalen Paradigma gehört haben.

Evgeny Elchev: Jeder versteht Paradigmen auf seine Weise. Wenn wir die Erklärung aus Wikipedia übernehmen, ist dies die Verwendung mathematischer Funktionen, bei denen das gesamte Programm als mathematische Funktion interpretiert wird.

Der funktionale Ansatz (FP) ist, wenn Sie in Ihrer Arbeit Funktionen verwenden, die nur Eingabeargumente und einen Ausgabewert enthalten. Wenn das gesamte Programm aus solchen Funktionen besteht, handelt es sich um ein Funktionsprogramm.

Daniil Popov: OOP war eine logische Fortsetzung der üblichen prozeduralen Programmierung und löste das Problem der Kapselung von Daten in Klassen. Welche Probleme sollte die funktionale Programmierung lösen?

Evgeny Elchev: Mathematiker haben die funktionale Programmierung erfunden. Die Jungs versammelten sich und beschlossen, ein Paradigma zu schaffen, in dem alles bewiesen werden kann. Es gibt Code, der noch nicht gestartet wurde, aber wir werden alles beweisen. Jeder Punkt des Programms kann berechnet werden, um zu verstehen, wohin wir kommen werden, wenn wir etwas zulassen.

Es klingt abstrakt, schauen wir uns also ein Beispiel für eine reine Funktion an. Wir schreiben eine Summenfunktion, die zwei Argumente akzeptiert, 2 und 3 übergeben, 5 erhalten und wir können es beweisen. Es ist immer wahr. Wenn unser gesamtes Programm aus solchen Funktionen besteht, ist alles beweisbar.

Beim Erstellen von Sprachen wurden grundlegende Funktionen übersehen, und zusätzliche Funktionen wurden angezeigt: Lambdas, Funktionen höherer Ordnung, Monaden, Monoide.

Das Funktionsparadigma löst kein einziges Problem, es ist der gleiche Wunsch, guten Code so einfach wie möglich zu schreiben, damit die Programme stabil und leicht zu warten sind.

Wenn Sie genau hinschauen, spiegeln sich viele der Dinge, die wir in OOP verwenden, in einem funktionalen Ansatz wider. Es gibt Klassen im OPP, die eine Reihe von Feldern kapseln. In FP kann dies auch mithilfe von Typklassen erfolgen. Wie Vitaly Bragilevsky gerne sagt : "Wenn Sie sich das Tablet ansehen, auf dem die Daten entlang der Linien und der Funktionsspalten verlaufen, dann geht der FI entlang der Spalten, OOP entlang der Linien." Das ist alles.

Daniil Popov: In welcher Beziehung steht FI zu anderen Paradigmen? Kann ich funktional in OOP schreiben? Wie mischt man Paradigmen und macht es Sinn?

Evgeny Elchev: Das Paradigma beschränkt sich auf die Tatsache, dass Sie Funktionen mit Daten schreiben. Eines der Merkmale von AF ist das Fehlen variabler Zustände. Wenn Ihre Daten eine Klasse sind, gibt es kein Problem. Wenn die Klasse vollständig unveränderlich ist, kann sie verwendet werden. Eine Klasse ist einfach ein Typ, wie eine Zeichenfolge oder eine Zahl, nur komplexer und besteht aus mehreren Werten.

Daniil Popov: Sie haben vorhin gesagt, dass Sie die mathematische Korrektheit eines Programms beweisen können, wenn Sie ausschließlich funktional schreiben. Dann hört der Witz „kompiliert - Werke“ für funktionale Sprachen auf, ein Witz zu sein, oder?

Evgeny Elchev: Wenn Sie sich E / A-Fehler ansehen, dann ja. Zuvor hatten Programmierer mit dem Problem zu kämpfen: Mit dem Netzwerk verbunden, kein Netzwerk, keine Rückgabe, und alles fiel. Für die Lösung war es am einfachsten zu überprüfen, was kam - null / nicht null, aber da das Risiko bestand, dass nicht alles berücksichtigt wurde, konnte das Programm kompiliert werden und abstürzen.

In modernen Sprachen wird dies entschieden. In Haskell können Sie ein Programm schreiben, das funktioniert und nicht abstürzt, aber niemand wird sagen, wie richtig es funktioniert. Natürlich gibt es strenge Typen, und Sie können keinen Fehler machen, indem Sie einer Zeichenfolge eine Zahl hinzufügen, aber Sie können jederzeit Fehler in der Anwendung hinterlassen, und es wird funktionieren.

Platz für funktionale Annäherung in Swift


Alexei Kudryavtsev: Wie viel kann Swift als funktionale Sprache bezeichnet werden?

Evgeny Elchev: Es ist möglich. Die Funktionalität ist als zustandslos positioniert, aber Sie können in Swift schreiben, um solche Zustände zu vermeiden. Gleichzeitig ist Swift nicht dasselbe wie das Schreiben unter iOS, wo es überall Zustände gibt. Natürlich gibt es in Swift keine speziellen Anweisungen wie in Haskell, wo alle Funktionen standardmäßig bereinigt sind und der Compiler Ihnen nicht erlaubt, auf den Status zuzugreifen und ihn zu ändern. Wenn Sie die Funktion als "schmutzig" markieren, werden die Änderungen verfügbar.

Alexei Kudryavtsev: Im zweiten oder dritten Swift gab es einen reinen Modifikator, der jedoch nur auf Kompilierungsebene wirkte, damit sich die globalen Werte nicht ändern. Sie haben etwas in sie geschrieben, aber der Compiler hat alles herausgeschnitten.

Evgeny Elchev: Ja, unter iOS wird der Compiler dem nicht folgen. Alles liegt ganz in unserem Gewissen: Während Sie schreiben, wird es so sein.

Alexei Kudryavtsev: Sie sagen, dass es in iOS-Anwendungen viele Zustände gibt, aber wo und was tun Sie damit, wenn Sie in einem funktionalen Stil schreiben?

Evgeny Elchev: Der wichtigste Status ist die Benutzeroberfläche, zum Beispiel Eingabefelder. Mit ihnen kann praktisch nichts gemacht werden. Sie können versuchen, von ihnen zu abstrahieren, an einem Ort zu sammeln und so viel Code wie möglich zu schreiben, ohne sie zu berücksichtigen. Beispielsweise schreiben Sie eine Dirty-Funktion, die alle Daten von der Benutzeroberfläche abruft.

In meinem Artikel habe ich ein Beispiel für ein Autorisierungsformular angegeben, bei dem es wichtig ist, dass der Benutzer einen Benutzernamen / ein Passwort eingibt. Wir schreiben eine Dirty-Funktion, die eine Struktur mit Autorisierungsdaten zurückgibt, und schreiben dann sauberen Code darauf. Wir haben diese Daten validiert. Wenn das Ergebnis gültig ist, senden Sie eine Anfrage an den Server. Eine Serveranforderung ist ebenfalls eine fehlerhafte Funktion, und die vollständige Verarbeitung kann sauber sein. "Empfangen, analysiert" ist eine lineare Funktion: Die Eingabe in Daten, die Ausgabe ist unsere Struktur. Dann wurden sie transformiert, gefiltert und können wieder auf dem Bildschirm angezeigt werden.

Alexei Kudryavtsev : In Haskell hilft der Compiler sehr. Wenn der Status von irgendwoher kommt, wird die gesamte Anrufkette als schmutzig betrachtet und Sie müssen alles in Monaden einwickeln. Wenn die Funktion rein ist, funktioniert das Zwischenspeichern der Ergebnisse - dieselbe Ausgabe ist immer dieselbe Ausgabe. In Swift müssen Sie die Karten selbst implementieren und versuchen, das Ergebnis zurückzugeben, wenn es bereits zwischengespeichert ist.

Daniil Popov: Die meisten modernen Sprachen gelten als multiparadigmatisch und viele haben funktionale Merkmale. In Java gibt es beispielsweise eine spezielle Anmerkung für die Schnittstelle - @FunctionalInterface , die den Entwickler dazu verpflichtet, nur eine Methode in der Schnittstelle zu definieren, sodass diese Schnittstelle in Form von Lambdas im gesamten Code verwendet wird. Wenn Sie eine zweite Methode hinzufügen oder eine vorhandene löschen, schwört der Compiler, dass er keine funktionale Schnittstelle mehr ist. Verfügt Swift neben der iOS-Plattform über solche Funktionsmerkmale?

Evgeny Elchev: Es fällt mir schwer zu verstehen, was eine solche Anmerkung in Java bewirkt. Wenn Sie damit meinen, dass Sie diese Schnittstelle für die Klasse implementieren und dann nur eine Methode implementieren, gibt es in Swift keine derartigen Einschränkungen. Sie können Typealias erstellen, benennen und als Funktionstyp als Argumenttyp, als Variablentyp, verwenden, um einen Abschluss zuzuweisen. Sie können Einschränkungen definieren - Argumente für die Eingabe und Ausgabe. Funktionen höherer Ordnung selbst, die Verschlüsse annehmen können, sind Polymorphismen, und in Swift können Sie Polymorphismen auf Typen erstellen, die nicht auf Objekte beschränkt sind.

Aber ich kenne keine spezifischen funktionalen Dinge. Früher gab es im ersten Swift Curry, aber es wurde herausgeschnitten. Jetzt können wir eine Funktion schreiben, um uns selbst zu curryen, oder eine Funktion schreiben, damit sie Schließungen ineinander zurückgibt, aber das ist nicht ganz richtig.

Wir haben keine Box-Funktoren oder Monaden. Sie können nicht einmal geschrieben werden. Neue Funktionen in Swift 5.1 sollten dabei helfen, aber ich habe versucht, solchen Code zu schreiben, und xCode ist gefallen.

Wenn Sie möchten, ist es in Swift im Prinzip einfach, alles selbst zu erledigen. Es gibt bereits eine optionale Monade (in Haskell - vielleicht). Sie hat eine Karte und eine Flatmap zum Erstellen linearer Berechnungen.

Swift verfügt über eine leistungsstarke Mustererkennung. Switch, der in fast jeder Sprache vorhanden ist und in den meisten Fällen eine Ganzzahl mit einer Einheit verknüpft, kann eine Variable einem bestimmten Muster, Bereichen, Typen zuordnen und Werte aus verwandten Typen extrahieren. Es gibt Karthago - Sie komponieren einen neuen Typ und geben mehrere andere hinein. Basierend auf ihnen können Sie auch Musterabgleich durchführen. Es gibt eine Aufzählung, die Typen einschränken und verwandte Typen an sie binden kann.

Alexei Kudryavtsev: Ich werde klarstellen, dass verwandte Typen den versiegelten Kotlin-Klassen ähnlich sind. Dies ist die Aufzählung in dem Fall, in die Sie den gebundenen Wert einfügen können. In switch können Sie schreiben: Hier ist der Fall, erweitern Sie, innerhalb des Objekts. Beispielsweise können Benutzer- und Firmenfälle mit entsprechenden Objekten aufgelistet und umgeschaltet werden. Nur versiegelte Klassen sind erweiterbar und der Schalter ist endlich.

Warum braucht ein Mobilist Funktionalismus?


Daniil Popov: Wie ist ein funktionaler Ansatz für die mobile Entwicklung nützlich? Gibt es irgendwelche Probleme, die er löst?

Evgeny Elchev: Es gibt kein spezifisches Problem, das mit Hilfe der funktionalen Programmierung präzise gelöst werden kann.

Das Wichtigste ist, dass wir, wenn wir diesen Prinzipien folgen, die Bedingungen aufgeben müssen, auch wenn sie nicht funktionieren, weil sie den Hauptschmerz darstellen.

Indem Sie sie aufgeben, machen Sie Ihren Code verständlicher. Ich sage nicht, dass es weniger Fehler geben wird, da dies zumindest gemessen werden muss. Wenn Sie jedoch mit der Implementierung beginnen, ändert sich der Code. Es kommt oft vor, dass Sie sich den Code ansehen und alles darin der Fall ist, aber Sie beginnen, unnötig und leichter zu lesen, umzuschreiben, auszutauschen, zu entfernen.

Wenn Sie dem Funktionsparadigma folgen, erhalten Sie eine zusätzliche Inspirationsquelle.

Daniil Popov: Wenn ich anfange, solche unveränderlichen Klassen in der OOP-Sprache zu schreiben und unveränderliche Methoden zu verwenden, kann ich dann sagen, dass ich funktional schreibe?

Evgeny Elchev: Ja, während Sie anfangen, die Profis zu sehen. Das Testen von Methoden wird aufgrund des Fehlens eines globalen Zustands immer einfacher. Es ist einfacher, eine Berechnungskette aus Methoden zusammenzustellen.

Daniil Popov: In Ihrem Artikel erklären Sie, was eine reine Funktion und Nebenwirkungen sind. Sie geben ein Beispiel mit Summation, bei dem die Funktion auch den externen Status ändert. Das Problem ist, dass es beim Lesen eines solchen Codes schwierig ist, alle Änderungen im Auge zu behalten: Sie müssen sich diese globale Variable ansehen, wer sie noch einliest, wer sonst darauf schreibt, was passieren kann. Mit dem funktionalen Ansatz können Sie jedoch im Stream bleiben und nicht zu benachbarten Klassen wechseln. Sie müssen nur den Code lesen.

Alexei Kudryavtsev: Wenn Sie in einer funktionalen Sprache sind, ist es einerseits einfacher für Sie, Code zu schreiben, andererseits müssen Sie verstehen, in welcher Art von Monade Sie sich gerade befinden.

Evgeny Elchev: Ja, aber wenn Sie anfangen, alles über reine Funktionen zu schreiben, treten andere Probleme auf. Zum Beispiel, wie man eine lange Kette von Berechnungen aufbaut. Im üblichen Stil können Sie, ohne darüber nachzudenken, problemlos Daten sichern, die anfangs nicht vorhanden waren. In einem funktionalen Ansatz ist dies nicht möglich: Sie müssen die Ketten aufbrechen und alle Berechnungen, die in verschiedenen Methoden verwendet werden, mit Zuständen verbinden. Man muss sich daran gewöhnen.

Im Gegensatz zu Klassen in OPP, die den Code verknöchert und schwierig zu komponieren machen, können Funktionen flexibler sein. Sie können eine Funktion schreiben, mit Hilfe des Schließens Freiheit hinzufügen, solche Funktionen werfen und sie zu Ketten kombinieren.

Alexei Kudryavtsev: Dies ähnelt der Unix-Ideologie: Es gibt Bash, Terminal, und Sie können Daten von kleinen Programmen übertragen, die eine kleine Aktion auf andere ausführen.

Daniil Popov: Es hat mich an den Rx-Ansatz erinnert, bei dem sie riesige Ketten schreiben.

Evgeny Elchev: Sie haben beide Recht. Und bei Unix-way geht es darum, und Rx ist eine Fusion der Idee von Bindung und Reaktivität. In FP binden wir an die Quelle des Ereignisses und in der Berechnungskette ändern wir sie und binden das Ergebnis an den Endzustand.

Daniil Popov: Sind Multi-Paradigmen-Sprachen überhaupt gut, wie bequem und nützlich ist es, dass die Sprache dies und das kann?

Evgeny Elchev: Wenn Sie sich strikt an ein Paradigma halten, wird es immer Dinge geben, die unpraktisch sind. Es gibt Dinge, die in einem funktionalen Stil schwer zu erreichen sind, z. B. das Speichern des Status und das Erstellen eines Caches.

Wenn es möglich ist, ein Werkzeug auszuwählen, das für eine bestimmte Aufgabe besser geeignet ist, ist das cool.

Sie können eine Klasse erstellen, darin mehrere Methoden in einem funktionalen Stil erstellen und den Code präzise in Ketten organisieren oder die Klasse vollständig verlassen, die erforderlichen Funktionen erstellen und verwenden.

Der Nachteil ist, dass es ein Dilemma der Wahl gibt und je mehr Optionen, desto schwieriger ist es zu wählen. Es wird auch schwieriger zu verstehen: Je mehr Optionen, desto schwieriger ist es, den Code zu lesen.

Über Monad Jam


Alexei Kudryavtsev: Zurück zum Funktionalismus, was ist eine Monade?

Evgeny Elchev: Ich würde es einen Container nennen, in dem Sie die Berechnungsketten kombinieren können. Der einfachste Weg ist ein Container, auf den Sie die Funktion anwenden und in einen neuen Container mit einem geänderten Wert konvertieren können.

Stellen Sie sich die Schachtel vor, in der die Erdbeere liegt, und es gibt ein Gerät, mit dem Sie Marmelade aus Erdbeeren herstellen können. Sie können jedoch keine Schachtel Erdbeeren hineinlegen, sondern müssen sie ausschütten. Monaden - genau das ermöglicht es Ihnen, eine Box in das Gerät zu stecken.

Dies ist kein Status im direkten Sinne, da der Status separat gespeichert wird. Hier ist jedoch der Kontext (Feld) mit dem Wert, und Sie wechseln von einem zum anderen. Dies ist die Übertragung von Informationen von einer Berechnung zur anderen.

Alexei Kudryavtsev: Es stellt sich heraus, dass man in einem funktionalen Ansatz, um Marmelade zu machen, in die Schachtel muss ...

Evgeny Elchev: Das Schöne ist, dass Sie nicht in die Box klettern müssen. Sie können eine Kiste werfen.

Funktionalität für die Elite?


Daniil Popov: Es gibt eine Meinung, dass funktionale Programmierung ohne eine Promotion in Mathematik nicht praktiziert werden kann. Ist das wahr?

Evgeny Elchev: Das ist nicht wahr. Mathematikkenntnisse machen natürlich alles besser, aber ich habe die Mathematik nach dem Abschluss vergessen und lebe normal. Tatsächlich sind all dies Werkzeuge, die in Sprachen zur Lösung spezifischer Probleme enthalten sind. Sie können verwendet werden, ohne zu versuchen, mathematisch zu beweisen. Während Sie eine Gleichung aus mathematischer Sicht kompilieren, ist es schneller und einfacher, ein paar Codezeilen durch Eingabe zu werfen, und sie funktionieren.

Alexei Kudryavtsev: Inwieweit kann ein Hobby für einen funktionalen Ansatz die Produktentwicklung beeinträchtigen? Wenn ein Teil des Codes bereits funktional geschrieben ist, gibt es dann Schwierigkeiten, damit zu arbeiten?

Evgeny Elchev: Überhaupt nicht. Wenn Sie kein Verrückter sind und kein riesiges Ökosystem mit Dekorateuren schreiben, können Sie den gleichen Mustervergleich verwenden.

Es wird schwieriger, wenn Sie zu einem neuen Element des Funktionalismus wechseln möchten. Zum Beispiel sind der fünfte Swift und die Ergebnismonade kürzlich erschienen. Sie hatten ihn vorher noch nicht verwendet, aber jetzt haben Sie entschieden, dass alles darauf sein wird. Sie bringen die Abfragefunktion in das Netzwerk und schreiben, dass das Ergebnis jetzt Ergebnis ist (entweder Daten oder Fehler), und Sie entscheiden sich, mit der nächsten Abfrage zu kombinieren. Dort haben Sie einen separaten Abschluss mit dem Wert und dem Fehler, und Sie müssen ihn neu schreiben. Ich begann an einer Stelle so zu schreiben und wachte zwei Tage später auf. Als ich den halben Code umschrieb, erstellte ich auch neue Wrapper für Bibliotheken, um sie wunderbar zu integrieren.

Wo soll ich anfangen?


Daniil Popov: Was sollte ein Anfänger lesen, um die funktionale Programmierung zu verstehen?

Evgeny Elchev: Wir müssen eine rein funktionale Sprache, zum Beispiel Haskell, nehmen und in der Praxis ausprobieren. Sie nehmen ein Lehrbuch und machen die einfachsten Beispiele. Hier verstehen Sie den Ansatz - wenn es kein for gibt, können Sie keine Variable erstellen, in der Sie den Wert ändern können. Persönlich habe ich einmal das Buch „Lerne Haskell im Namen des Guten“ genommen, in dem alles in einfacher Sprache beschrieben wird. Danach können Sie Artikel im Internet lesen: Wie Monaden in Swift aussehen, über algebraische Datentypen. Ein paar Artikel, und es wird klar, dass dies keine Angst haben sollte.

Daniil Popov : Das Schwierigste ist, das Paradigma in Ihrem eigenen Kopf zu brechen.

Evgeny Elchev: Sie müssen nicht scharf in die funktionale Programmierung eintauchen . Viele Leute denken, dass sie sich beide hinsetzen und anfangen werden, funktional zu schreiben - das ist falsch.

Alexei Kudryavtsev: Das Coolste, was ich gesehen habe, war ein Kurs über Stepic von Haskell von Denis Moskvin . Sie addieren zunächst einige Zahlen und wickeln die Monaden in Monaden ein. Und wenn Sie Ihre Meinung völlig brechen möchten, ist das Buch „Die Struktur der Interpretation von Computerprogrammen“ ein Kurs in Lisp, von einfachen Beispielen bis zu dem, was Sie als Lisp-Interpreter in Lisp schreiben.

Wenn die primäre Angst vor dem Funktionalismus vorbei ist, werfen Sie einen Blick auf den Bericht von Vitaliy Bragilevsky von der Frühjahrs-AppsConf. In der Herbstsaison von AppsConf werden wir jedoch nicht weniger interessante Themen ansprechen - die iOS-Community freut sich auf einen Bericht von Daniil Goncharov über Bluetooth-Reverse Engineering , und Android-Entwickler werden zusammen mit Alexander Smirnov aktuelle Ansätze zum Erstellen von Animationen diskutieren

Source: https://habr.com/ru/post/de462121/


All Articles