Czy funkcjonalne programowanie GUI jest możliwe? [zamknięte]

Niedawno złapałem błąd FP( próbując nauczyć się Haskella), i byłem naprawdę pod wrażeniem tego, co widziałem do tej pory (funkcje pierwszej klasy, leniwa Ocena i wszystkie inne gadżety). Nie jestem jeszcze ekspertem, ale już zacząłem przekonywać się, że łatwiej jest rozumować "funkcjonalnie" niż imperatywnie dla podstawowych algorytmów (i mam problem z powrotem tam, gdzie muszę).

Jedynym obszarem, w którym obecny FP wydaje się być płaski, jest programowanie GUI. Podejście Haskella wydaje się być GTK+ lub wxWidgets) i używać bloków" do " do symulacji stylu imperatywnego. Nie używałem F#, ale rozumiem, że robi coś podobnego używając OOP z klasami. NET. Oczywiście, jest ku temu dobry powód-obecne programowanie GUI polega na IO i efektach ubocznych, więc programowanie czysto funkcjonalne nie jest możliwe w większości obecnych frameworków.

Moje pytanie brzmi, czy można mieć funkcjonalne podejście do programowania GUI? Mam problem z wyobrażeniem sobie, jak to będzie wyglądało w praktyce. Czy ktoś zna jakieś frameworki, eksperymentalne lub inne, które próbują tego typu rzeczy (lub nawet wszelkie frameworki, które są zaprojektowane od podstaw dla języka funkcjonalnego)? A może rozwiązaniem jest po prostu użycie podejścia hybrydowego, z OOP dla części GUI i FP dla logiki? (Pytam tylko z ciekawości-chętnie bym pomyślał, że FP to "przyszłość", ale programowanie GUI wydaje się dość dużą dziurą do wypełnienia.)

Author: shosti , 2010-04-20

15 answers

GTK+ lub wxWidgets) i używać bloków" do " do symulacji imperatywnego stylu

To nie jest tak naprawdę "Haskell approach" -- w ten sposób można powiązać imperatywne Zestawy narzędzi GUI najbardziej bezpośrednio -- poprzez imperatywny interfejs. Haskell 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 za pomocą funkcjonalnego programowania reaktywnego.

Niektóre przykłady to:

Dla tych z Was, którzy nie znają Haskell, Flapjax, http://www.flapjax-lang.org/ jest implementacją functional reactive programming na bazie JavaScript.
 166
Author: Don Stewart,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-28 23:03:41

Moje pytanie brzmi, czy można mieć funkcjonalne podejście do programowania GUI?

Słowa kluczowe, których szukasz, to "functional reactive programming" (FRP).

Conal Elliott i niektórzy inni zrobili trochę przemysłu chałupniczego, próbując znaleźć właściwą abstrakcję dla FRP. Istnieje kilka implementacji koncepcji FRP w Haskell.

Możesz rozważyć rozpoczęcie od najnowszego Conala " Push-Pull Functional Reactive Programowanie " , ale istnieje kilka innych (starszych) implementacji, niektóre powiązane z haskell.org Strona . Conal ma talent do pokrywania całej domeny, a jego artykuł można przeczytać bez odniesienia do tego, co było wcześniej.

Aby poczuć, jak to podejście może być używane do rozwoju GUI, warto spojrzeć na Fudgets , który podczas gdy jest coraz trochę długi w zębie w dzisiejszych czasach, jest zaprojektowany w połowie lat 90-tych, prezentuje solidne podejście FRP do projektu GUI.

 68
Author: Edward Kmett,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-05-01 23:06:27

Windows Presentation Foundation jest dowodem na to, że podejście funkcjonalne działa bardzo dobrze w przypadku programowania GUI. Ma wiele aspektów funkcjonalnych i "dobry" kod WPF (search for MVVM pattern) kładzie nacisk na podejście funkcjonalne nad imperatywem. Mogę śmiało twierdzić, że WPF jest najbardziej udanym w świecie rzeczywistym funkcjonalnym zestawem narzędzi GUI: -)

WPF opisuje interfejs użytkownika w XAML (chociaż można go przepisać do funkcjonalnie wyglądającego C # lub F#), więc aby stworzyć jakiś interfejs użytkownika napisałbyś:

<!-- Declarative user interface in WPF and XAML --> 
<Canvas Background="Black">
   <Ellipse x:Name="greenEllipse" Width="75" Height="75" 
      Canvas.Left="0" Canvas.Top="0" Fill="LightGreen" />
</Canvas>

Co więcej, WPF pozwala również deklaratywnie opisywać animacje i reakcje na zdarzenia za pomocą innego zestawu deklaratywnych tagów (ponownie to samo można zapisać jako kod C#/F#):

<DoubleAnimation
   Storyboard.TargetName="greenEllipse" 
   Storyboard.TargetProperty="(Canvas.Left)"
   From="0.0" To="100.0" Duration="0:0:5" />

W rzeczywistości, myślę, że WPF ma wiele wspólnego z FRP Haskell (choć wierzę, że projektanci WPF nie wiedzieli o FRP i jest to trochę niefortunne-WPF czasami wydaje się trochę dziwne i niejasne, jeśli używasz funkcjonalnego punktu widzenia).

 60
Author: Tomas Petricek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-04-20 20:54:56

Powiedziałbym, że programowanie funkcyjne (F#) jest dużo lepszym narzędziem do programowania interfejsu użytkownika niż np. C#. Po prostu trzeba myśleć o problemie trochę inaczej.

Omawiam ten temat w mojej książce o programowaniu funkcyjnym w rozdziale 16, ale jest darmowy fragment, który pokazuje (IMHO) najciekawszy wzór, którego można użyć W F#. Powiedzieć, że chcesz zaimplementować rysowanie prostokątów (użytkownik naciska przycisk, przesuwa myszki i zwalnia przycisk). W F# możesz napisać coś takiego:

let rec drawingLoop(clr, from) = async { 
   // Wait for the first MouseMove occurrence 
   let! move = Async.AwaitObservable(form.MouseMove) 
   if (move.Button &&& MouseButtons.Left) = MouseButtons.Left then 
      // Refresh the window & continue looping 
      drawRectangle(clr, from, (move.X, move.Y)) 
      return! drawingLoop(clr, from) 
   else
      // Return the end position of rectangle 
      return (move.X, move.Y) } 

let waitingLoop() = async { 
   while true do
      // Wait until the user starts drawing next rectangle
      let! down = Async.AwaitObservable(form.MouseDown) 
      let downPos = (down.X, down.Y) 
      if (down.Button &&& MouseButtons.Left) = MouseButtons.Left then 
         // Wait for the end point of the rectangle
         let! upPos = drawingLoop(Color.IndianRed, downPos) 
         do printfn "Drawn rectangle (%A, %A)" downPos upPos }

Jest to bardzo imperatywne podejście (w zwyczajowym pragmatycznym stylu F#), ale pozwala uniknąć używania zmiennego stanu do przechowywania bieżącego stanu rysunku i 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 mój blog w ciągu najbliższych kilku dni.

Funkcjonalne Programowanie reaktywne jest bardziej funkcjonalne podejście, ale uważam, że jest nieco trudniejsze w użyciu, ponieważ opiera się na dość zaawansowanych funkcjach Haskella (takich jak strzałki). Jednak w dużej liczbie przypadków jest bardzo elegancki. Jego ograniczeniem jest to, że nie można łatwo zakodować maszyny stanowej (która jest użytecznym modelem mentalnym dla programów reaktywnych). Jest to bardzo proste przy użyciu powyższej techniki F#.

 26
Author: Tomas Petricek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-13 13:08:11

Niezależnie od tego, czy jesteś w hybrydowym języku funkcyjnym/oo, takim jak F# czy OCaml, czy w języku czysto funkcjonalnym, takim jak Haskell, w którym efekty uboczne są relegowane do monady IO, to głównie przypadek, że mnóstwo pracy wymaganej do zarządzania GUI jest znacznie bardziej "efektem ubocznym" niż czysto funkcjonalnym algorytmem.

To powiedziawszy, przeprowadzono naprawdę solidne badania w funkcjonalny GUI . Istnieją nawet niektóre (głównie) funkcjonalne Zestawy narzędzi, takie jak Fudgets lub FranTk .

 16
Author: sblom,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-04-20 05:48:49

Możesz sprawdzić serię Don Syme na F#, gdzie demo tworzy gui. poniższy link jest do trzeciej części serii (możesz link stamtąd do pozostałych dwóch części).

Używanie F # do tworzenia 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/

 15
Author: Kevin Won,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-04-20 06:05:08

Jednym z pomysłów otwierających umysł za funkcyjnym programowaniem reaktywnym jest funkcja obsługi zdarzeń produkująca zarówno reakcję na zdarzenia, jak i następną funkcję obsługi zdarzeń. W ten sposób rozwijający się system jest reprezentowany jako sekwencja funkcji obsługi zdarzeń.

Dla mnie nauka Yampy stała się kluczowym poingiem, aby właściwie uzyskać tę funkcję-produkującą-funkcje. Są fajne papiery o Yampie. Polecam Yampa Arcade:

Http://www.cs.nott.ac.uk/~nhn/rozmowy / HW2003-YampaArcade.pdf (slajdy, PDF) http://www.cs.nott.ac.uk/ ~ nhn/publikacje / Hw2003. pdf (cały artykuł, PDF)

Jest strona wiki na Yampa w Haskell.org

Http://www.haskell.org/haskellwiki/Yampa

Oryginalna Strona domowa Yampy:

Http://www.haskell.org/yampa (niestety w tej chwili jest zepsuty)

 12
Author: Boris Berkgaut,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-25 14:16:38

Rozmowę Elliota na FRP można znaleźć tutaj .

W dodatku, nie tak naprawdę odpowiedź, ale uwaga i kilka myśli: jakoś termin "funkcjonalny GUI" wydaje się trochę jak oksymoron(pureness i IO w tym samym terminie).

Ale moje niejasne zrozumienie jest to, że programowanie funkcjonalne GUI polega na deklaratywnym zdefiniowaniu funkcji zależnej od czasu, która pobiera (rzeczywisty)czas zależny od wejścia użytkownika i produkuje zależne od czasu wyjście GUI.

Innymi słowy, funkcja ta jest definiowana jak równanie różniczkowe deklaratywnie, a nie przez algorytm imperatywnie wykorzystujący stan zmienny.

Tak więc w konwencjonalnym FP używa się funkcji niezależnych od czasu, podczas gdy w FRP używa się funkcji zależnych od czasu jako bloków konstrukcyjnych do opisu programu.

Pomyślmy o symulowaniu piłki na sprężynce, z którą użytkownik może wchodzić w interakcje. Pozycja piłki jest wyjściem graficznym (na ekranie), użytkownik pchający piłkę jest naciśnięciem klawisza (wejściem).

Opisanie tego programu symulacyjnego w FRP (według mojego zrozumienia) 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 film na ELM , który ilustruje ten punkt widzenia.

 6
Author: jhegedus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-21 18:08:16

Odkąd to pytanie zostało zadane po raz pierwszy, programowanie funkcjonalne stało się bardziej powszechne przez Elm.

Proponuję sprawdzić na http://elm-lang.org , który ma również naprawdę doskonałe interaktywne samouczki, jak stworzyć w pełni funkcjonalny GUI w przeglądarce.

Pozwala na tworzenie w pełni funkcjonalnych GUI, w których kod, który musisz DOSTARCZYĆ, składa się tylko z czystych funkcji. Osobiście uważam, że dużo łatwiej się dostać niż różne Haskell GUI framework.

 6
Author: saolof,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-18 12:43:08

Od 2016 roku istnieje kilka innych, stosunkowo dojrzałych frameworków FRP dla Haskell, takich jak Sodium i Reflex (ale także Netwire).

The Manning book on Functional Reactive Programming prezentuje Java wersję Sodium, jako przykłady robocze, i ilustruje, jak zachowuje się baza kodu GUI FRP i skaluje się w porównaniu z imperatywem, a także podejściem opartym na aktorze.

Jest też niedawny artykuł na Arrowized FRP i perspektywa włączenia skutków ubocznych, IO i mutacja w ustawieniu zgodnym z prawem, czystym ustawieniu FRP: http://haskell.cs.yale.edu/wp-content/uploads/2015/10/dwc-yale-formatted-dissertation.pdf.

Warto również zauważyć, że JavaScript frameworków takich jak ReactJS i Angular i wiele innych albo już są lub idą w kierunku korzystania z FRP lub inaczej funkcjonalne podejście do osiągnięcia skalowalne i komponowalne komponenty GUI.

 5
Author: Erik Allik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-06 12:31:16

Języki znaczników, takie jak XUL, pozwalają zbudować GUI w deklaratywny sposób.

 4
Author: StackedCrooked,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-04-20 12:36:01

Aby się tym zająć zamieściłem kilka moich przemyśleń w using 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 zakończyć serię i pokazać, jak F # może przyczynić się do programowania UX.

Mówię tu tylko w kontekście F#.

-Fahad

 4
Author: Fahad,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-04-28 16:46:06

Wszystkie te odpowiedzi opierają się na programowaniu funkcjonalnym, ale podejmują wiele własnych decyzji projektowych. Jedną z bibliotek zbudowanych zasadniczo w całości z funkcji i prostych abstrakcyjnych typów danych jest gloss. Oto typ dla jego play Funkcja ze źródła

-- | Play a game in a window. Like `simulate`, but you manage your own input events.
play    :: Display              -- ^ Display mode.
        -> Color                -- ^ Background color.
        -> Int                  -- ^ Number of simulation steps to take for each second of real time.
        -> world                -- ^ The initial world.
        -> (world -> Picture)   -- ^ A function to convert the world a picture.
        -> (Event -> world -> world)    
                -- ^ A function to handle input events.
        -> (Float -> world -> world)
                -- ^ A function to step the world one iteration.
                --   It is passed the period of time (in seconds) needing to be advanced.
        -> IO ()

Jak widzisz, działa całkowicie poprzez dostarczanie czystych funkcji z prostymi abstrakcyjnymi typami, w których pomagają inne biblioteki.

 2
Author: PyRulez,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-01 01:05:55

Najbardziej widoczną innowacją zauważoną przez ludzi nowych w Haskell jest to, że istnieje oddzielenie między nieczystym światem, który zajmuje się komunikacją ze światem zewnętrznym, a czystym światem obliczeń i algorytmów. Częstym pytaniem dla początkujących jest "Jak mogę pozbyć się IO, tj. przekształcić IO a w a?"Sposobem na to jest użycie monad (lub innych abstrakcji) do pisania kodu, który wykonuje efekty IO i chains. Kod ten zbiera dane ze świata zewnętrznego, tworzy model tego, robi pewne obliczenia, ewentualnie stosując czysty kod, i wyprowadza wynik.

Jeśli chodzi o powyższy model, nie widzę nic strasznego złego w manipulowaniu Gui w IO monadzie. Największym problemem wynikającym z tego stylu jest to, że moduły nie są już komponowalne, tzn. tracę większość mojej wiedzy na temat globalnego porządku wykonywania poleceń w moim programie. Aby go odzyskać, muszę zastosować podobne rozumowanie jak w równoległym, imperatywnym GUI kod. Tymczasem dla nieczystego, nie-GUI kodu kolejność wykonania jest oczywista ze względu na definicję IO operatora >== monada (przynajmniej tak długo, jak jest 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ąć oceny skutkującej .

Największą filozoficzną różnicą między konsolowym a graficznym IO 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ń: strumień bajtów powszechnie nazywany stdin. Interfejsy graficzne są z natury asynchroniczne i muszą reagować na zdarzenia klawiatury i kliknięcia myszy.

[16]}popularna filozofia wykonywania asynchronicznych IO w sposób funkcjonalny nazywa się Functional Reactive Programming (FRP). Dzięki bibliotekom, takim jak ReactiveX, i frameworkom, zyskała ostatnio dużą popularność w nieczystych, niefunkcjonalnych językach takie jak Elm. W skrócie, to tak 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 operatorów, takich jak map, foldl, zip, filter, concat, join, itd., do produkcji nowych strumieni. Jest to przydatne, ponieważ sam stan programu może być postrzegany jako scanl . map reactToEvents $ zipN <eventStreams> programu, gdzie {[15] } jest równy liczbie obserwowalnych kiedykolwiek rozważanych przez program.

Praca z obiektami obserwacyjnymi FRP umożliwia odzyskanie kompostowalności, ponieważ zdarzenia w strumieniu są uporządkowane w czasie. Powodem jest to, że abstrakcja strumienia zdarzeń umożliwia wyświetlanie wszystkich obserwowalnych jako czarnych skrzynek. Ostatecznie połączenie strumieni zdarzeń za pomocą operatorów powoduje przywrócenie lokalnego porządku podczas realizacji. Zmusza mnie to do większej szczerości co do tego, na których niezmiennikach opiera się mój program, podobnie jak wszystkie funkcje w Haskell muszą być referencjalnie przejrzyste: jeśli chcę pobierać dane z innej części mojego programu, muszę być jawny ad zadeklarować odpowiedni typ dla moich funkcji. Monada IO, będąca specyficznym dla danej domeny językiem do pisania nieczystego kodu, skutecznie omija ten problem.]}

 1
Author: MauganRa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-18 21:09:11

Programowanie funkcyjne mogło ruszyć dalej z czasów, kiedy byłem na uniwersytecie, ale o ile pamiętam, głównym punktem systemu programowania funkcyjnego było powstrzymanie programisty tworzenia jakiegokolwiek "efektu ubocznego". Jednak użytkownicy kupują oprogramowanie ze względu na efekty uboczne, które są tworzone, np. aktualizacja interfejsu użytkownika.

 -19
Author: Ian Ringrose,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-10 15:43:30