From the Lukasz Stafiniak pages
Porównujemy języki na bazie OCamla: Lucid Synchrone http://www.lri.fr/~pouzet/lucid-synchrone/, Reactive ML http://rml.inria.fr/, JoCaml http://moscova.inria.fr/jocaml/, biblioteki dla OCamla Objective Caml Reactive Toolkit http://users.wpi.edu/~squirrel/ocamlrt/, moduł Event http://caml.inria.fr/pub/docs/manual-ocaml/libref/Event.html, oraz podejście obiektowe. Zbadamy też bliżej FrTime. Reactive Toolkit jest wzorowany na Yampa http://www.haskell.org/yampa/, oraz na FrTime dla PLT Scheme http://citeseer.ist.psu.edu/cooper04frtime.html.
Podstawowym typem są funkcje sygnałowe, dla danego wejścia obliczające wyjście oraz kontynuację (funkcję sygnałową w miejsce obliczanej, dla kolejnego kroku systemu). Obliczanie kontynuacji oznacza, że funkcja sygnałowa może ewoluować, posiada pamięć (stan). Z mniejszych funkcji sygnałowych składa się większe, tak że wyjścia jednych funkcji sygnałowych stają się wejściami innych. Obliczenia są synchroniczne: krok systemu to zaaplikowanie głównej funkcji sygnałowej do wejścia systemu, obliczenia “rozchodzą się top-down”. Zdarzenia (events) to wartości funkcji sygnałowych będące typu option
, wartość None
oznacza, że zdarzenie nie zaszło.
Podstawowym typem są sygnały: zdarzenia (events) i zachowania (behaviors): w rzeczywistości są to funkcje sygnałowe. System rejestruje sygnały “wynikowe” programu: punkty końcowe (endpoints) i uaktualnia się przez zaaplikowanie wszystkich (?) punktów końcowych do wejścia systemu. W tym sensie jest synchroniczny. Z drugiej strony, uaktualnienie następuje na życzenie bibliotek generujących wejścia systemu (wywołujących send_event
albo zmieniającą wartość danego zachowania set_cell
). Sygnały są memoizowane dla danej chwili “w punktach złożenia” w każdym sygnale złożonym. Zauważ, że gdyby sygnały nie były funkcyjne tylko imperatywne — gdyby modyfikowały się zamiast konstruować kontynuację — to zepsułoby semantykę Reactive Toolkit, bo sygnały robiłyby w jednej chwili tyle kroków, ile mają wystąpień (chyba, żeby memoizację przesunąć z zastosowania sygnału na sam sygnał).
Jest to w pełni asynchroniczna, imperatywna implementacja programowania reaktywnego. Cytując “FrTime: Functional Reactive Programming in PLT Scheme” (Gregory Cooper and Shriram Krishnamurthi):
To rozwiązanie przypomina Adaptive Functional Programming http://citeseer.ist.psu.edu/acar01adaptive.html.
Jest systemem synchronicznym. Podstawowymi obiektami są procesy, które komunikują się ze sobą przy pomocy nazwanych sygnałów. Procesy buduje się ze zwykłych wyrażeń OCamla, złożenia sekwencyjnego procesów (a; b
: najpierw wykonaj podproces a
, następnie b
), złożenia równoległego (współbieżnego) procesów (kończy się, gdy obydwa podprocesy się kończą), operacji “czekaj do następnej chwili (pause
)”, operacji “oczekuj na nadejście sygnału o danej nazwie” (nazwy są wartościami, można je przekazywać jako parametry), operacji “wykonuj podproces do nadejścia sygnału o danej nazwie”, operacji testującej obecność sygnału, etc. Sygnały jak zwykle przekazują wartości. Reactive ML ma bardzo ładną semantykę dzięki rozdzieleniu kroku systemu na dwie fazy: fazę obliczeń i generowania sygnałów oraz fazę odbierania sygnałów (“End of Instant’s Reaction”).
Jeśli w jednej chwili wyemitowano kilka wartości danego sygnału, to są one zwijane przez funkcję zadaną dla tego sygnału (gather
), domyślnie budującą multizbiór. W trakcie pierwszej fazy obliczeń procesy oczekujące na dany sygnał są ustawiane w kolejkę do tego sygnału, pozostałe procesy tworzą kolejkę procesów aktywnych. W drugiej fazie, procesy które odebrały sygnał są wstawiane do kolejki procesów aktywnych, i system przechodzi do następnego kroku, w którym oblicza procesy z tej kolejki.
Jest to system synchroniczny. Podstawowymi pojęciami Lucid Synchrone są strumień i zegar.
Zwykłe wartości OCamla są zamieniane na strumienie stałe, funkcje liczą na strumieniach “po osiach” (pointwise). Można dostawiać wartość z przodu strumienia, opóźniając ten strumień, np. s -. (0. fby s)
to strumień przyrostów strumienia s
. Proces ze stanem (np. strumień liczb naturalnych) można zrobić przez połączenie opóźnienia i rekurencji.
Każde wyrażenie ma oprócz zwykłego typu typ zegarowy, mówiący jak jego części są taktowane, np. jak taktowanie wyniku funkcji zależy od taktowań argumentów funkcji. Zegar to strumień boolowski, przetaktowujący (spowalniający) jakiś inny zegar: tyknięcia zegara są w chwilach true
strumienia (względem tego innego zegara). Rozłącznie taktowane strumienie można łączyć w gęściej taktowane. Jeśli nasz proces a
jest wolniejszy niż proces b
z którym się komunikujemy, to próbkujemy b
zegarem dla a
, jeśli b
jest wolniejszy, to budujemy strumień przytrzymujący ostatnią wartość b
do nadejścia następnej.
Sygnały (czyli zdarzenia w terminologii Yampa), to strumienie powstałe ze strumieni taktowanych innym zegarem: sygnał jest obecny, jeśli zegar oryginalnego strumienia ma tyknięcie, i wartością sygnału jest wartość z oryginalnego strumienia. Można dopasowywać wartości obecnych sygnałów na zasadzie dopasowywania wzorca: jeśli definiujemy strumień, to trzeba podać gałąź domyślną gdy nie ma żadnego z dopasowywanych sygnałów, a jeśli definiujemy sygnał, to przy braku dopasowania nie zostanie on wyemitowany.
Wzorowane na http://citeseer.ist.psu.edu/maraninchi98modeautomata.html. Ze stanami (trybami, modes) programu są związane równania określające wartości strumieni (lokalnych i wyjściowych strumieni automatu), taka definicja przez przypadki. Stany zmieniają się zgodnie z funkcją przejścia z wartownikami (warunkami na strumieniach, które muszą być spełnione, żeby przejść do danego stanu).
Copyright © 2005–2006 the Main wiki and its authors
Retrieved from http://ii.uni.wroc.pl/~lukstafi/pmwiki/index.php?n=ProgFun.Reaktywne
Page last modified on January 09, 2007, at 01:27 AM