Ich habe kürzlich den FP-Fehler entdeckt (beim Versuch, Haskell zu lernen) und war wirklich beeindruckt von dem, was ich bisher gesehen habe (erstklassige Funktionen, verzögerte Bewertung und all die anderen Extras). Ich bin noch kein Experte, aber ich habe bereits begonnen, es einfacher zu finden, "funktional" zu argumentieren als zwingend für grundlegende Algorithmen (und ich habe Probleme, dorthin zurückzukehren, wo ich muss).
Der einzige Bereich, in dem die aktuelle FP flach zu fallen scheint, ist die GUI-Programmierung. Der Haskell-Ansatz scheint darin zu bestehen, nur imperative GUI-Toolkits (wie GTK + oder wxWidgets) zu verpacken und "do" -Blöcke zu verwenden, um einen imperativen Stil zu simulieren. Ich habe F # nicht verwendet, aber ich verstehe, dass es mit OOP mit .NET-Klassen etwas Ähnliches macht. Offensichtlich gibt es dafür einen guten Grund: Bei der aktuellen GUI-Programmierung dreht sich alles um E / A und Nebenwirkungen, sodass mit den meisten aktuellen Frameworks keine rein funktionale Programmierung möglich ist.
Meine Frage ist, ist es möglich, einen funktionalen Ansatz für die GUI-Programmierung zu haben? Ich kann mir nur schwer vorstellen, wie das in der Praxis aussehen würde. Kennt jemand irgendwelche experimentellen oder sonstigen Frameworks, die solche Dinge ausprobieren (oder sogar Frameworks, die von Grund auf für eine funktionale Sprache entwickelt wurden)? Oder ist die Lösung, nur einen hybriden Ansatz zu verwenden, mit OOP für die GUI-Teile und FP für die Logik? (Ich frage nur aus Neugier - ich würde gerne denken, dass FP "die Zukunft" ist, aber die GUI-Programmierung scheint ein ziemlich großes Loch zu sein, das es zu füllen gilt.)
Antworten:
Das ist nicht wirklich der "Haskell-Ansatz" - so binden Sie sich am direktesten an imperative GUI-Toolkits - über eine imperative Schnittstelle. Haskell hat zufällig ziemlich markante Bindungen.
Es gibt mehrere mäßig ausgereifte oder experimentellere rein funktionale / deklarative Ansätze für GUIs, hauptsächlich in Haskell, und hauptsächlich unter Verwendung funktionaler reaktiver Programmierung.
Einige Beispiele sind:
Für diejenigen unter Ihnen, die mit Haskell, Flapjax, nicht vertraut sind, ist http://www.flapjax-lang.org/ eine Implementierung der funktionalen reaktiven Programmierung auf JavaScript.
quelle
Die Schlüsselwörter, nach denen Sie suchen, sind "Functional Reactive Programming" (FRP).
Conal Elliott und einige andere haben es sich zur Aufgabe gemacht, die richtige Abstraktion für FRP zu finden. In Haskell gibt es mehrere Implementierungen von FRP-Konzepten.
Sie könnten in Betracht ziehen, mit Conals jüngstem Artikel "Push-Pull Functional Reactive Programming" zu beginnen , aber es gibt mehrere andere (ältere) Implementierungen, von denen einige über die Website haskell.org verlinkt sind . Conal hat ein Händchen für die Abdeckung der gesamten Domäne, und sein Artikel kann ohne Bezugnahme auf das Vorherige gelesen werden.
Um ein Gefühl dafür zu bekommen, wie dieser Ansatz für die GUI-Entwicklung verwendet werden kann, sollten Sie sich Fudgets ansehen , das, obwohl es heutzutage etwas langwierig wird und Mitte der 90er Jahre entwickelt wurde, einen soliden FRP-Ansatz darstellt zum GUI-Design.
quelle
Windows Presentation Foundation ist ein Beweis dafür, dass der funktionale Ansatz für die GUI-Programmierung sehr gut funktioniert. Es hat viele funktionale Aspekte und "guter" WPF-Code (Suche nach MVVM-Muster) betont den funktionalen Ansatz gegenüber dem Imperativ. Ich könnte mutig behaupten, dass WPF das erfolgreichste funktionale GUI-Toolkit der realen Welt ist :-)
WPF beschreibt die Benutzeroberfläche in XAML (obwohl Sie sie auch in C # oder F # umschreiben können), um eine Benutzeroberfläche zu erstellen, die Sie schreiben würden:
Darüber hinaus können Sie mit WPF Animationen und Reaktionen auf Ereignisse mithilfe eines anderen Satzes deklarativer Tags deklarativ beschreiben (dasselbe kann auch als C # / F # -Code geschrieben werden):
Tatsächlich denke ich, dass WPF viele Gemeinsamkeiten mit Haskells FRP hat (obwohl ich glaube, dass WPF-Designer nichts über FRP wussten und es ein bisschen unglücklich ist - WPF fühlt sich manchmal etwas seltsam und unklar an, wenn Sie die Funktion verwenden Perspektive).
quelle
INotifyPropertyChanged
), scheint FP im Widerspruch zu stehen. Ich bin definitiv kein Experte für FP, und vielleicht konzentriere ich mich zu sehr auf den Unveränderlichkeitsaspekt im Gegensatz zum deklarativen Aspekt, aber ich habe Probleme zu erkennen, wie das MVVM-Muster (wie es normalerweise verwendet wird) ein Beispiel für FP ist.INotifyPropertyChanged
ist nur eine Update-Funktion, die Sie überall dort übergeben, wo Sie die GUI-Updates durchführen müssen - es handelt sich um eine Latenz-Korrektur.Ich würde tatsächlich sagen, dass die funktionale Programmierung (F #) ein viel besseres Werkzeug für die Programmierung der Benutzeroberfläche ist als zum Beispiel C #. Sie müssen nur ein wenig anders über das Problem nachdenken.
Ich diskutiere dieses Thema in meinem funktionalen Programmierbuch in Kapitel 16, aber es gibt einen kostenlosen Auszug , der (IMHO) das interessanteste Muster zeigt, das Sie in F # verwenden können. Angenommen, Sie möchten das Zeichnen von Rechtecken implementieren (Benutzer drückt die Taste, bewegt die Maus und lässt die Taste los). In F # können Sie so etwas schreiben:
Dies ist ein sehr zwingender Ansatz (im üblichen pragmatischen F # -Stil), der jedoch die Verwendung eines veränderlichen Zustands zum Speichern des aktuellen Zeichnungszustands und zum Speichern der Anfangsposition vermeidet. Es kann jedoch noch funktionaler gestaltet werden. Ich habe eine Bibliothek geschrieben, die dies im Rahmen meiner Masterarbeit tut und in den nächsten Tagen auf meinem Blog verfügbar sein sollte .
Funktionale reaktive Programmierung ist ein funktionalerer Ansatz, aber ich finde es etwas schwieriger, ihn zu verwenden, da er auf ziemlich fortgeschrittenen Haskell-Funktionen (wie Pfeilen) beruht. Es ist jedoch in vielen Fällen sehr elegant. Die Einschränkung besteht darin, dass Sie eine Zustandsmaschine nicht einfach codieren können (was ein nützliches mentales Modell für reaktive Programme ist). Dies ist mit der obigen F # -Technik sehr einfach.
quelle
IObservable
.Egal, ob Sie in einer hybriden funktionalen / OO-Sprache wie F # oder OCaml oder in einer rein funktionalen Sprache wie Haskell arbeiten, in der Nebenwirkungen auf die E / A-Monade verwiesen werden, es ist meistens so , dass eine Menge Arbeit für die Verwaltung einer GUI erforderlich ist ist viel mehr wie ein "Nebeneffekt" als wie ein rein funktionaler Algorithmus.
Trotzdem wurden einige wirklich solide Untersuchungen zu funktionalen GUIs durchgeführt . Es gibt sogar einige (meistens) funktionale Toolkits wie Fudgets oder FranTk .
quelle
Sie können sich die Serie von Don Syme auf F # ansehen, in der er eine GUI-Demo erstellt. Der folgende Link führt zum dritten Teil der Serie (von dort aus können Sie zu den beiden anderen Teilen verlinken).
Die Verwendung von F # für die WPF-Entwicklung wäre ein sehr interessantes GUI-Paradigma ...
http://channel9.msdn.com/shows/Going+Deep/C9-Lectures-Dr-Don-Syme-Introduction-to-F-3-of-3/
quelle
Eine der aufschlussreichen Ideen hinter Functional Reactive Programming ist eine Ereignisbehandlungsfunktion, die sowohl eine Reaktion auf Ereignisse als auch die nächste Ereignisbehandlungsfunktion erzeugt. Somit wird ein sich entwickelndes System als eine Folge von Ereignisbehandlungsfunktionen dargestellt.
Für mich wurde das Erlernen von Yampa zu einem entscheidenden Punkt, um diese funktionsproduzierenden Funktionen richtig zu machen. Es gibt einige nette Papiere über Yampa. Ich empfehle The Yampa Arcade:
http://www.cs.nott.ac.uk/~nhn/Talks/HW2003-YampaArcade.pdf (Folien, PDF) http://www.cs.nott.ac.uk/~nhn/Publications/hw2003. pdf (vollständiger Artikel, PDF)
Es gibt eine Wiki-Seite auf Yampa bei Haskell.org
http://www.haskell.org/haskellwiki/Yampa
Original Yampa Homepage:
http://www.haskell.org/yampa (ist momentan leider kaputt)
quelle
Seit diese Frage zum ersten Mal gestellt wurde, hat Elm die funktionale reaktive Programmierung etwas mehr zum Mainstream gemacht.
Ich schlage vor, es unter http://elm-lang.org zu lesen , wo es auch einige wirklich hervorragende interaktive Tutorials gibt, wie man eine voll funktionsfähige GUI im Browser erstellt.
Sie können damit voll funktionsfähige GUIs erstellen, bei denen der Code, den Sie selbst bereitstellen müssen, nur aus reinen Funktionen besteht. Ich persönlich fand es viel einfacher, in die verschiedenen Haskell-GUI-Frameworks einzusteigen.
quelle
Elliots Vortrag über FRP finden Sie hier .
Außerdem nicht wirklich eine Antwort, sondern eine Bemerkung und ein paar Gedanken : Irgendwie scheint der Begriff "funktionale GUI" ein bisschen wie ein Oxymoron zu sein (Reinheit und E / A im selben Begriff).
Mein vages Verständnis ist jedoch, dass es bei der funktionalen GUI-Programmierung darum geht, eine zeitabhängige Funktion deklarativ zu definieren, die die (Echtzeit-) abhängige Benutzereingabe verwendet und eine zeitabhängige GUI-Ausgabe erzeugt.
Mit anderen Worten, diese Funktion wird deklarativ wie eine Differentialgleichung definiert, anstatt durch einen Algorithmus, der zwingend einen veränderlichen Zustand verwendet.
In herkömmlichen FP werden zeitunabhängige Funktionen verwendet, während in FRP zeitabhängige Funktionen als Bausteine zur Beschreibung eines Programms verwendet werden.
Denken wir darüber nach, einen Ball auf einer Feder zu simulieren, mit dem der Benutzer interagieren kann. Die Position des Balls ist die grafische Ausgabe (auf dem Bildschirm). Der Benutzer, der den Ball drückt, ist ein Tastendruck (Eingabe).
Die Beschreibung dieses Simulationsprogramms in FRP (nach meinem Verständnis) erfolgt durch eine einzige Differentialgleichung (deklarativ): Beschleunigung * Masse = - Federdehnung * Federkonstante + vom Benutzer ausgeübte Kraft.
Hier ist ein Video zu ELM , das diesen Standpunkt veranschaulicht.
quelle
Ab 2016 gibt es für Haskell mehrere relativ ausgereifte FRP-Frameworks wie Natrium und Reflex (aber auch Netwire).
Das Manning-Buch über funktionale reaktive Programmierung zeigt anhand von Arbeitsbeispielen die Java-Version von Sodium und zeigt, wie sich eine FRP-GUI-Codebasis im Vergleich zu imperativen und aktorbasierten Ansätzen verhält und skaliert.
Es gibt auch ein kürzlich veröffentlichtes Papier über Arrowized FRP und die Aussicht, Nebenwirkungen, IO und Mutation in eine gesetzestreue, reine FRP-Einstellung einzubeziehen: http://haskell.cs.yale.edu/wp-content/uploads/2015/10/ dwc-yale-formatierte-dissertation.pdf .
Erwähnenswert ist auch, dass JavaScript-Frameworks wie ReactJS und Angular und viele andere entweder bereits einen FRP oder einen anderen funktionalen Ansatz zur Erzielung skalierbarer und zusammensetzbarer GUI-Komponenten verwenden oder verwenden.
quelle
Mit Markup-Sprachen wie XUL können Sie eine GUI deklarativ erstellen.
quelle
Um dies zu beheben, habe ich einige meiner Gedanken zur Verwendung von F # gepostet.
http://fadsworld.wordpress.com/2011/04/13/f-in-the-enterprise-i/ http://fadsworld.wordpress.com/2011/04/17/fin-the-enterprise-ii- 2 /
Ich plane auch ein Video-Tutorial, um die Serie zu beenden und zu zeigen, wie F # zur UX-Programmierung beitragen kann.
Ich spreche hier nur im Zusammenhang mit F #.
-Fahad
quelle
Alle diese anderen Antworten basieren auf funktionaler Programmierung, treffen jedoch viele eigene Entwurfsentscheidungen. Eine Bibliothek, die im Wesentlichen vollständig aus Funktionen und einfachen abstrakten Datentypen besteht, ist
gloss
. Hier ist der Typ für seineplay
Funktion aus der QuelleWie Sie sehen können, funktioniert es vollständig, indem reine Funktionen mit einfachen abstrakten Typen bereitgestellt werden, bei denen andere Bibliotheken Ihnen helfen.
quelle
Die offensichtlichste Neuerung, die Menschen in Haskell bemerken, ist die Trennung zwischen der unreinen Welt, die sich mit der Kommunikation mit der Außenwelt befasst, und der reinen Welt der Berechnungen und Algorithmen. Eine häufige Anfängerfrage lautet: "Wie kann ich loswerden
IO
, dh konvertierenIO a
ina
?" Der Weg dorthin besteht darin, Monaden (oder andere Abstraktionen) zu verwenden, um Code zu schreiben, der E / A- und Ketteneffekte ausführt. Dieser Code sammelt Daten aus der Außenwelt, erstellt ein Modell davon, führt einige Berechnungen durch, möglicherweise unter Verwendung von reinem Code, und gibt das Ergebnis aus.Was das obige Modell betrifft, sehe ich nichts Schreckliches daran, GUIs in der
IO
Monade zu manipulieren . Das größte Problem, das sich aus diesem Stil ergibt, ist, dass Module nicht mehr zusammensetzbar sind, dh ich verliere den größten Teil meines Wissens über die globale Ausführungsreihenfolge von Anweisungen in meinem Programm. Um es wiederherzustellen, muss ich ähnliche Überlegungen anstellen wie im gleichzeitigen, zwingenden GUI-Code. Für unreinen Nicht-GUI-Code ist die Ausführungsreihenfolge aufgrund der Definition desIO
Monadenoperators offensichtlich>==
(mindestens solange nur ein Thread vorhanden ist). Für reinen Code spielt es keine Rolle, außer in Eckfällen, um die Leistung zu steigern oder Auswertungen zu vermeiden, die dazu führen⊥
.Der größte philosophische Unterschied zwischen Konsole und grafischer E / A besteht darin, dass Programme, die erstere implementieren, normalerweise synchron geschrieben werden. Dies ist möglich, weil es (abgesehen von Signalen und anderen offenen Dateideskriptoren) nur eine Ereignisquelle gibt: den allgemein aufgerufenen Bytestream
stdin
. GUIs sind jedoch von Natur aus asynchron und müssen auf Tastaturereignisse und Mausklicks reagieren.Eine beliebte Philosophie, asynchrone E / A auf funktionale Weise auszuführen, heißt Functional Reactive Programming (FRP). Dank Bibliotheken wie ReactiveX und Frameworks wie Elm hat es in letzter Zeit in unreinen, nicht funktionierenden Sprachen viel Anklang gefunden. Kurz gesagt, es ist so, als würde man GUI-Elemente und andere Dinge (wie Dateien, Uhren, Alarme, Tastatur, Maus) als Ereignisquellen, sogenannte "Observables", anzeigen, die Ereignisströme ausgeben. Diese Ereignisse werden mit dem bekannten Operatoren wie
map
,foldl
,zip
,filter
,concat
,join
, etc., um neue Streams zu erzeugen. Dies ist nützlich, da der Programmstatus selbst abscanl . map reactToEvents $ zipN <eventStreams>
dem Programm gesehen werden kann, wobei diesN
der Anzahl der Observablen entspricht, die jemals vom Programm berücksichtigt wurden.Durch die Arbeit mit FRP-Observablen kann die Kompositionsfähigkeit wiederhergestellt werden, da Ereignisse in einem Stream rechtzeitig geordnet werden. Der Grund dafür ist, dass die Ereignisstromabstraktion es ermöglicht, alle Observablen als Black Boxes anzuzeigen. Letztendlich gibt das Kombinieren von Ereignisströmen mit Operatoren bei der Ausführung eine lokale Reihenfolge zurück. Dies zwingt mich, viel ehrlicher darüber zu sein, auf welche Invarianten sich mein Programm tatsächlich stützt, ähnlich wie alle Funktionen in Haskell referenziell transparent sein müssen: Wenn ich Daten aus einem anderen Teil meines Programms abrufen möchte, muss ich explizit sein Anzeige deklarieren Sie einen geeigneten Typ für meine Funktionen. (Die E / A-Monade, eine domänenspezifische Sprache zum Schreiben von unreinem Code, umgeht dies effektiv.)
quelle
Die funktionale Programmierung mag sich seit meinem Studium weiterentwickelt haben, aber wie ich mich erinnere, bestand der Hauptpunkt eines funktionalen Programmiersystems darin, den Programmierer daran zu hindern, „Nebenwirkungen“ zu verursachen. Benutzer kaufen jedoch Software aufgrund der Nebenwirkungen, die entstehen, z. B. beim Aktualisieren einer Benutzeroberfläche.
quelle