Jaki jest stan bieżących wdrożeń programowania funkcjonalnego reaktywnego?


90

Próbuję wyobrazić sobie kilka prostych, automatycznych układów fizycznych (takich jak wahadło, ramiona robotów itp.) W Haskell. Często systemy te można opisać równaniami takimi jak

df/dt = c*f(t) + u(t)

gdzie u(t)reprezentuje rodzaj „inteligentnej kontroli”. Systemy te bardzo dobrze pasują do paradygmatu programowania funkcjonalnego reaktywnego.

Więc złapałem książkę „Haskell Szkołę wyrażenie” przez Paula Hudák, a okazało się, że domena język „FAL” (dla Functional Animacja Język) przedstawił tam faktycznie działa całkiem przyjemnie dla moich prostych systemów zabawki (choć niektórych funkcji, zwłaszcza integrate, wydawał się nieco zbyt leniwy, aby można go było efektywnie wykorzystać, ale łatwo go naprawić).

Moje pytanie brzmi: jaka jest obecnie bardziej dojrzała, aktualna, dobrze utrzymana, dostrojona pod kątem wydajności alternatywa dla bardziej zaawansowanych, a nawet praktycznych zastosowań?

Ta strona wiki zawiera kilka opcji dla Haskell, ale nie jestem pewien co do następujących aspektów:

  1. Status „reaktywny”, projekt Conala Eliotta, który jest (jak rozumiem) jednym z wynalazców tego paradygmatu programowania, wygląda na nieco przestarzały. Uwielbiam jego kod, ale może powinienem wypróbować inne, bardziej aktualne alternatywy? Jaka jest główna różnica między nimi pod względem składni / wydajności / stabilności czasu wykonywania?

  2. Cytując z ankiety przeprowadzonej w 2011 r., Sekcja 6: „ … implementacje FRP nadal nie są wystarczająco wydajne lub przewidywalne pod względem wydajności, aby można je było skutecznie wykorzystać w domenach, które wymagają gwarancji opóźnienia… ”. Chociaż ankieta sugeruje kilka interesujących możliwych optymalizacji, biorąc pod uwagę fakt, że FRP istnieje od ponad 15 lat, mam wrażenie, że ten problem z wydajnością może być czymś bardzo lub nawet z natury trudnym do rozwiązania przynajmniej w ciągu kilku lat. Czy to prawda?

  3. Ten sam autor ankiety na swoim blogu mówi o „wyciekach czasu” . Czy problem dotyczy wyłącznie FRP, czy jest to coś, z czym mamy do czynienia podczas programowania w czystym, nieostrym języku? Czy kiedykolwiek okazało się, że ustabilizowanie systemu opartego na FRP w czasie było zbyt trudne, jeśli nie było wystarczająco wydajne?

  4. Czy to nadal projekt na poziomie badawczym? Czy ludzie tacy jak inżynierowie fabryk, inżynierowie robotyki, inżynierowie finansowi itp. Faktycznie ich używają (w jakimkolwiek języku, który odpowiada ich potrzebom)?

Chociaż osobiście wolę implementację Haskell, jestem otwarty na inne sugestie. Na przykład, byłoby szczególnie przyjemnie mieć implementację Erlanga - wówczas bardzo łatwo byłoby mieć inteligentny, adaptacyjny, samouczący się proces serwera!

Odpowiedzi:


82

Obecnie istnieją głównie dwie praktyczne biblioteki Haskell do funkcjonalnego programowania reaktywnego. Oba są obsługiwane przez pojedyncze osoby, ale otrzymują również wkład w postaci kodu od innych programistów Haskell:

  • Netwire stawia na wydajność, elastyczność i przewidywalność. Ma swój własny paradygmat zdarzeń i może być używany w obszarach, w których tradycyjne FRP nie działa, w tym usługi sieciowe i złożone symulacje. Styl: aplikacyjny i / lub strzałkowy. Pierwszy autor i opiekun: Ertugrul Söylemez (to ja).

  • reactive-banana opiera się na tradycyjnym paradygmacie FRP. Chociaż jest to praktyczne w użyciu, służy również jako podstawa do klasycznych badań nad FRP. Skupia się głównie na interfejsach użytkownika i jest gotowy interfejs do wx. Styl: aplikacyjny. Pierwszy autor i opiekun: Heinrich Apfelmus.

Powinieneś wypróbować oba z nich, ale w zależności od zastosowania prawdopodobnie okaże się, że jeden lub drugi będzie lepiej pasował.

Netwire okaże się przydatny do gier, sieci, sterowania robotami i symulacji. Jest dostarczany z gotowymi przewodami dla tych aplikacji, w tym różnymi przydatnymi różnicami, całkami i wieloma funkcjami do przejrzystej obsługi zdarzeń. Aby zapoznać się z samouczkiem, odwiedź dokumentację Control.Wiremodułu na podlinkowanej stronie.

W przypadku graficznych interfejsów użytkownika obecnie najlepszym wyborem jest reaktywny banan. Ma już interfejs wx (jako osobna biblioteka reactive-banana-wx), a Heinrich dużo bloguje o FRP w tym kontekście, w tym próbki kodu.

Aby odpowiedzieć na inne pytania: FRP nie jest odpowiedni w scenariuszach, w których potrzebujesz przewidywalności w czasie rzeczywistym. Wynika to w dużej mierze z powodu Haskella, ale niestety FRP jest trudny do zrealizowania w językach niższego poziomu. Gdy tylko sam Haskell stanie się gotowy do pracy w czasie rzeczywistym, FRP też tam dotrze. Koncepcyjnie Netwire jest gotowy do aplikacji czasu rzeczywistego.

Przecieki czasu nie stanowią już problemu, ponieważ są w dużej mierze związane z ramami monadycznymi. Praktyczne implementacje FRP po prostu nie oferują monadycznego interfejsu. Yampa rozpoczął to, a Netwire i reactive-banana opierają się na tym.

Nie znam żadnych komercyjnych lub innych projektów na dużą skalę wykorzystujących FRP w tej chwili. Biblioteki są gotowe, ale myślę, że ludzie nie - jeszcze.


Świetna odpowiedź, dziękuję ... niezłą zabawą będzie zaimplementowanie algorytmów uczenia się przez wzmocnienie w bibliotece.
mnish

3
Warto zauważyć, że ostatnie indie gra napisana w Haskell ( Nikki i robotów ) podjął decyzję, nie korzystania z FRP.
Alex R

23

Chociaż jest już kilka dobrych odpowiedzi, spróbuję odpowiedzieć na Twoje konkretne pytania.

  1. reactive nie nadaje się do użytku w poważnych projektach z powodu problemów z upływem czasu. (patrz nr 3). Obecna biblioteka o najbardziej podobnym projekcie to reaktywny banan, który został opracowany z inspiracji reaktywnością i w dyskusji z Conalem Elliottem.

  2. Chociaż sam Haskell jest nieodpowiedni dla trudnych aplikacji czasu rzeczywistego, w niektórych przypadkach można go używać do miękkich aplikacji czasu rzeczywistego. Nie znam aktualnych badań, ale nie wierzę, że jest to problem nie do pokonania. Podejrzewam, że systemy takie jak Yampa lub systemy generowania kodu, takie jak Atom, są prawdopodobnie najlepszym podejściem do rozwiązania tego problemu.

  3. „Przeciek czasu” to problem charakterystyczny dla przełączalnego FRP. Przeciek występuje, gdy system nie jest w stanie uwolnić starych obiektów, ponieważ może ich potrzebować, gdyby w pewnym momencie w przyszłości nastąpiła zmiana. Oprócz wycieku pamięci (który może być dość poważny), inną konsekwencją jest to, że gdy nastąpi przełączenie, system musi wstrzymać działanie, podczas gdy przechodzi łańcuch starych obiektów, aby wygenerować bieżący stan.

Niezłączalne biblioteki frp, takie jak Yampa i starsze wersje reactive-banana, nie cierpią z powodu wycieków czasu. Przełączalne biblioteki frp zazwyczaj stosują jeden z dwóch schematów: albo mają specjalną „monadę tworzenia”, w której tworzone są wartości FRP, albo używają parametru typu „starzenie się”, aby ograniczyć konteksty, w których mogą wystąpić przełączniki. elerea (i prawdopodobnie netwire?) używają tego pierwszego, podczas gdy ostatnie reaktywne banany i grejpfruty używają drugiego.

Przez „przełączalne frp” rozumiem taki, który implementuje funkcję Conala switcher :: Behavior a -> Event (Behavior a) -> Behavior alub identyczną semantykę. Oznacza to, że kształt sieci może się dynamicznie zmieniać w trakcie jej działania.

Nie jest to tak naprawdę sprzeczne ze stwierdzeniem @ ertes dotyczącym interfejsów monadycznych: okazuje się, że dostarczenie Monadinstancji a Eventumożliwia upływ czasu, a przy żadnym z powyższych podejść nie jest już możliwe zdefiniowanie równoważnych instancji Monady.

Wreszcie, chociaż wciąż pozostaje wiele pracy do wykonania z FRP, myślę, że niektóre z nowszych platform (reactive-banana, elerea, netwire) są wystarczająco stabilne i dojrzałe, aby można było z nich zbudować niezawodny kod. Ale być może będziesz musiał spędzić dużo czasu na poznawaniu tajników, aby zrozumieć, jak uzyskać dobrą wydajność.


2
Jeśli chodzi o biblioteki oparte na strzałkach (Yampa, netwire), są one również przełączalne. Powodem jest to, że strzały mają wbudowane starzenie się, nie można się go pozbyć. (Będąc transformatorami strumieniowymi, strzały są agnostykami co do czasu rozpoczęcia ich strumienia wejściowego.)
Heinrich Apfelmus


1
@HeinrichApfelmus: to interesujący punkt. Generalnie nie uważam bibliotek opartych na strzałkach za przełączalne w taki sam sposób, jak elerea / grapefruit / current-reactive-banana. Myślę, że ich zmiana jest dużo bliższa temu, co było wymagane w poprzednich wersjach reaktywnego banana. To tylko przeczucie, nie zastanawiałem się nad tym wystarczająco, aby opisać, co mam na myśli.
John L

2
@DanBurton dzięki, bezskutecznie próbowałem zapamiętać to imię. Zgadzam się, że sód należy uważać za nowoczesną bibliotekę FRP, chociaż nie jest tak popularny jak reaktywny banan.
John L

Chociaż tocząca się dyskusja jest nieco trudna do prześledzenia, wydaje się wskazywać, że system miękkiego czasu rzeczywistego jest rzeczywiście możliwy, pod warunkiem, że czas GC można w jakiś sposób ograniczyć. W każdym razie dziękuję za świetną odpowiedź.
mnish

20

Zamierzam wymienić kilka elementów z przestrzeni Mono i .Net oraz jedną z przestrzeni Haskell, którą znalazłem nie tak dawno temu. Zacznę od Haskella.

Wiąz - link

Jego opis na swojej stronie:

Elm ma na celu uczynienie tworzenia stron WWW bardziej przyjemnymi. Wprowadza nowe podejście do programowania GUI, które rozwiązuje systemowe problemy HTML, CSS i JavaScript. Elm pozwala szybko i łatwo pracować z układem wizualnym, korzystać z kanwy, zarządzać skomplikowanymi danymi wejściowymi użytkownika i uciec z piekła wywołań zwrotnych.

Ma swój własny wariant FRP . Zabawa przykładami wydaje się całkiem dojrzała.

Rozszerzenia reaktywne - link

Opis z pierwszej strony:

Rozszerzenia reaktywne (Rx) to biblioteka do tworzenia programów asynchronicznych i opartych na zdarzeniach przy użyciu obserwowalnych sekwencji i operatorów zapytań w stylu LINQ. Korzystając z Rx, deweloperzy reprezentują asynchroniczne strumienie danych za pomocą Observables, wykonują zapytania o asynchroniczne strumienie danych przy użyciu operatorów LINQ i parametryzują współbieżność w asynchronicznych strumieniach danych przy użyciu harmonogramów. Mówiąc najprościej, Rx = Observables + LINQ + Schedulers.

Reactive Extensions pochodzi z MSFT i implementuje wiele doskonałych operatorów, które upraszczają obsługę zdarzeń. To było open source zaledwie kilka dni temu. Jest bardzo dojrzały i używany w produkcji; moim zdaniem byłby to lepszy interfejs API dla Windows 8 API niż zapewnia biblioteka TPL; ponieważ obserwowalne mogą być zarówno gorące, jak i zimne, ponawiane / scalane itp., Podczas gdy zadania zawsze reprezentują gorące lub wykonane obliczenia, które są uruchomione, błędne lub zakończone.

Napisałem kod po stronie serwera przy użyciu Rx dla asynchroniczności, ale muszę przyznać, że pisanie funkcjonalne w C # może być nieco denerwujące. F # ma kilka otok, ale trudno było śledzić rozwój interfejsu API, ponieważ grupa jest stosunkowo zamknięta i nie jest promowana przez MSFT, tak jak inne projekty.

Jego otwarte źródło pochodziło z otwartym źródłem jego kompilatora IL-to-JS, więc prawdopodobnie może dobrze działać z JavaScript lub Elm.

Prawdopodobnie możesz bardzo ładnie powiązać F # / C # / JS / Haskell za pomocą brokera komunikatów, takiego jak RabbitMQ i SocksJS.

Bling UI Toolkit - link

Opis z pierwszej strony:

Bling to oparta na języku C # biblioteka do łatwego programowania obrazów, animacji, interakcji i wizualizacji na platformie Microsoft WPF / .NET. Bling jest skierowany do technologów projektowania, tj. Projektantów, którzy czasami programują, aby pomóc w szybkim prototypowaniu bogatych pomysłów projektowych UI. Studenci, artyści, naukowcy i hobbystów również uznają Bling za przydatne jako narzędzie do szybkiego wyrażania pomysłów lub wizualizacji. Interfejsy API i konstrukcje Blinga są zoptymalizowane pod kątem szybkiego programowania wyrzucanego kodu, w przeciwieństwie do starannego programowania kodu produkcyjnego.

Bezpłatny artykuł LtU .

Przetestowałem to, ale nie pracowałem z tym dla projektu klienta. Wygląda niesamowicie, ma ładne przeciążenie operatorów C #, które tworzą powiązania między wartościami. Używa właściwości zależności w WPF / SL / (WinRT) jako źródeł zdarzeń. Animacje 3D działają dobrze na rozsądnym sprzęcie. Użyłbym tego, jeśli skończę na projekcie wymagającym wizualizacji; prawdopodobnie przenosząc go na Windows 8.

ReactiveUI - link

Paul Betts, wcześniej w MSFT, a teraz w Github, napisał ten framework. Pracowałem z nim dość intensywnie i podoba mi się model. Jest bardziej odsprzężony niż Blink (z natury od używania Rx i jego abstrakcji) - co ułatwia testowanie kodu jednostkowego za jego pomocą. W tym jest napisany klient gitub dla systemu Windows.

Komentarze

Model reaktywny jest wystarczająco wydajny dla większości aplikacji wymagających dużej wydajności. Jeśli myślisz o trudnym czasie rzeczywistym, założę się, że większość języków GC ma problemy. Rx, ReactiveUI tworzą pewną ilość małych obiektów, które muszą być poddane GC, ponieważ w ten sposób tworzone / usuwane są subskrypcje, a wartości pośrednie są progresywne w reaktywnej „monadzie” wywołań zwrotnych. Ogólnie w .Net wolę programowanie reaktywne niż programowanie oparte na zadaniach, ponieważ wywołania zwrotne są statyczne (znane w czasie kompilacji, bez alokacji), podczas gdy zadania są przydzielane dynamicznie (nieznane, wszystkie wywołania wymagają instancji, tworzone są śmieci) - a lambdy kompilują się do klasy generowane przez kompilator.

Oczywiście C # i F # są ściśle oceniane, więc przeciek czasu nie stanowi tutaj problemu. To samo dotyczy JS. Może to być jednak problem z odtwarzalnymi lub buforowanymi obserwowalnymi.


Dzięki za świetną odpowiedź. Jedną z rzeczy, które podobały mi się w implementacjach Haskell FRP, jest to, że wydają mi się one pozwalać mi na czyste oddzielenie obliczeń dla sterowania u(t)i symulacji dla f(t). Czy tak jest w przypadku implementacji języka F #?
mnish

Myślę, że można powiedzieć, że te dwie funkcje są tymczasowo odsprzężone, tak. Prawdopodobnie nie są one jednak logicznie oddzielone. ;)
Henrik

O ile mi wiadomo, Reactive Extensions i inne bardziej dopracowane pakiety skoncentrowane na interfejsie użytkownika (i wszystko poza Haskell, w rzeczywistości) używają tylko semaniki zdarzeniowej - co oznacza, że ​​mają pojęcie zdarzeń, które można włączyć, ale nie pojęcie ciągłych sygnałów czasowych, które mogą oddziaływać równorzędnie. Myślę, że w przypadku tworzenia GUI jest to w porządku. Jednak w przypadku tworzenia symulacji i modeli może to być niefortunne.
sclv

Czy sugerujesz, że wszystkie implementacje bibliotek funkcjonalnego programowania reaktywnego muszą modelować czas w sposób ciągły, a nie dyskretny? Znalazłem artykuł zatytułowany „Algebra procesów z synchronizacją: czas rzeczywisty i czas dyskretny” - czy to dobry punkt wyjścia do zrozumienia, o czym mówisz?
Henrik

Nie mówię, że wszyscy muszą - niektórzy tak, niektórzy nie. Ale te, które mają, są bardziej odpowiednie do pewnych zadań, a te, które nie są, bardziej odpowiednie dla innych ...
sclv
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.