Co oznacza „nie komponuje”?

35

Widzę wiele tekstów, zwłaszcza funkcjonalnych tekstów programistycznych, które twierdzą, że niektóre koncepcje CS „nie komponują” . Przykładami są: zamki nie komponują, monady nie komponują.

Trudno mi było wyśledzić dokładnie to zdanie. Kiedy myślę o kompozycji, myślę o kompozycji funkcji lub agregacji obiektów (jak w „faworyzowaniu kompozycji nad dziedziczeniem”), ale wydaje się, że nie w tym sensie ludzie używają jej tutaj.

Czy ktoś może wyjaśnić, co oznacza to wyrażenie, gdy jest używane w wyrażeniach takich jak dwa powyższe przykłady (tj. Zamki i monady)?

Rachunek
źródło
Ma to bardziej znaczenie dla kompozycji funkcji niż dla agregacji obiektów.
Andres F.,
Z grubsza i nieformalnie, jeśli masz dwie oddzielne rzeczy, które używają zamków, trudno je połączyć. (w przypadku zamków trudno jest obejść się bez wprowadzenia zakleszczeń; w przypadku monad typy mogą się skomplikować)
user253751

Odpowiedzi:

35

Kiedy ludzie mówią, że „X nie komponuje”, to co rozumieją przez „komponować” tak naprawdę oznacza po prostu „razem”, a co i jak je łączysz, może być bardzo różne, w zależności od tego, czym dokładnie jest „X”.

Ponadto, gdy mówią „nie komponuje”, mogą oznaczać nieco inne rzeczy:

  1. Nie możesz w ogóle połączyć dwóch X, kropka.
  2. Państwo może umieścić dwa Xs razem, ale wynik może nie być X (IOW: X nie jest zamknięty pod kompozycją ).
  3. Możesz umieścić dwa X razem, ale wynikowy X może nie działać w oczekiwany sposób.

Przykładem # 1 są parsery ze skanerami / leksykonami. Możesz usłyszeć wyrażenie „skanery / leksykony nie tworzą”. To nie jest prawda. Chodzi im o to, że „parser, który używa osobnego etapu leksykalnego, nie tworzy”.

Dlaczego chcesz komponować parsery? Wyobraź sobie, że jesteś dostawcą IDE, takim jak JetBrains, Eclipse Foundation, Microsoft lub Embarcadero, i chcesz zbudować IDE dla środowiska sieciowego. W typowym tworzeniu stron internetowych często mieszamy języki. Masz pliki HTML z <script>elementami zawierającymi ECMAScript i<style>elementy zawierające CSS. Masz pliki szablonów zawierające HTML, język programowania i metasyntax języka szablonów. Nie chcesz pisać różnych wyróżników składni dla „Python”, „Python osadzony w szablonie”, „CSS”, „CSS w HTML”, „ECMASCript”, „ECMAScript w HTML”, „HTML”, „HTML w” szablon ”itd. itd. Chcesz napisać wyróżnienie składni dla Pythona, jedno dla HTML, jedno dla języka szablonów, a następnie skomponować trzy w wyróżnienie składni dla pliku szablonu.

Jednak lexer analizuje cały plik w strumień tokenów, co ma sens tylko w tym jednym języku. Analizator składni dla drugiego języka nie może współpracować z tokenami, które przekazuje mu leksykon. Na przykład, parsery Pythona są zwykle pisane w taki sposób, że leksykator śledzi wcięcia i wstrzykuje fałszywe INDENTi DEDENTtokeny do strumienia tokenu, dzięki czemu analizator składni może być pozbawiony kontekstu, nawet jeśli składnia Pythona tak naprawdę nie jest. Jednak leksykon HTML całkowicie ignoruje białe znaki, ponieważ nie ma to znaczenia w HTML.

Jednak parser bez skanera, który po prostu odczytuje znaki, może przekazywać strumień znaków do innego parsera, który może następnie oddać go z powrotem, co znacznie ułatwia komponowanie.

Przykładem # 2 są ciągi znaków z zapytaniami SQL. Możesz mieć dwa ciągi, z których każdy zawiera poprawne składniowo zapytanie SQL, ale jeśli połączysz oba ciągi, wynik może nie być poprawnym składniowo zapytaniem SQL. Właśnie dlatego mamy takie algebry zapytań ARel, które się komponują.

Zamki są przykładem nr 3. Jeśli masz dwa programy z blokadami i łączysz je w jeden program, nadal masz program z blokadami, ale nawet jeśli dwa oryginalne programy były całkowicie poprawne, wolne od zakleszczeń i wyścigów, wynikowy program niekoniecznie ma to własność. Prawidłowe użycie blokad jest globalną właściwością całego programu i właściwością, która nie jest zachowywana podczas tworzenia programów. Różni się to na przykład od transakcji, które się składają. Program, który prawidłowo wykorzystuje transakcje, może zostać skomponowany z innym takim programem i da połączony program, który poprawnie wykorzystuje transakcje.

Jörg W Mittag
źródło
1
Innymi słowy „nie komponuje” oznacza „nie tworzy półgrupy” (lub nieco mocniej monoidu).
Jon Purdy,
Nie jestem pewien, czy wymagana jest asocjacja, więc bardziej przypomina magmę (o której dowiedziałem się dosłownie 3 sekundy temu :-D)
Jörg W Mittag
Jeśli jednak zapytasz kogoś, co mają na myśli, mówiąc „zamki się nie komponują”, jestem prawie pewien, że nie odpowiedzą: „Mam na myśli to, że zestaw poprawnych programów współbieżnych z zamkami i operacja kompozycji tworzą magmę” .
Jörg W Mittag,
Hah, oczywiście że nie. Tylko alternatywne sformułowanie. Mam wrażenie, że do utrzymania „płaskiego” poczucia kompozycji potrzebna jest asocjatywność, ale nie ma mocnego argumentu na poparcie tego.
Jon Purdy,
20

Kompatybilność oznacza, że ​​możesz łatwo i niezawodnie łączyć ze sobą komponenty programu, aby tworzyć większe komponenty i bardziej złożone funkcje.

Niektóre rzeczy, które pomagają w komponowaniu komponentów:

  1. Idempotencja. Funkcja idempotentna zawsze wywoła ten sam efekt wyjściowy lub skutki uboczne, jeśli zostanie wywołana wiele razy z tymi samymi wartościami parametrów. Poprawia to kompozycyjność, ponieważ wynik wywołania funkcji jest przewidywalny.

  2. Referencyjna przejrzystość. Referencyjnie przejrzyste wyrażenie zawsze będzie miało taki sam wynik. Poprawia to kompozycyjność, umożliwiając zastępowanie identycznych wyrażeń oraz umożliwiając obliczanie wyrażeń niezależnie od siebie (tj. W różnych wątkach) bez użycia blokad.

  3. Niezmienność. Nie można zmienić stanu obiektu niezmiennego po jego utworzeniu. Poprawia to kompozycyjność, ponieważ można polegać na stabilnej wartości obiektu, nie martwiąc się, że jakaś funkcja lub obiekt gdzieś zmienił stan obiektu po jego utworzeniu.

  4. Czystość. Czyste funkcje nie mają skutków ubocznych. Mają tylko dane wejściowe i wyjściowe, co czyni je bardziej kompozycyjnymi, ponieważ można umieścić dane wyjściowe jednej funkcji na wejściu innej funkcji, nie martwiąc się o to, czy coś poza funkcją się zmieniło.

Zamki nie komponują się, ponieważ są elementem zewnętrznym, na którym musisz polegać, łącząc dwie operacje, które mają taki sam stan, i z różnych powodów związanych ze złożonością związaną z używaniem zamków.

Wyrażenie „monady nie komponują” nie ma dla mnie większego sensu. Cały sens monady polega na przyjęciu pewnych stanowych rzeczy, takich jak wprowadzanie z klawiatury lub wyświetlanie ekranu, i przekształcenie ich w czystszą, matematyczną formę, która w rzeczywistości jest bardziej złożona.

Robert Harvey
źródło
4
Myślę, że stackoverflow.com/questions/7040844/... wykonuje dobrą robotę, wyjaśniając, co prawdopodobnie oznacza „monady się nie komponują”, chociaż zgadzam się, że monady są znacznie łatwiejsze do skomponowania niż zamki.
Ixrec
Twoje punkty za „referencyjną przejrzystość” i „czystość” są w rzeczywistości dwoma wymogami dotyczącymi czystości . Należy unikać terminu „przejrzystość referencyjna”, ponieważ nie jest dobrze zdefiniowany .
BlueRaja - Danny Pflughoeft
1
Monada to struktura algebraiczna z pewnymi operacjami. W ostatnim akapicie opisałeś IOtyp, który przechwytuje „stanowe działania”, takie jak wprowadzanie z klawiatury. Wartości tego IOtypu rzeczywiście się komponują, ale to cały typ IO jest monadą. Ten typ nie komponuje się z innymi typami, które są monadami, jak powiedzmy typ listy. Oznacza to, że nie możemy systematycznie produkować typu, IO . Listktóry zachowuje się zarówno jednocześnie, jakIO i Listjednocześnie. Czy to ma sens? Nie jestem pewien, czy dobrze to wytłumaczyłem.
Tikhon Jelvis,