Niedawno złapałem błąd FP (próbując nauczyć się Haskella) i byłem pod wielkim wrażeniem tego, co do tej pory widziałem (pierwszorzędne funkcje, leniwa ocena i wszystkie inne zalety). Nie jestem jeszcze ekspertem, ale zacząłem już łatwiej rozumować „funkcjonalnie” niż bezwzględnie dla podstawowych algorytmów (i mam problem z powrotem tam, gdzie muszę).
Jednak jedynym obszarem, w którym wydaje się, że obecny FP jest płaski, jest programowanie GUI. Podejście Haskell wydaje się polegać tylko na pakowaniu imperatywnych zestawów narzędzi GUI (takich jak GTK + lub wxWidgets) i używaniu bloków „do” do symulacji stylu imperatywnego. Nie korzystałem z F #, ale rozumiem, że robi coś podobnego przy użyciu OOP z klasami .NET. Oczywiście jest ku temu dobry powód - obecne programowanie GUI dotyczy IO i efektów ubocznych, więc czysto funkcjonalne programowanie nie jest możliwe w przypadku większości obecnych frameworków.
Moje pytanie brzmi: czy możliwe jest funkcjonalne podejście do programowania GUI? Mam problem z wyobrażeniem sobie, jak to wyglądałoby w praktyce. Czy ktoś zna jakieś frameworki, eksperymentalne lub inne, które próbują tego rodzaju rzeczy (a nawet jakiekolwiek frameworki, które są zaprojektowane od podstaw dla funkcjonalnego języka)? Czy może rozwiązaniem jest zastosowanie podejścia hybrydowego z OOP dla części GUI i FP dla logiki? (Po prostu pytam z ciekawości - chciałbym myśleć, że FP to „przyszłość”, ale programowanie GUI wydaje się być dość dużą dziurą do wypełnienia.)
Odpowiedzi:
To nie jest tak naprawdę „podejście Haskell” - tak właśnie łączy się bezpośrednio z niezbędnymi zestawami narzędzi GUI - za pomocą interfejsu imperatywnego. Haskell po prostu ma dość wyraźne wiązania.
Istnieje kilka umiarkowanie dojrzałych lub bardziej eksperymentalnych czysto funkcjonalnych / deklaratywnych podejść do GUI, głównie w Haskell, i przede wszystkim z wykorzystaniem programowania funkcjonalnego reaktywnego.
Oto niektóre przykłady:
Dla tych z Was, którzy nie znają Haskell, Flapjax, http://www.flapjax-lang.org/ to implementacja funkcjonalnego programowania reaktywnego na JavaScript.
źródło
Kluczowe słowa, których szukasz, to „funkcjonalne programowanie reaktywne” (FRP).
Conal Elliott i inni stworzyli trochę przemysłu chałupniczego, próbując znaleźć odpowiednią abstrakcję dla FRP. W Haskell istnieje kilka implementacji koncepcji FRP.
Możesz rozważyć rozpoczęcie od najnowszego artykułu Conala „Funkcjonalne programowanie reaktywne w trybie push-pull” , ale istnieje kilka innych (starszych) implementacji, niektóre z nich powiązane ze strony haskell.org . Conal ma talent do zajmowania się całą domeną, a jego artykuł można przeczytać bez odniesienia do tego, co było wcześniej.
Aby przekonać się, w jaki sposób można zastosować to podejście do tworzenia GUI, warto spojrzeć na Fudgets , które, choć ostatnio stają się coraz bardziej popularne, zaprojektowane w połowie lat 90., stanowią solidne podejście FRP do projektowania GUI.
źródło
Windows Presentation Foundation jest dowodem na to, że funkcjonalne podejście działa bardzo dobrze w programowaniu GUI. Ma wiele aspektów funkcjonalnych, a „dobry” kod WPF (poszukiwanie wzorca MVVM) podkreśla funkcjonalne podejście nad koniecznością. Mógłbym śmiało twierdzić, że WPF jest najbardziej udanym zestawem funkcjonalnych GUI w świecie rzeczywistym :-)
WPF opisuje interfejs użytkownika w XAML (chociaż można go przepisać do funkcjonalnie wyglądającego C # lub F #), więc aby utworzyć interfejs, należy napisać:
Ponadto WPF pozwala również deklaratywnie opisywać animacje i reakcje na zdarzenia przy użyciu innego zestawu deklaratywnych znaczników (znowu to samo można zapisać jako kod C # / F #):
W rzeczywistości myślę, że WPF ma wiele cech wspólnych z FRP Haskella (choć uważam, że projektanci WPF nie wiedzieli o FRP i jest to trochę niefortunne - WPF czasami wydaje się dziwne i niejasne, jeśli używasz funkcji punkt widzenia).
źródło
INotifyPropertyChanged
wszystkimi rzeczami), wydaje mi się dla FP sprzeczne z hipotezą. Zdecydowanie nie jestem ekspertem od FP i może zbytnio skupiam się na aspekcie niezmienności w przeciwieństwie do aspektu deklaratywnego, ale mam problem z dostrzeżeniem, w jaki sposób wzorzec MVVM (jak zwykle używany) jest przykładem FP.INotifyPropertyChanged
to tylko funkcja aktualizacji, którą przekazujesz wszędzie tam, gdzie musisz obsłużyć aktualizacje GUI - to naprawa opóźnienia.Powiedziałbym, że programowanie funkcjonalne (F #) jest znacznie lepszym narzędziem do programowania interfejsu użytkownika niż na przykład C #. Musisz tylko pomyśleć o problemie nieco inaczej.
Omawiam ten temat w mojej książce o programowaniu funkcjonalnym w rozdziale 16, ale dostępny jest bezpłatny fragment , który pokazuje (IMHO) najciekawszy wzorzec, którego można użyć w języku F #. Powiedz, że chcesz zastosować rysunek prostokątów (użytkownik naciska przycisk, porusza myszą i zwalnia przycisk). W F # możesz napisać coś takiego:
Jest to bardzo imperatywne podejście (w zwykłym pragmatycznym stylu F #), ale pozwala uniknąć użycia stanu zmiennego do przechowywania aktualnego stanu rysowania i do przechowywania początkowej lokalizacji. Może być jeszcze bardziej funkcjonalny, napisałem bibliotekę, która robi to w ramach mojej pracy magisterskiej, która powinna być dostępna na moim blogu w ciągu najbliższych kilku dni.
Funkcjonalne programowanie reaktywne jest bardziej funkcjonalnym podejściem, ale korzystanie z niego jest nieco trudniejsze, ponieważ opiera się na dość zaawansowanych funkcjach Haskell (takich jak strzałki). Jest jednak bardzo elegancki w wielu przypadkach. Ograniczeniem jest to, że nie można łatwo zakodować automatu stanów (który jest przydatnym modelem mentalnym dla programów reaktywnych). Jest to bardzo łatwe przy użyciu powyższej techniki F #.
źródło
IObservable
.Nieważne, czy jesteś w hybrydowym funkcjonalnej / oo języka jak F # lub SML, lub w języku czysto funkcjonalnego jak Haskell, gdzie skutki uboczne są spadł do IO monady, to najczęściej zdarza się, że mnóstwo pracy wymagane do zarządzania GUI jest znacznie bardziej jak „efekt uboczny” niż czysto funkcjonalny algorytm.
To powiedziawszy, przeprowadzono kilka naprawdę solidnych badań nad funkcjonalnymi GUI . Istnieją nawet (głównie) funkcjonalne zestawy narzędzi, takie jak Fudgets lub FranTk .
źródło
Możesz sprawdzić serię Don Syme na F #, gdzie demo tworzy gui. poniższy link jest do trzeciej części serii (możesz stamtąd link do pozostałych dwóch części).
Użycie F # do rozwoju WPF byłoby bardzo interesującym paradygmatem GUI ...
http://channel9.msdn.com/shows/Going+Deep/C9-Lectures-Dr-Don-Syme-Introduction-to-F-3-of-3/
źródło
Jednym z otwierających umysły pomysłów na funkcjonalne programowanie reaktywne jest posiadanie funkcji obsługi zdarzeń, ZARÓWNO reakcję na zdarzenia ORAZ następnej funkcji obsługi zdarzeń. W ten sposób ewoluujący system jest reprezentowany jako sekwencja funkcji obsługi zdarzeń.
Dla mnie nauka Yampy stała się kluczową sprawą, aby poprawnie uzyskać funkcje produkujące funkcje. Jest kilka fajnych artykułów na temat Yampy. Polecam The Yampa Arcade:
http://www.cs.nott.ac.uk/~nhn/Talks/HW2003-YampaArcade.pdf (slajdy, PDF) http://www.cs.nott.ac.uk/~nhn/Publications/hw2003. pdf (pełny artykuł, PDF)
Istnieje strona wiki na Yampa na Haskell.org
http://www.haskell.org/haskellwiki/Yampa
Oryginalna strona główna Yampa:
http://www.haskell.org/yampa (obecnie niestety jest zepsuty)
źródło
Od momentu postawienia tego pytania Elm sprawił, że funkcjonalne programowanie reaktywne stało się bardziej popularne.
Proponuję sprawdzić to na stronie http://elm-lang.org , która zawiera również kilka naprawdę doskonałych interaktywnych samouczków na temat tworzenia w pełni funkcjonalnego GUI w przeglądarce.
Pozwala tworzyć w pełni funkcjonalne GUI, w których kod, który sam musisz podać, składa się wyłącznie z czystych funkcji. Osobiście uważałem, że łatwiej jest się do niego dostać niż różne frameworki GUI Haskell.
źródło
Rozmowę Elliota na temat FRP można znaleźć tutaj .
Ponadto nie jest to tak naprawdę odpowiedź, ale uwaga i kilka przemyśleń : w pewnym sensie termin „funkcjonalny GUI” przypomina trochę oksymoron (czystość i IO w tym samym znaczeniu).
Ale moje niejasne zrozumienie jest takie, że funkcjonalne programowanie GUI polega na deklaratywnym zdefiniowaniu funkcji zależnej od czasu, która pobiera (zależne od czasu) dane wejściowe użytkownika i tworzy zależne od czasu dane wyjściowe GUI.
Innymi słowy, funkcja ta jest zdefiniowana deklaracyjnie jak równanie różniczkowe, zamiast przez algorytm bezwzględnie wykorzystujący stan zmienny.
Tak więc w konwencjonalnym FP używa się funkcji niezależnych od czasu, podczas gdy we FRP używa się funkcji zależnych od czasu jako elementów składowych do opisu programu.
Zastanówmy się nad symulacją piłki na sprężynie, z którą użytkownik może wchodzić w interakcje. Pozycja piłki jest wyjściem graficznym (na ekranie), użytkownik popychający piłkę to naciśnięcie klawisza (wejście).
Opisanie tego programu symulacyjnego we FRP (zgodnie z moim rozumieniem) odbywa się za pomocą jednego równania różniczkowego (deklaratywnie): przyspieszenie * masa = - rozciągnięcie sprężyny * stała sprężyny + siła wywierana przez użytkownika.
Oto wideo na temat ELM, które ilustruje ten punkt widzenia.
źródło
Od 2016 r. Istnieje kilka innych, stosunkowo dojrzałych platform FRP dla Haskell, takich jak Sodium i Reflex (ale także Netwire).
Książka Manning na funkcyjną reagującą Programowanie gablotach wersja Java sód przykładów roboczych i ilustruje, w jaki sposób zachowuje się baza kodu FRP GUI i wagi w porównaniu z bezwzględnym, jak podejść opartych aktora.
Istnieje również najnowszy artykuł na temat Arrowized FRP i perspektywy włączenia efektów ubocznych, IO i mutacji do zgodnych z prawem, czystych ustawień FRP: http://haskell.cs.yale.edu/wp-content/uploads/2015/10/ dwc-yale-formatted-dissertation.pdf .
Warto również zauważyć, że frameworki JavaScript, takie jak ReactJS i Angular, i wiele innych już stosuje lub zbliża się do korzystania z FRP lub innego funkcjonalnego podejścia do uzyskania skalowalnych i możliwych do skomponowania komponentów GUI.
źródło
Języki znaczników, takie jak XUL, umożliwiają tworzenie GUI w sposób deklaratywny.
źródło
Aby rozwiązać ten problem, zamieściłem kilka moich przemyśleń na temat używania F #,
http://fadsworld.wordpress.com/2011/04/13/f-in-the-enterprise-i/ http://fadsworld.wordpress.com/2011/04/17/fin-the-enterprise-ii- 2 /
Planuję również zrobić samouczek wideo, aby dokończyć serię i pokazać, w jaki sposób F # może przyczynić się do programowania UX.
Mówię tu tylko w kontekście F #.
-Fahad
źródło
Wszystkie pozostałe odpowiedzi są oparte na programowaniu funkcjonalnym, ale podejmują wiele własnych decyzji projektowych. Jedną biblioteką zbudowaną zasadniczo całkowicie z funkcji i prostych abstrakcyjnych typów danych jest
gloss
. Oto typ jegoplay
funkcji ze źródłaJak widać, działa całkowicie, dostarczając czyste funkcje z prostymi typami abstrakcyjnymi, w czym pomagają inne biblioteki.
źródło
Najbardziej widoczną innowacją zauważoną przez nowych użytkowników Haskell jest oddzielenie nieczystego świata związanego z komunikowaniem się ze światem zewnętrznym od czystego świata obliczeń i algorytmów. Często zadawane pytanie dla początkujących brzmi: „Jak się pozbyć
IO
, tzn. ZamienićIO a
naa
?”. Sposób na to polega na użyciu monad (lub innych abstrakcji) do napisania kodu wykonującego efekty IO i łańcuchowe. Ten kod zbiera dane ze świata zewnętrznego, tworzy jego model, wykonuje pewne obliczenia, prawdopodobnie wykorzystując czysty kod i generuje wynik.Jeśli chodzi o powyższy model, nie widzę nic strasznie złego w manipulowaniu GUI w
IO
monadzie. Największym problemem wynikającym z tego stylu jest to, że moduły nie są już możliwe do skomponowania, tzn. Tracę większość mojej wiedzy na temat globalnej kolejności wykonywania instrukcji w moim programie. Aby go odzyskać, muszę zastosować podobne rozumowanie jak w przypadku współbieżnego, bezwzględnego kodu GUI. Tymczasem w przypadku nieczystego kodu bez GUI kolejność wykonywania jest oczywista ze względu na definicję operatoraIO
monady>==
(przynajmniej tak długo, jak istnieje tylko jeden wątek). W przypadku czystego kodu nie ma to żadnego znaczenia, z wyjątkiem przypadków narożnych, aby zwiększyć wydajność lub uniknąć wyników oceny⊥
.Największą filozoficzną różnicą między konsolowym a graficznym We / Wy jest to, że programy implementujące te pierwsze są zwykle pisane w stylu synchronicznym. Jest to możliwe, ponieważ istnieje (pomijając sygnały i inne otwarte deskryptory plików) tylko jedno źródło zdarzeń: powszechnie nazywany strumień bajtów
stdin
. GUI są z natury asynchroniczne i muszą reagować na zdarzenia z klawiatury i kliknięcia myszą.Popularna filozofia wykonywania asynchronicznych operacji we / wy w sposób funkcjonalny nosi nazwę Functional Reactive Programming (FRP). Ostatnio zyskał dużą przyczepność w nieczystych, niefunkcjonalnych językach dzięki bibliotekom takim jak ReactiveX i frameworkom takim jak Elm. W skrócie, to jak przeglądanie elementów GUI i innych rzeczy (takich jak pliki, zegary, alarmy, klawiatura, mysz) jako źródeł zdarzeń, zwanych „obserwowalnymi”, które emitują strumienie zdarzeń. Zdarzenia te są łączone za pomocą znanych, takich jak operatorzy
map
,foldl
,zip
,filter
,concat
,join
, itd., W celu wytworzenia nowych strumieni. Jest to przydatne, ponieważ sam stan programu może być postrzegany jakoscanl . map reactToEvents $ zipN <eventStreams>
program, gdzieN
jest równy liczbie obserwowalnych kiedykolwiek rozważanych przez program.Praca z obserwowalnymi FRP pozwala odzyskać kompozycję, ponieważ zdarzenia w strumieniu są uporządkowane w czasie. Powodem jest to, że abstrakcja strumienia zdarzeń umożliwia wyświetlanie wszystkich obserwowalnych obiektów jako czarnych skrzynek. Ostatecznie połączenie strumieni zdarzeń za pomocą operatorów zwróci niektóre lokalne porządki przy wykonywaniu. Zmusza mnie to do większej szczerości w kwestii niezmienników, na których faktycznie polega mój program, podobnie do tego, w jaki sposób wszystkie funkcje w Haskell muszą być referencyjnie przejrzyste: jeśli chcę pobierać dane z innej części mojego programu, muszę wyrazić się jasno reklama deklaruje odpowiedni typ dla moich funkcji. (Monada IO, będąca językiem specyficznym dla domeny do pisania nieczystego kodu, skutecznie to omija)
źródło
Programowanie funkcjonalne mogło się zmienić od czasu, gdy byłem na uniwersytecie, ale o ile pamiętam, głównym celem funkcjonalnego systemu programowania było powstrzymanie programisty przed jakimkolwiek „efektem ubocznym”. Jednak użytkownicy kupują oprogramowanie z powodu tworzonych efektów ubocznych, np. Aktualizacji interfejsu użytkownika.
źródło