Czy programowanie funkcjonalne ignoruje korzyści płynące z „O kryteriach, które należy zastosować przy rozkładaniu systemów na moduły” (ukrywanie danych)?

27

Jest klasyczny artykuł zatytułowany „Kryteria do zastosowania w rozkładaniu systemów na moduły”, który właśnie przeczytałem po raz pierwszy. Ma to dla mnie idealny sens i jest prawdopodobnie jednym z tych artykułów, na których oparto OOP. Wniosek:

Na podstawie tych przykładów próbowaliśmy wykazać, że prawie zawsze niewłaściwe jest rozpoczęcie rozkładu systemu na moduły na podstawie schematu blokowego. ... Każdy moduł jest następnie zaprojektowany tak, aby ukryć taką decyzję przed innymi

W mojej niewykształconej i niedoświadczonej opinii programowanie funkcjonalne przyjmuje dokładnie przeciwną radę z tego artykułu. Rozumiem, że programowanie funkcjonalne sprawia, że ​​przepływ danych jest idiomatyczny. Dane są przekazywane z funkcji do funkcji, przy czym każda funkcja jest ściśle świadoma danych i „zmienia je” po drodze. I myślę, że widziałem przemówienie Richa Hickeya, w którym opowiada o tym, jak ukrywanie danych jest przereklamowane, niepotrzebne lub coś w tym rodzaju, ale nie pamiętam na pewno.

  1. Najpierw chcę wiedzieć, czy moja ocena jest poprawna. Czy paradygmat FP i ten artykuł filozoficznie się nie zgadzają?
  2. Zakładając, że się nie zgadzają, w jaki sposób FP „kompensuje” brak ukrywania danych? Być może poświęcają ukrywanie danych, ale zyskują X, Y i Z. Chciałbym poznać powód, dla którego X, Y i Z są uważane za bardziej korzystne niż ukrywanie danych.
  3. Lub, zakładając, że się nie zgadzają, być może FP uważa, że ​​ukrywanie danych jest złe. Jeśli tak, dlaczego uważa, że ​​ukrywanie danych jest złe?
  4. Zakładając, że się zgadzają, chciałbym wiedzieć, czym jest wdrożenie ukrywania danych przez FP. Widać to wyraźnie w OOP. Możesz mieć privatepole, do którego nikt poza klasą nie ma dostępu. W FP nie ma dla mnie oczywistej analogii.
  5. Czuję, że są inne pytania, które powinienem zadać, ale nie wiem, czy powinienem zadawać. Możesz też na nie odpowiedzieć.

Aktualizacja

Znalazłem to przemówienie Neala Forda, które ma bardzo odpowiedni slajd. Zamieszczę zrzut ekranu tutaj:

wprowadź opis zdjęcia tutaj

Daniel Kaplan
źródło
1
Nie mogę odpowiedzieć na pełne pytanie, ale jak w przypadku (4), istnieją systemy modułowe w niektórych językach FP, które mogą zapewnić enkapsulację.
Andres F.,
@AndresF. o tak, to prawda. Zapomniałem, że Haskell ma moduły i można w nich ukryć typy danych i funkcje. Może kiedy mówię FP, naprawdę mówię Clojure. Możesz mieć prywatne funkcje i „pola” w Clojure, ale wydaje mi się, że idiomatyczne jest, aby twoje dane były widoczne dla czegokolwiek i przekazywane gdziekolwiek.
Daniel Kaplan
6
Często robisz, aby twoje typy były widoczne, ale ukrywasz konstruktorów. Te abstrakcyjne typy są szczególnie dobrze wykonywane przez system modułów OCaml
Daniel Gratzer
6
W języku podobnym do ML brak dostępu do konstruktorów oznacza, że ​​nie można dopasować wzorca wartości tego typu, aby go zdekonstruować. Jedyne, co możesz zrobić z tymi wartościami, to przekazać je do dowolnej udostępnionej funkcji. Jest to ten sam rodzaj abstrakcji danych, jak w powiedzmy C, który nie ma pierwszorzędnych pojęć dotyczących tego, co publiczne lub prywatne.
Luc Danton
1
@ SK-logic: Z punktu widzenia „problemu z wyrażeniem” ujawnianie danych jest dobre, gdy chcesz rozszerzyć je o nową funkcję w przyszłości (i nie przeszkadza ci utrzymanie danych), a ukrywanie danych jest dobre, kiedy chcesz rozszerzyć o nowe typy danych w przyszłości (kosztem utrzymania interfejsu funkcjonalnego na stałym poziomie)
hugomg

Odpowiedzi:

23

Wspomniany artykuł dotyczy ogólnie modułowości i miałby zastosowanie w równym stopniu do programów ustrukturyzowanych, funkcjonalnych i obiektowych. Słyszałem o tym artykule wcześniej od kogoś, kto był dużym facetem od OOP, ale przeczytałem go jako artykuł o programowaniu w ogóle, a nie czymś specyficznym dla OOP. Znany jest artykuł o programowaniu funkcjonalnym, Dlaczego programowanie funkcjonalne ma znaczenie , a pierwsze zdanie z konkluzji stwierdza: „W tym artykule argumentowaliśmy, że modułowość jest kluczem do udanego programowania”. Tak więc odpowiedź na (1) brzmi „nie”.

Dobrze zaprojektowane funkcje nie zakładają więcej o swoich danych niż muszą, więc część o „ścisłej świadomości danych” jest błędna. (Lub przynajmniej tak źle, jak byłoby w przypadku OOP. Nie można programować ściśle na wysokim poziomie abstrakcji i zignorować wszystkie szczegóły na zawsze w dowolnym paradygmacie. W końcu pewna część programu musi naprawdę wiedzieć o konkretne szczegóły danych).

Ukrywanie danych jest terminem specyficznym dla OOP i nie jest dokładnie takie samo, jak ukrywanie informacji omówione w artykule. Informacje ukryte w artykule dotyczą decyzji projektowych, które były trudne do podjęcia lub mogą ulec zmianie. Nie każda decyzja projektowa dotycząca formatu danych jest trudna lub może ulec zmianie i nie każda decyzja, która jest trudna lub może ulec zmianie, dotyczy formatu danych. Osobiście nie rozumiem, dlaczego programiści OO chcą, aby wszystko było przedmiotem. Czasami wystarczy prosta struktura danych.

Edycja: Znalazłem odpowiedni cytat z wywiadu z Richem Hickeyem .

Fogus: Zgodnie z tym pomysłem - niektórzy ludzie są zaskoczeni faktem, że Clojure nie angażuje się w enkapsulację ukrywania danych na swoich typach. Dlaczego zdecydowałeś się zrezygnować z ukrywania danych?

Hickey: Wyjaśnijmy, że Clojure zdecydowanie kładzie nacisk na programowanie na abstrakcje. W pewnym momencie ktoś będzie musiał mieć dostęp do danych. A jeśli masz pojęcie „prywatny”, potrzebujesz odpowiednich pojęć przywileju i zaufania. A to dodaje całej tony złożoności i małej wartości, stwarza sztywność w systemie i często zmusza rzeczy do życia w miejscach, w których nie powinny. Jest to dodatek do innych strat, które występują, gdy proste informacje są umieszczane w klasach. W zakresie, w jakim dane są niezmienne, istnieje niewiele szkód, które mogą wyniknąć z zapewnienia dostępu, poza tym, że ktoś może polegać na czymś, co może się zmienić. No dobrze, ludzie robią to przez cały czas w prawdziwym życiu, a kiedy wszystko się zmienia, dostosowują się. A jeśli są racjonalni, wiedzą, kiedy podejmują decyzję na podstawie czegoś, co może się zmienić, co w przyszłości mogą wymagać dostosowania. Jest to więc decyzja dotycząca zarządzania ryzykiem, którą moim zdaniem programiści powinni mieć swobodę. Jeśli ludzie nie mają wrażliwości, aby chcieć programować abstrakcje i nieufnie podchodzić do szczegółów implementacji, to nigdy nie będą dobrymi programistami.

Michael Shaw
źródło
2
Programiści OO nie chcą, aby wszystko było obiektem. Ale niektóre rzeczy (wiele rzeczy) korzystają z enkapsulacji. Mam problem ze zrozumieniem, w jaki sposób lub gdzie Twoja odpowiedź naprawdę odnosi się do pytania. Wydaje się po prostu twierdzić, że koncepcja nie jest specyficzna dla OOP i że OOP ma inne problemy itd. - czy możesz podać jasny przykład, nawet jeśli to tylko kilka linii pseudokodu? A może opis tablicy, który bierze to pod uwagę? A może coś, co uzasadniałoby te twierdzenia?
Aaronaught
2
@Aaronaught: Odniosłem się do wielu (choć nie wszystkich) kwestii poruszonych w pytaniu i odniosłem się do artykułu na temat programowania funkcjonalnego, który patrzy na modułowość w podobny sposób, jak w pytaniu. W dużej mierze fakt, że koncepcja nie jest specyficzna dla OOP, jest odpowiedzią na jego pytanie (chyba że całkowicie nie zrozumiałem pytania). Naprawdę nie mówiłem o tym, że OOP ma tutaj inne problemy. Masz dobry punkt, podając przykład; Zobaczę, czy uda mi się wymyślić dobry.
Michael Shaw,
2
„Czasami wystarczy prosta struktura danych”. +1. Coś OOP ma sens, czasami jest to FP.
Laurent Bourgault-Roy,
1
@Aaronaught Ta odpowiedź wskazuje, że modułowość (która jest zarówno enkapsulacją, jak i ponownym użyciem) jest jednym z celów FP (jak omówiono w „Dlaczego programowanie funkcjonalne ma znaczenie”), dlatego odpowiedź na pytanie (1) pytania jest „ Nie".
Andres F.,
2
@JimmyHoffa ukrywanie informacji jest zdrową zasadą nawet poza OO. W haskell nadal chcę, aby użytkownicy mogli pracować przy minimalnej wiedzy na temat dowolnej struktury danych. Oczywiście dostęp do wewnętrznych elementów jest mniej niebezpieczny, ponieważ nic nie jest zmienne. Ale im mniej użytkownik zobaczy jeden moduł / strukturę danych / jakąkolwiek abstrakcyjną koncepcję, tym więcej możliwości refaktoryzacji otrzymasz. Nie dbam o to, czy Mapa to zrównoważone drzewo binarne lub mysz w małym pudełku na moim komputerze. Jest to główna motywacja ukrywania danych i jest ważna poza OO.
Simon Bergot,
12

... i prawdopodobnie jest jednym z tych artykułów, na których oparto OOP.

Nie do końca, ale dodało to dyskusji, szczególnie dla praktyków, którzy w tym czasie byli szkoleni w zakresie rozkładania systemów według pierwszych kryteriów opisanych w artykule.

Najpierw chcę wiedzieć, czy moja ocena jest poprawna. Czy paradygmat FP i ten artykuł filozoficznie się nie zgadzają?

Nie. Ponadto, moim zdaniem, opis tego, jak wygląda program FP, nie różni się od żadnego innego, który wykorzystuje procedury lub funkcje:

Dane są przekazywane z funkcji do funkcji, przy czym każda funkcja jest ściśle świadoma danych i „zmienia je” po drodze.

... z wyjątkiem części „intymności”, ponieważ możesz (i często masz) funkcje działające na abstrakcyjnych danych, właśnie po to, aby uniknąć intymności. Tak więc masz pewną kontrolę nad tą „intymnością” i możesz ją regulować w dowolny sposób, konfigurując interfejsy (tj. Funkcje) dla tego, co chcesz ukryć.

Nie widzę więc żadnego powodu, dla którego nie moglibyśmy przestrzegać kryteriów ukrywania informacji Parnasa za pomocą programowania funkcjonalnego i skończyć na implementacji indeksu KWIC o podobnych wskazanych korzyściach jak jego druga implementacja.

Zakładając, że się zgadzają, chciałbym wiedzieć, czym jest implementacja ukrywania danych przez FP. Widać to wyraźnie w OOP. Możesz mieć prywatne pole, do którego nikt poza klasą nie ma dostępu. W FP nie ma dla mnie oczywistej analogii.

Jeśli chodzi o dane, możesz opracować abstrakcje danych i abstrakcje typów danych za pomocą FP. Każda z nich ukrywa konstrukcje betonowe i manipuluje nimi, wykorzystując funkcje jako abstrakcje.

EDYTOWAĆ

Coraz więcej twierdzeń mówi, że „ukrywanie danych” w kontekście FP nie jest tak przydatne (lub OOP-ish (?)). Pozwólcie, że wytłoczę tutaj bardzo prosty i jasny przykład z SICP:

Załóżmy, że Twój system musi działać z liczbami wymiernymi. Jednym ze sposobów przedstawienia ich jest para lub lista dwóch liczb całkowitych: licznika i mianownika. A zatem:

(define my-rat (cons 1 2)) ; here is my 1/2 

Jeśli zignorujesz abstrakcję danych, najprawdopodobniej otrzymasz licznik i mianownik za pomocą cari cdr:

(... (car my-rat)) ; do something with the numerator

Zgodnie z tym podejściem wszystkie części systemu, które manipulują liczbami wymiernymi, będą wiedziały, że liczba wymierna to cons- będą consnumerować, aby tworzyć racjonalne liczby i wyodrębniać je za pomocą operatorów list.

Jednym z problemów, z którymi możesz się spotkać, jest konieczność zredukowania liczby racjonalnych liczb - konieczne będą zmiany w całym systemie. Ponadto, jeśli zdecydujesz się zmniejszyć w czasie tworzenia, możesz później odkryć, że zmniejszenie przy dostępie do jednego z racjonalnych warunków jest lepsze, co daje kolejną zmianę na pełną skalę.

Innym problemem jest to, że hipotetycznie preferowana jest alternatywna reprezentacja dla nich i użytkownik zdecyduje się porzucić consreprezentację - zmiana w pełnej skali ponownie.

Każdy rozsądny wysiłek w radzeniu sobie z tymi sytuacjami prawdopodobnie zacznie ukrywać reprezentację racjonalności za interfejsami. Na koniec możesz otrzymać coś takiego:

  • (make-rat <n> <d>)zwraca liczbę wymierną, której licznikiem jest liczba całkowita, <n>a której mianownikiem jest liczba całkowita <d>.

  • (numer <x>)zwraca licznik liczby wymiernej <x>.

  • (denom <x>)zwraca mianownik liczby wymiernej <x>.

i system nie będzie już (i nie powinien) wiedzieć, z czego są racjonalne. Wynika to z faktu cons, cari cdrnie są nierozerwalnie związane z wymiernych, ale make-rat, numeri denom to . Oczywiście może to być z łatwością system FP. Zatem „ukrywanie danych” (w tym przypadku lepiej znane jako abstrakcja danych lub próba enkapsulacji reprezentacji i konkretnych struktur) stanowi istotną koncepcję i technikę szeroko stosowaną i badaną, czy to w kontekście OO, programowania funkcjonalnego czy cokolwiek.

Chodzi o to ... chociaż można próbować rozróżnić, jaki rodzaj ukrywania lub enkapsulacji dokonują (czy ukrywają decyzję projektową, czy struktury danych lub algorytmy - w przypadku abstrakcji proceduralnych), wszystkie mają ten sam motyw: są motywowane przez jeden lub więcej punktów, które Parnas wyraził wyraźnie. To jest:

  • Możliwość zmiany: czy wymagane zmiany mogą być wprowadzone lokalnie, czy rozłożone w systemie.
  • Niezależny rozwój: w jakim stopniu dwie części systemu mogą być rozwijane równolegle.
  • Zrozumiałość: jaka część systemu musi być znana, aby zrozumieć jedną z jego części.

Powyższy przykład został zaczerpnięty z książki SICP, więc w celu pełnej dyskusji i prezentacji tych koncepcji w książce bardzo polecam sprawdzenie rozdziału 2 . Polecam również zapoznanie się z abstrakcyjnymi typami danych w kontekście FP, co przedstawia inne problemy.

Thiago Silva
źródło
Zgadzam się, że ukrywanie danych jest istotne w PR. I, jak mówisz, istnieją sposoby na osiągnięcie tego.
Andres F.,
2
Właśnie powiedziałeś mi pięknie: masz te funkcje, które nie ukrywają danych, są wyrażeniami, które opisują, jak uzyskać dane, a więc dzięki abstrakcji w wyrażeniu zamiast w polu danych nie musisz się martwić o ukrywanie dane poprzez utworzenie jakiegoś złożonego obiektu z prywatnymi członkami lub przez uniemożliwienie dostępu do wartości przeciwnych, działania związane z generowaniem, wyszukiwaniem i interakcją z danymi wymiernymi są wyrażane, dlatego rzeczywiste dane wymierne nie muszą być ukryte, ponieważ zmiana danych nie spowoduje zmień swoje wyrażenia.
Jimmy Hoffa,
8

Twoje przekonanie, że programowaniu funkcjonalnemu brakuje ukrywania danych, jest błędne. Po prostu stosuje inne podejście do ukrywania danych. Jednym z najczęstszych sposobów ukrywania danych w programowaniu funkcjonalnym jest użycie funkcji polimorficznych, które przyjmują funkcję jako argument. Na przykład ta funkcja

map :: (a -> b) -> [a] -> [b]
map _ [] = []
map f (x:xs) = f x : map f xs

może widzieć tylko najbardziej zewnętrzną strukturę danych (tj. że jest to lista), nie może nic widzieć o danych, które zawiera lista i może działać tylko na danych za pośrednictwem pojedynczej funkcji, która jest do niej przekazywana.

Funkcja przekazywana jako argument jest analogiczna do metody publicznej dla typu danych zawierającego listę. Zapewnia ograniczony sposób działania na danych, ale nie ujawnia wewnętrznego działania typu danych.

Dirk Holsopple
źródło
5

Zamierzam tu uderzyć w kończynę i powiedzieć, że ta koncepcja nie ma znaczenia w FP, tak jak w OO.

tl; dr; Celem ukrywania danych jest zapewnienie, że obowiązki są utrzymywane tam, gdzie powinny, i że nie ma zewnętrznych podmiotów, które bałagają się danymi, dla których nie mają wiedzy. W FP dane są generowane przez wyrażenia, i w ten sposób nie możesz z nimi bałaganu, ponieważ nie są to zmienne właściwości, tak jak obliczenia kompozycyjne, które całkowicie zmieniają zasady gry.


Z moich doświadczeń związanych z FP; które są wprawdzie nieistotne, staram się znaleźć jeden wyraźny kontrast z OO w tym, co oznacza dobre / wspólne modelowanie danych.

Ten kontrast polega na tym, że w OO ogólnie modelujesz rzeczy, które reprezentują twoje dane. Obowiązkowa analogia samochodu:

OO

  • Masz obiekt samochodowy, który poprawnie ukrywa szczegóły dotyczące samochodu, takie jak implementacja AC (czy jest to napęd pasowy, czy pneumatyczny? Konsumenci nie powinni tego wiedzieć, więc ukryj go).
  • Ten obiekt samochodu ma wiele właściwości i metod, które przedstawiają wszystkie fakty dotyczące samochodu, a także sposoby pracy z samochodem.
  • Ten obiekt samochodu ma właściwości, które są komponentami samochodu, które dodatkowo ukrywają przed całym samochodem ich poszczególne implementacje, a ich fakty z danymi pozwalają na wymienne komponenty samochodu.

Należy tutaj zwrócić uwagę na to, że kiedy modelujesz rzeczy w formacie OO, chodzi przede wszystkim o reprezentowanie rzeczy jako danych. Masz obiekty o właściwościach, wiele z tych właściwości to obiekty o większej liczbie właściwości. Istnieje kilka metod tu i ówdzie dołączonych do tych obiektów, ale tak naprawdę zazwyczaj zmieniają właściwości obiektów w ten sposób i to znowu, jest to modelowanie bardzo skoncentrowane na danych; oznacza to, że modelujesz swoje dane do interakcji, koncentrując się na ich strukturze, aby udostępnić wszystkie punkty danych, aby konsumenci mogli zmieniać dane w ten i inny sposób.

FP

  • Masz wiele obliczeń, które pozwalają opisać zachowania
  • Te wyrażenia zachowania są powiązane w sposób, który można przełożyć na sposób, w jaki zachowania samochodów są ze sobą powiązane, na przykład samochód z przyspieszaniem / zwalnianiem, istnieją dwa zachowania, które są sobie przeciwne w podobny sposób.

Duża różnica między OO i FP, która ciągle mnie uderza, polega na tym, jak powiedziałem powyżej, w jaki sposób modelujesz dane. W OO, jak wspomniano powyżej, modelujesz dane jako dane, w FP modelujesz dane jako obliczenia, wyrażenia, algorytmy, bardziej chodzi o modelowanie działań twoich danych niż o fakty. Pomyśl o podstawowym modelowaniu danych w matematyce, zawsze chodzi o uzyskanie równania, które może wygenerować twoje dane, które modelują twoje dane jako działanie, które je powoduje, w przeciwieństwie do OO, modelowanie wymyśla sposób reprezentacji danych. To duża różnica między FP a OO.

Pamiętaj, że przez długi czas LISP, jeden z podstawowych języków FP, żył z bardzo małą ilością prymitywnych typów danych. Działa to, ponieważ podejście nie polega na modelowaniu złożonych reprezentacji danych, lecz na obliczeniach generujących i wyrażających zachowania systemu.

Kiedy zaczynam pisać kod w FP, zaczynam od pisania kodu, który coś robi, a kiedy zaczynam pisać kod w OO, zaczynam od pisania modeli, które coś opisują. Robienie rzeczy jest ukryte w FP przez bycie wyrażeniami, robienie rzeczy jest ujawniane w OO poprzez opisywanie danych, ukrycie tych danych ogranicza wspomniane narażenie.


Wracając do pytania, co FP mówi o chowaniu danych, czy docenia je, czy się z tym nie zgadza, czy co nie?

Mówię, że to nie ma znaczenia, w OO twoje dane to odwaga i ważne elementy w twoim programie, które powinny być ukryte przed wtrącaniem się. W FP odwaga i wiedza o twoim systemie są ukryte w algorytmach i obliczeniach, które wyrażają ten system. Są one z definicji mniej lub bardziej niezmienne, jedynym sposobem na mutację wyrażeń obliczeniowych są makra, ale nawet wtedy definicje mutacji są wyrażeniami, których nie można dalej mieszać.

Jimmy Hoffa
źródło
to jest wspaniałe, naprawdę podobało mi się czytanie. Dzięki za Twój wkład
Chris McCall
5

Tu jest trochę paradoksu. Mimo że programowanie funkcjonalne koncentruje się na funkcjach i często ma funkcje, które działają bezpośrednio na prymitywnych typach danych, zwykle ukrywa więcej danych niż programowanie obiektowe.

Jak to się dzieje? Pomyśl o ładnym interfejsie OO, który ukrywa podstawowe dane - być może kolekcje (staram się wybrać coś niemal wszechobecnego). Być może nie będziesz musiał znać podstawowego typu obiektów w kolekcji lub typu obiektu implementującego kolekcję, o ile wiesz, że kolekcja implementuje, powiedzmy, IEnumerable. Więc masz ukrywanie danych.

W programowaniu funkcjonalnym możesz napisać funkcję, która skutecznie działa z interfejsem IEnumerable, ale działa na prymitywnym typie danych (lub na dowolnym typie danych). Ale co, jeśli ten typ nigdy nie wdrożył metod IEnumerable? Oto klucz, zawsze możesz mieć „metody”, które tworzą potrzebne elementy „interfejsu”, parametry przekazane do twojej funkcji. Lub możesz łączyć funkcje z danymi i robić rzeczy w sposób podobny do OO.

Zauważ, że tak czy inaczej nie ukrywasz żadnych danych w OO. Moja ogólna funkcja, która działa na dowolnym typie, wyraźnie nie ma dostępu do danych tego typu - dzieje się to w ramach funkcji przekazywanych jako parametry do funkcji ogólnej, ale funkcja ogólna nigdy nie zagląda do tych funkcji, aby zobaczyć dane.

Jeśli chodzi o punkt 1, nie sądzę, aby FP i artykuł tak naprawdę się nie zgadzały. Nie sądzę, aby twoja charakterystyka ukrywania danych przez FP była poprawna. Z pewnością można by wdrożyć projekt preferowany przez autora w FP.

O ile punkt 4 (2 i 3 nie ma sensu odpowiadać, biorąc pod uwagę to, co powiedziałem dla punktu 1), jest różny. Różni się również w językach OO, a w wielu prywatnych obszarach są prywatne na podstawie konwencji, a nie są wymuszone przez język.

psr
źródło
Innymi słowy: w programowaniu funkcjonalnym znacznie więcej jest „ukrytych” domyślnie po prostu dlatego, że nawet nie istnieje! Tylko rzeczy, które wyraźnie wprowadzasz w zakres, są „niejawne”.
lewo wokół
3

Po pierwsze, dzięki za link do tego wspaniałego artykułu, nie wiedziałem o tym do tej pory i dał mi to świetny wkład w niektóre rzeczy, o których rozmawiałem z innymi projektantami oprogramowania ze społeczności w ostatnich latach. Oto moja opinia na ten temat:

Najpierw chcę wiedzieć, czy moja ocena jest poprawna. Czy paradygmat FP i ten artykuł filozoficznie się nie zgadzają?

Projektowanie FP koncentruje się w dużej mierze na przepływie danych (co nie jest tak złe, jak sugeruje artykuł). Jeśli jest to całkowite, „niezgodność” jest dyskusyjna.

Zakładając, że się nie zgadzają, w jaki sposób FP „kompensuje” brak ukrywania danych? Być może poświęcają ukrywanie danych, ale zyskują X, Y i Z. Chciałbym poznać powód, dla którego X, Y i Z są uważane za bardziej korzystne niż ukrywanie danych.

IMHO nie rekompensuje. Patrz poniżej.

Lub, zakładając, że się nie zgadzają, być może FP uważa, że ​​ukrywanie danych jest złe. Jeśli tak, dlaczego uważa, że ​​ukrywanie danych jest złe?

Nie sądzę, że większość użytkowników lub projektantów FP czuje lub myśli w ten sposób, patrz poniżej.

Zakładając, że się zgadzają, chciałbym wiedzieć, czym jest implementacja ukrywania danych przez FP. Widać to wyraźnie w OOP. Możesz mieć prywatne pole, do którego nikt poza klasą nie ma dostępu. W FP nie ma dla mnie oczywistej analogii.

Chodzi o to - prawdopodobnie widziałeś tyle systemów OOP zaimplementowanych w niefunkcjonalny sposób, że uważasz, że OOP jest niefunkcjonalny. I to jest błędem, IMHO OOP i FP są w większości koncepcjami ortogonalnymi i możesz doskonale budować funkcjonalne systemy OO, co daje oczywistą odpowiedź na twoje pytanie. Klasyczna implementacja „obiektowa” w FP odbywa się za pomocą zamknięć , a jeśli chcesz, aby obiekty były używane w funkcjonalnym systemie, kluczową kwestią jest zaprojektowanie ich jako niezmiennych.

Aby tworzyć większe systemy, IMHO możesz tworzyć moduły, klasy i obiekty przy użyciu koncepcji OO, dokładnie tak, jak opisano w rozdziale „Modularyzacja 2” w artykule bez opuszczania „ścieżki FP”. Wykorzystasz koncepcję modułową swojego ulubionego języka FP, sprawisz, że wszystkie twoje obiekty będą niezmienne i użyjesz tego, co najlepsze z obu światów.

Doktor Brown
źródło
3

TL; DR : Nie

Czy paradygmat FP i ten artykuł filozoficznie się nie zgadzają ?.

Nie, nie ma. Programowanie funkcjonalne jest deklaratywne, które jest „stylem budowania struktury i elementów programów komputerowych, który wyraża logikę obliczeń bez opisywania przepływu sterowania”. Nie chodzi tylko o przestrzeganie schematu, a raczej o tworzenie reguł, które pozwalają na powstanie przepływu samodzielnie.

Programowanie proceduralne jest znacznie bliższe kodowaniu schematu blokowego niż programowanie funkcjonalne. Wynika z tego, że zachodzą transformacje i koduje je w procedury, które są wykonywane w kolejności, dokładnie tak, jak opisano to na schemacie blokowym.

Podczas gdy wykonywanie procedur przez model proceduralny języka jako sekwencja rozkazów imperatywnych, które mogą pośrednio zmieniać stan współdzielony, wykonywanie funkcjonalnych modeli języków programowania jako ocena złożonych wyrażeń, które zależą tylko od siebie pod względem argumentów i zwracanych wartości. Z tego powodu programy funkcjonalne mogą mieć dowolniejszą kolejność wykonywania kodu, a języki mogą oferować niewielką kontrolę nad kolejnością wykonywania różnych części programu. (Na przykład argumenty wywołania procedury w schemacie są wykonywane w dowolnej kolejności).

Ukrywanie danych

  • Programowanie funkcjonalne ma własne metody ukrywania danych, na przykład zamykanie sieci . To jest ukrywanie danych przez enkapsulację w zamknięciu. Trudno jest, aby pola były już prywatnymi danymi, które zostały zamknięte, ponieważ tylko zamknięcie ma odniesienie do danych i nie można odwoływać się do nich poza zamknięciem.
  • Jednym z powodów ukrywania danych jest stabilizacja interfejsu programistycznego poprzez ukrywanie mutujących danych. Programowanie funkcjonalne nie ma mutujących danych, dlatego nie wymaga tyle ukrywania danych.
dietbuddha
źródło
3
„Programowanie funkcjonalne nie ma mutacji danych, dlatego nie wymaga tyle ukrywania danych”. - jest to bardzo mylące stwierdzenie. Sam powiedziałeś (i zgadzam się), że jednym z powodów enkapsulacji zachowania jest kontrola mutacji danych. Ale wyciągnięcie wniosku, że brak mutacji prawie sprawia, że ​​enkapsulacja jest bezużyteczna, jest ogromnym problemem. ADT i abstrakcja danych ogólnie są wszechobecne w literaturze i systemach FP.
Thiago Silva,
Nigdy nie powiedziałem, że „prawie czyni enkapsulację bezużyteczną”. To są twoje myśli i tylko twoje. Powiedziałem, że nie musisz ukrywać tylu danych z powodu braku mutujących zmiennych. Nie powoduje to, że enkapsulacja lub ukrywanie danych są bezużyteczne, po prostu ogranicza ich użycie, ponieważ te przypadki nie istnieją. Wszystkie pozostałe przypadki, w których ukrywanie danych i enkapsulacja są przydatne, są nadal aktualne.
dietbuddha,