Ponieważ język maszynowy (np. 0110101000110101
) Języki komputerowe ewoluowały w kierunku wyższych form abstrakcji, ogólnie ułatwiając zrozumienie kodu, gdy dotyczy on problemu. Asembler był abstrakcją nad kodem maszynowym, C był abstrakcją nad asemblerem itp.
Projektowanie obiektowe wydaje się bardzo dobrze pozwalać modelować problem pod względem obiektów, np. Problem systemu rejestracji na studia można modelować za pomocą Course
klasy, Student
klasy itp. Następnie, pisząc rozwiązanie w języku OO mamy podobne klasy, które przyjmują obowiązki i co na ogół jest pomocne przy projektowaniu, szczególnie do modularyzacji kodu. Jeśli dam ten problem 10 niezależnym zespołom, które rozwiążą go metodą OO, ogólnie 10 rozwiązań będzie miało wspólne klasy związane z problemem. Różnic może być wiele, kiedy zaczniesz się łączyć i interakcji tych klas, więc nie ma czegoś takiego jak „zerowa luka reprezentacyjna”.
Moje doświadczenie z programowaniem funkcjonalnym jest bardzo ograniczone (bez użycia w świecie rzeczywistym, tylko programy typu Hello World). Nie widzę, w jaki sposób takie języki pozwalają na łatwe mapowanie rozwiązań FP na problemy (z małą luką reprezentacyjną), tak jak robią to języki OO.
Rozumiem zalety FP w odniesieniu do programowania współbieżnego. Ale czy czegoś mi brakuje, czy FP nie polega na zmniejszeniu luki reprezentacyjnej (ułatwienie zrozumienia rozwiązań)?
Innym sposobem na zadanie tego pytania: czy kod FP 10 różnych zespołów rozwiązujących ten sam rzeczywisty problem ma wiele wspólnego?
Z Wikipedii na temat Abstrakcji (informatyka) (moje wyróżnienie):
Funkcjonalne języki programowania zwykle wykazują abstrakcje związane z funkcjami , takie jak abstrakcje lambda (przekształcenie terminu w funkcję jakiejś zmiennej), funkcje wyższego rzędu (parametry to funkcje), abstrakcja w nawiasie (przekształcenie terminu w funkcję zmiennej).
Luka reprezentacyjna mogłaby zostać potencjalnie zwiększona, ponieważ [niektórych] problemów w świecie rzeczywistym nie da się łatwo modelować za pomocą takich abstrakcji.
Innym sposobem, w jaki widzę zmniejszającą się lukę reprezentacyjną, jest prześledzenie elementów rozwiązania z powrotem do problemu. 0
„S i 1
s w kodzie maszynowym jest bardzo trudny do prześledzenia, podczas gdy Student
klasa jest łatwa do prześledzenia. Nie wszystkie klasy OO łatwo śledzą obszar problemów, ale wiele z nich.
Czy abstrakcje FP nie zawsze wymagają wyjaśnienia, aby dowiedzieć się, którą część problematycznej przestrzeni rozwiązują (oprócz problemów matematycznych )?OK - jestem dobry z tej strony. Po przeanalizowaniu wielu innych przykładów widzę, jak abstrakcje FP są bardzo jasne dla części problemu wyrażonej w przetwarzaniu danych.
Akceptowana odpowiedź na powiązane pytanie Czy można używać UML do modelowania programu funkcjonalnego? - mówi „Funkcjonalni programiści nie mają większego zastosowania do tworzenia diagramów”. Naprawdę nie dbam o to, czy jest to UML, ale zastanawiam się, czy abstrakty FP są łatwe do zrozumienia / komunikacji, jeśli nie ma powszechnie używanych diagramów (zakładając, że ta odpowiedź jest poprawna). Ponownie, mój poziom wykorzystania / zrozumienia FP jest trywialny, więc nie rozumiem potrzeby schematów na prostych programach FP.
Projekt OO ma poziomy abstrakcji dla funkcji / klasy / pakietu, z enkapsulacją (kontrola dostępu, ukrywanie informacji), co ułatwia zarządzanie złożonością. Są to elementy, które pozwalają łatwiej przejść od problemu do rozwiązania i wrócić.
Wiele odpowiedzi mówi o tym, jak analizy i projektowanie są wykonywane w FP w sposób analogiczny do OO, ale jak dotąd nikt nie przytacza niczego wysokiego poziomu (Paul zacytował kilka interesujących rzeczy, ale jest niski poziom). Wczoraj dużo ćwiczyłem w Google i znalazłem ciekawą dyskusję. Poniżej pochodzi z Refactoring Functional Programs autorstwa Simona Thompsona (2004) (wyróżnienie moje)
Przy projektowaniu systemu obiektowego przyjmuje się za pewnik, że projekt poprzedzi programowanie. Projekty będą pisane przy użyciu systemu takiego jak UML, który jest obsługiwany w narzędziach takich jak Eclipse. Początkujący programiści mogą nauczyć się projektowania wizualnego przy użyciu systemów takich jak BlueJ. Prace nad podobną metodologią programowania funkcjonalnego opisano w FAD: Analiza i projektowanie funkcjonalne , ale niewiele jest innych prac. Przyczyn może być wiele.
Istniejące programy funkcjonalne mają skalę, która nie wymaga projektowania. Wiele programów funkcjonalnych jest niewielkich, ale inne, takie jak Glasgow Haskell Compiler, są znaczne.
Programy funkcjonalne bezpośrednio modelują domenę aplikacji, przez co projektowanie nie ma znaczenia. Podczas gdy języki funkcjonalne zapewniają wiele potężnych abstrakcji, trudno argumentować, że zapewniają one wszystkie abstrakcje potrzebne do modelowania świata rzeczywistego.
Programy funkcjonalne są budowane jako ewoluująca seria prototypów.
W cytowanej powyżej rozprawie doktorskiej korzyści wynikające z zastosowania metodologii analizy i projektowania (ADM) są przedstawione niezależnie od paradygmatów. Podano jednak argument, że ADM powinny być zgodne z paradygmatem implementacji. Oznacza to, że OOADM działa najlepiej w programowaniu OO i nie jest dobrze stosowany w innym paradygmacie, takim jak FP. Oto świetny cytat, który, jak myślę, parafrazuje to, co nazywam luką reprezentacyjną:
można długo spierać się o to, który paradygmat zapewnia najlepsze wsparcie dla rozwoju oprogramowania, ale osiąga się najbardziej naturalny, wydajny i skuteczny pakiet programistyczny, gdy pozostaje się w obrębie jednego paradygmatu, od opisu problemu do wdrożenia i dostarczenia.
Oto zestaw diagramów zaproponowanych przez FAD:
- diagramy zależności funkcji przedstawiające funkcję z tymi, których używa w swojej implementacji;
- diagram zależności typów, który zapewnia tę samą usługę dla typów; i,
- diagramy zależności modułów, które przedstawiają widoki architektury modułu systemu.
W sekcji 5.1 pracy FAD znajduje się studium przypadku, które jest systemem do automatyzacji produkcji danych związanych z ligą piłki nożnej. Wymagania są w 100% funkcjonalne, np. Wprowadzanie wyników piłki nożnej, tworzenie tabel ligowych, tabel wyników, tabel obecności, przenoszenie graczy między drużynami, aktualizowanie danych po nowych wynikach itp. Nie udokumentowano, jak FAD działa w celu rozwiązania niefunkcjonalnych wymagań , oprócz stwierdzenia, że „nowa funkcjonalność powinna być dozwolona przy minimalnym koszcie”, co jest prawie niemożliwe do przetestowania.
Niestety, oprócz FAD, nie widzę żadnych współczesnych odniesień do języków modelowania (wizualnych), które byłyby proponowane dla FP. UML to kolejny paradygmat, więc powinniśmy o tym zapomnieć.
Odpowiedzi:
Podstawowe dane mają taką samą strukturę w prawie każdym paradygmacie. Będziesz mieć a
Student
,Course
itp., Niezależnie od tego, czy jest to obiekt, struktura, zapis, czy cokolwiek innego. Różnica w OOP nie polega na strukturze danych, lecz na strukturze funkcji.W rzeczywistości uważam, że programy funkcjonalne są znacznie bardziej dopasowane do tego, co myślę o problemie. Na przykład, aby zaplanować harmonogram studenta na następny semestr, myślisz o takich rzeczach, jak listy kursów, które student ukończył, kursy w programie studiów, kursy oferowane w tym semestrze, kursy, na które student ukończył warunki wstępne, kursy z czasami, które nie konflikt itp.
Nagle nie jest jasne, która klasa powinna utworzyć i przechowywać wszystkie te listy. Tym bardziej, gdy masz złożone kombinacje tych list. Musisz jednak wybrać jedną klasę.
W FP piszesz funkcje, które zabierają ucznia i listę kursów, i zwracasz przefiltrowaną listę kursów. Możesz zgrupować wszystkie takie funkcje razem w module. Nie musisz go łączyć z jedną klasą.
Dlatego modele danych wyglądają bardziej jak programiści OOP myślą o swoich modelach, zanim zostaną zanieczyszczeni klasami, które nie mają innego celu niż zapewnienie dogodnych miejsc do umieszczenia funkcji, które działają na kombinacjach innych klas. Żadnych dziwnych
CourseStudentFilterList
lub podobnych klas, których zawsze potrzebujesz w OOP, ale nigdy nie myśl o początku.źródło
StudentCourseFilterer
aby utrzymać zarządzaniefunc
(tak jak to nazwałeśIFilter
, itp.). Ogólnie rzecz biorąc, w FP nazwałbyś tofunc
lubf
gdy albo jestsort
albofilter
jest prawidłowym wyborem! Na przykład podczas pisania funkcji wyższego rzędu, która przyjmujefunc
jako parametr.Kiedy wiele lat temu brałem udział w zajęciach z języka Java, oczekiwano, że pokażemy nasze rozwiązania całej klasie, więc mogłem zobaczyć, jak ludzie myślą; jak logicznie rozwiązują problemy. W pełni oczekiwałem, że rozwiązania skupią się wokół trzech lub czterech popularnych rozwiązań. Zamiast tego obserwowałem, jak 30 uczniów rozwiązało problem na 30 zupełnie różnych sposobów.
Oczywiście, gdy początkujący programiści zdobywają doświadczenie, zyskają kontakt z typowymi wzorcami oprogramowania, zaczną używać tych wzorców w kodzie, a następnie ich rozwiązania mogą się łączyć wokół kilku optymalnych strategii. Wzorce te tworzą język techniczny, za pomocą którego doświadczeni programiści mogą się komunikować.
Językiem technicznym leżącym u podstaw programowania funkcjonalnego jest matematyka . W związku z tym problemy, które najlepiej nadają się do rozwiązania przy użyciu programowania funkcjonalnego, są zasadniczo problemami matematycznymi. Z punktu widzenia matematyki nie mam na myśli matematyki, którą można zobaczyć w rozwiązaniach biznesowych, takich jak dodawanie i odejmowanie. Mówię raczej o matematyce, którą możesz zobaczyć w Math Overflow, lub matematyce, którą zobaczysz w wyszukiwarce Orbitza (jest napisana w Lisp).
Ta orientacja ma pewne istotne konsekwencje dla funkcjonalnych programistów próbujących rozwiązać rzeczywiste problemy programistyczne:
Programowanie funkcjonalne jest bardziej deklaratywne niż imperatywne; dotyczy przede wszystkim powiedzenia komputerowi, co ma robić, a nie jak to zrobić.
Programy obiektowe są często budowane od góry do dołu. Tworzony jest projekt klasy, a szczegóły są wypełniane. Programy funkcjonalne są często budowane od podstaw, zaczynając od małych, szczegółowych funkcji, które są łączone w funkcje wyższego poziomu.
Programowanie funkcjonalne może mieć zalety w prototypowaniu złożonej logiki, konstruowaniu elastycznych programów, które mogą zmieniać się i ewoluować w sposób organiczny, oraz w tworzeniu oprogramowania, w którym początkowy projekt jest niejasny.
Programy obiektowe mogą być bardziej odpowiednie dla domen biznesowych, ponieważ klasy, komunikaty między obiektami i wzorce oprogramowania zapewniają strukturę, która jest odwzorowana na domenę biznesową, przechwytuje jej inteligencję biznesową i dokumentuje ją.
Ponieważ całe praktyczne oprogramowanie wywołuje efekty uboczne (I / O), czysto funkcjonalne języki programowania wymagają mechanizmu do wywoływania tych efektów ubocznych, pozostając matematycznie czystym (monady).
Programy funkcjonalne można łatwiej sprawdzić ze względu na ich matematyczny charakter. System typowania Haskell może znaleźć w czasie kompilacji rzeczy, których nie potrafi większość języków OO.
I tak dalej. Podobnie jak w przypadku wielu rzeczy w informatyce, języki obiektowe i języki funkcjonalne mają różne kompromisy.
Niektóre współczesne języki OO przyjęły niektóre użyteczne koncepcje programowania funkcjonalnego, dzięki czemu możesz mieć to, co najlepsze z obu światów. Linq i funkcje językowe dodane w celu jego obsługi są tego dobrym przykładem.
źródło
SafeString
typu do reprezentowania ciągów znaków, którym uniknięto HTML - i generalnie śledzą efekty.Chciałbym podkreślić aspekt, który uważam za ważny i który nie został uwzględniony w innych odpowiedziach.
Po pierwsze, myślę, że luka reprezentacyjna między problemami a rozwiązaniami może być bardziej w umyśle programisty, zgodnie z ich pochodzeniem i pojęciami, które są im bardziej znane.
OOP i FP patrzą na dane i operacje z dwóch różnych perspektyw i zapewniają różne kompromisy, jak już zauważył Robert Harvey.
Jednym z ważnych aspektów, które się różnią, jest sposób, w jaki pozwalają one na rozszerzenie twojego oprogramowania.
Rozważ sytuację, w której masz kolekcję typów danych i zbiór operacji, na przykład masz różne formaty obrazów i utrzymujesz bibliotekę algorytmów do przetwarzania obrazów.
Programowanie funkcjonalne ułatwia dodawanie nowych operacji do oprogramowania: potrzebujesz tylko lokalnej zmiany w kodzie, a mianowicie dodajesz nową funkcję, która obsługuje różne formaty danych wejściowych. Z drugiej strony, dodawanie nowych formatów jest bardziej zaangażowane: musisz zmienić wszystkie funkcje, które już zaimplementowałeś (zmiana nielokalna).
Podejście obiektowe jest do tego symetryczne: każdy typ danych przeprowadza własną implementację wszystkich operacji i odpowiada za wybór właściwej implementacji w czasie wykonywania (dynamiczne wysyłanie). Ułatwia to dodanie nowego typu danych (np. Nowego formatu obrazu): wystarczy dodać nową klasę i zaimplementować wszystkie jej metody. Z drugiej strony dodanie nowej operacji oznacza zmianę wszystkich klas, które muszą zapewnić tę operację. W wielu językach odbywa się to poprzez rozszerzenie interfejsu i dostosowanie wszystkich klas implementujących go.
To inne podejście do rozszerzenia jest jednym z powodów, dla których OOP jest bardziej odpowiedni w przypadku niektórych problemów, w których zestaw operacji zmienia się rzadziej niż zestaw typów danych, na których działają te operacje. Typowym przykładem są GUI: masz stały zestaw operacji, które musi implementować wszystkie widżety (
paint
,resize
,move
, i tak dalej) i zbiór widżetów, które chcesz rozszerzyć.Tak więc, zgodnie z tym wymiarem, OOP i FP to tylko dwa szczególne sposoby organizacji twojego kodu. Patrz SICP , w szczególności sekcja 2.4.3, tabela 2.22 i akapit Przekazywanie wiadomości .
Podsumowując: w OOP używasz danych do organizowania operacji, w FP używasz operacji do organizowania danych. Każde podejście jest silniejsze lub słabsze w zależności od kontekstu. Ogólnie rzecz biorąc, żaden z nich nie ma większej reprezentacyjnej luki między problemem a rozwiązaniem.
źródło
Other
wariant / konstruktor w Twoim typie danych oraz dodatkowy parametr funkcji, który należy przekazać wszystkim funkcjom w celu obsługi drugiego przypadku. Jest to oczywiście niewygodne / mniej naturalne w odniesieniu do rozwiązania OOP (dynamiczna wysyłka). W ten sam sposób wzór odwiedzających jest niewygodny / mniej naturalny niż rozwiązanie FP (funkcja wyższego rzędu).Większość języków funkcjonalnych nie jest zorientowana obiektowo. Nie oznacza to, że nie mają żadnych obiektów (w sensie złożonych typów, które są powiązane z określoną funkcjonalnością). Haskell, podobnie jak java, ma Listy, Mapy, Tablice, wszelkiego rodzaju Drzewa i wiele innych złożonych typów. Jeśli spojrzysz na moduł Haskell List lub Map, zobaczysz zestaw funkcji bardzo podobnych do metod w większości równoważnych modułów bibliotecznych w języku OO. Jeśli przejrzysz kod, znajdziesz nawet podobne kapsułkowanie, z niektórymi typami (a dokładniej ich konstruktorami) i funkcjami, które mogą być używane tylko przez inne funkcje w module.
Sposób, w jaki pracujesz z tymi typami w Haskell, często niewiele różni się od sposobu OO. W Haskell mówię
a w Javie mówisz
Pomidor, pomidor. Funkcje Haskell nie są powiązane z obiektem, ale są ściśle związane z jego typem. Ach, ale , ¨ mówisz w Javie jest wywołaniem które kiedykolwiek długość metoda jest odpowiednia do rzeczywistego klasy tego obiektu. Niespodzianka - Haskell robi polimorfizm również z klasami typów (i innymi rzeczami).
W Haskell, jeśli mam to - zbiór liczb całkowitych - to nie ma znaczenia, czy kolekcja jest lista lub zestaw lub tablicy, ten kod
zwróci kolekcję ze wszystkimi elementami podwójnymi. Polimorfizm oznacza, że zostanie wywołana odpowiednia funkcja mapy dla określonego typu.
Te przykłady nie są tak naprawdę złożonymi typami, ale to samo dotyczy trudniejszych problemów. Pomysł, że języki funkcjonalne nie pozwalają modelować złożonych obiektów i kojarzyć z nimi określoną funkcjonalność, jest po prostu błędny.
Jest to ważne i znaczące różnice między stylem i funkcjonalnej OO Do not ale myślę, że to odpowiedź musi radzić sobie z nimi. Pytałeś, czy języki funkcjonalne zapobiegają (czy utrudniają) intuicyjne modelowanie problemów i zadań. Odpowiedź brzmi nie.
źródło
FP rzeczywiście dąży do zmniejszenia luki reprezentacyjnej:
W językach funkcjonalnych często spotyka się praktykę budowania języka (przy użyciu projektowania oddolnego) we wbudowanym języku specyficznym dla domeny (EDSL) . Pozwala to opracować sposób wyrażania obaw biznesowych w sposób naturalny dla domeny w języku programowania. Haskell i Lisp są z tego dumni.
Częścią (jeśli nie wszystkim) tego, co umożliwia tę umiejętność, jest większa elastyczność i ekspresja w obrębie samych podstawowych języków; dzięki funkcjom pierwszej klasy, funkcjom wyższego rzędu, składowi funkcji, aw niektórych językach, algebraicznym typom danych (związki dyskryminowane AKA) oraz możliwości definiowania operatorów *, istnieje większa elastyczność w wyrażaniu rzeczy niż w przypadku OOP, który oznacza, że prawdopodobnie znajdziesz bardziej naturalne sposoby wyrażania rzeczy ze swojej problematycznej domeny. (Powinno powiedzieć coś, że wiele języków OOP ostatnio stosuje wiele z tych funkcji, jeśli nie zaczyna od nich).
* Tak, w wielu językach OOP możesz zastąpić standardowe operatory, ale w Haskell możesz zdefiniować nowe!
Z mojego doświadczenia w pracy z językami funkcjonalnymi (głównie F # i Haskell, trochę Clojure oraz trochę Lisp i Erlang), łatwiej jest mi mapować przestrzeń problemową na język niż z OOP - szczególnie z algebraicznymi typami danych, które uważam za bardziej elastyczne niż zajęcia. Coś, co rzuciło mnie na pętlę podczas rozpoczynania pracy z FP, szczególnie z Haskellem, polegało na tym, że musiałem myśleć / pracować na poziomie nieco wyższym niż byłem przyzwyczajony w językach imperatywnych lub OOP; możesz też na to wpaść.
źródło
Jak ujął to Niklaus Wirth: „Algorytmy + struktury danych = programy”. Programowanie funkcjonalne dotyczy sposobu organizowania algorytmów i nie mówi wiele o sposobach organizacji struktur danych. Rzeczywiście istnieją języki FP zarówno ze zmiennymi (Lisp), jak i niezmiennymi (Haskell, Erlang). Jeśli chcesz porównać i porównać FP z czymś, powinieneś wybrać programowanie imperatywne (C, Java) i deklaratywne (Prolog).
Z drugiej strony OOP jest sposobem na budowanie struktur danych i dołączanie do nich algorytmów. FP nie uniemożliwia budowania struktur danych podobnych do OOP. Jeśli mi nie wierzysz, spójrz na deklaracje typu Haskell. Jednak większość języków FP zgadza się, że funkcje nie należą do struktur danych i powinny raczej znajdować się w tej samej przestrzeni nazw. Odbywa się to w celu uproszczenia budowy nowych algorytmów poprzez składanie funkcji. Rzeczywiście, jeśli wiesz, jakie dane wejściowe pobiera funkcja i jakie generuje dane wyjściowe, dlaczego miałoby to mieć znaczenie, do której struktury danych należy? Na przykład, dlaczego
add
funkcja musi być wywołana na instancji typu Integer i przekazała argument zamiast po prostu przekazać dwa argumenty Integer?Dlatego nie rozumiem, dlaczego FP powinien utrudniać zrozumienie rozwiązań w ogóle, i nie sądzę, że i tak to robi. Należy jednak pamiętać, że doświadczony programista imperatywny z pewnością znajdzie programy funkcjonalne trudniejsze do zrozumienia niż programy imperatywne i na odwrót. Jest to podobne do dychotomii systemu Windows - Linux, gdy ludzie, którzy zainwestowali 10 lat w wygodne korzystanie ze środowiska Windows, mają trudności z Linuksem po miesiącu użytkowania, a ci, którzy są przyzwyczajeni do systemu Linux, nie mogą osiągnąć takiej samej wydajności w systemie Windows. Stąd „luka reprezentacyjna”, o której mówisz, jest bardzo subiektywna.
źródło
if you know what input a function takes and what output it produces, why should it matter which data structure it belongs too?
To brzmi jak spójność, która jest ważna dla każdego systemu modułowego. Twierdziłbym, że brak wiedzy na temat znalezienia funkcji utrudniłby zrozumienie rozwiązania, co wynika z większej luki reprezentacyjnej. Wiele systemów OO ma ten problem, ale jest to spowodowane złym projektem (niska kohezja). Znów problem przestrzeni nazw nie leży w mojej kompetencji w FP, więc może nie jest tak źle, jak się wydaje.