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.
- Najpierw chcę wiedzieć, czy moja ocena jest poprawna. Czy paradygmat FP i ten artykuł filozoficznie się nie zgadzają?
- 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.
- 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?
- 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ć
private
pole, do którego nikt poza klasą nie ma dostępu. W FP nie ma dla mnie oczywistej analogii. - 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:
źródło
Odpowiedzi:
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 .
źródło
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.
Nie. Ponadto, moim zdaniem, opis tego, jak wygląda program FP, nie różni się od żadnego innego, który wykorzystuje procedury lub funkcje:
... 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.
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:
Jeśli zignorujesz abstrakcję danych, najprawdopodobniej otrzymasz licznik i mianownik za pomocą
car
icdr
: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ącons
numerować, 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ć
cons
reprezentację - 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
,car
icdr
nie są nierozerwalnie związane z wymiernych, alemake-rat
,numer
idenom
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:
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.
źródło
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
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.
źródło
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
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
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ć.
źródło
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.
źródło
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:
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.
IMHO nie rekompensuje. Patrz poniżej.
Nie sądzę, że większość użytkowników lub projektantów FP czuje lub myśli w ten sposób, patrz poniżej.
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.
źródło
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.
Ukrywanie danych
źródło