Czy programowanie funkcjonalne zwiększa złożoność kodu? [Zamknięte]

17

Przez cały miniony rok pisałem kod Scala (pochodzący z Java). Naprawdę podobało mi się, jak można stworzyć prostszy i bardziej przejrzysty kod z vals, klasami przypadków, funkcjami map / filter / lambda, implikacjami i wnioskowanie o typie. Użyłem go głównie do aplikacji opartych na Akce .

W tym roku pracuję nad projektem Scala z nowym zespołem, który naprawdę lubi programowanie funkcjonalne. Często używają Scalaz , a kod jest wszędzie wypełniony aplikacjami, granicami kontekstu, czytnikiem / pisarzem / monadą stanu, nawet główna metoda jest „zawinięta” w monadę we / wy. Ich rozumowanie polega na tym, że kompilator „działa dla nas”, zapewniając, że kod jest poprawny, a każda funkcja jest wolna od skutków ubocznych.

Mimo to z mojego punktu widzenia cała ta składnia naprawdę przeszkadza w logice biznesowej. Na przykład rodzaj „MyBusinessObject” jest w porządku, a także typy takie jak „List [MyBusinessObject]”, „Option [MyBusinessObject]” lub nawet „Future [MyBusinessObject]”. Wszystkie mają jasne znaczenie i cel. Z drugiej strony kod taki jak:

def method[M[_]: Applicative] = {
  case (a, b) => (ca[M](a) |@| cb[M](b)) {
    case t @ (ra, rb) =>
      if (ra.result && rb.result) t.right
      else t.left
  }
}

czy to dodaje złożoności programowi, czy tylko ja nie jestem przyzwyczajony do tego sposobu programowania?

Luciano
źródło
6
Jakie jest twoje obiektywne pytanie?
Deer Hunter
1
Wygląda na to, że prowadzi do ton kodu z jedną lub dwiema literowymi nazwami zmiennych. Wygląda trochę jak APL. To nie jest uzupełnienie.
user949300
3
Zacząłem grać z Haskellem w zeszłym roku. W tamtych czasach wyglądało to niezwykle skomplikowane, kiedy nie rozumiałem takich pojęć, jak curry, funktory, monady itp. Haskell jest podobny do Scalaza, ponieważ ma wiele krótkich, symbolicznych funkcji, takich jak >>=i <$>, które nic nie znaczą, dopóki ty wiedzieć, co robią. Po zapoznaniu się z ich znaczeniem czytają mi teraz bardzo naturalnie i szybko. Nie do końca odpowiedź, tylko moje obiektywne doświadczenie z takimi rzeczami. Używam również Scali, ale nie mam doświadczenia z biblioteką Scalaz.
KChaloux
3
Po prostu nie znasz idiomów. @ user949300 krótkie zmienne nie są tak naprawdę problemem dla wielu kodów funkcjonalnych (pomyśl o konwencjach stylów matematycznych!). Przeczytaj także blog Tony'ego Morrisa, aby uzyskać bardziej szczegółową dyskusję na temat tego, co lepiej przekazuje znaczenie, typy lub pełne nazwy zmiennych.
Andres F.
3
@ user949300: krótkie nazwy zmiennych są preferowane lokalnie, to samo jest w świecie funkcjonalnym i imperatywnym (prawda, nie pisałbyś for(i=0; i<7; ++i) { trivialOperation(i); }przy pomocy jakiejś niewygodnej trivialOperationCountzmiennej, prawda?) Teraz, funkcjonalne języki programowania z dopasowaniem wzorców czasami wprowadzają więcej zmiennych, w których po prostu wypiszę wywołania metody akcesora w OO. Wynik jest ogólnie bardziej zwięzły; być może nieco mniej zrozumiałe, ale przeglądanie deklaracji danych zwykle szybko to wyjaśnia. Pisanie statyczne bardzo pomaga, nie jest tak jak w APL.
leftaroundabout

Odpowiedzi:

37

Nie ma to nic wspólnego z programowaniem funkcjonalnym - taką sytuację można znaleźć w kontekście dowolnego innego języka programowania - programiści, którzy tak bardzo lubią zaawansowane konstrukcje „swojego” języka, że ​​ignorują zdrowy rozsądek dotyczący czytelności i prostoty. Z taką sytuacją spotkałem się w językach C, C ++, Perl, Java, C #, Basic i innych niefunkcjonalnych. To nie jest funkcjonalne programowanie, które zwiększa złożoność kodu - robią to programiści.

Nie zrozum mnie źle, nie polecam unikania zaawansowanych funkcji językowych - ale ważne jest, aby znaleźć właściwą równowagę w danym kontekście. Pisząc ogólną bibliotekę dla ponad 100 000 programistów na całym świecie, obowiązują inne środki, jak w przypadku pisania indywidualnego generatora raportów tylko dla lokalnego biura.

Doktor Brown
źródło
7
Ma to również wiele wspólnego ze społecznością. Dla programistów z tłem Java lub C # kod jest ledwo zrozumiały (i jego społeczność też go nie zrozumie). Ale jeśli piszesz na przykład Haskell i nie używasz monad, aplikacji, funktorów itp., Zaskakujesz społeczność tego języka. „Naturalność” kodu nie jest nieodłączna, lecz w stosunku do społeczności i ustalonych praktyk.
Andres F.
1
Trudno to dostrzec, ponieważ większość z nas wywodzi się z imperatywnych środowisk, co czasami prowadzi nas do błędnych założeń na temat tego, co naturalne.
Andres F.
wystarczy spojrzeć na bibliotekę SLT C ++, która mogłaby zostać napisana tak, aby była o wiele bardziej czytelna dla amatora
maniaka zapadkowego
@ratchetfreak: Chyba masz na myśli STL. Myślę, że to bardzo dobry przykład (i rzeczywiście miałem to na uwadze w swojej odpowiedzi). Korzystanie z meta programowania szablonów ma wiele sensu, gdy jesteś programistą STL, ponieważ sprawia, że ​​STL jest bardziej wielokrotnego użytku. Ludzie, którzy muszą utrzymywać ten kod, są zwykle przyzwyczajeni do metaprogramowania szablonów. Używanie metaprogramowania szablonów w sposób podobny do STL w standardowej aplikacji biznesowej może łatwo doprowadzić do nadmiernie skomplikowanego, trudnego do utrzymania kodu. Z pewnością można znaleźć (rzadkie) przypadki, w których TMP jest w porządku nawet w aplikacjach biznesowych, oczywiście.
Doc Brown
@DocBrown tak, dysleksja rozpoczęła się w niewłaściwym czasie, ale szczerze mówiąc, gdybym miał pół umysłu do (i znacznie więcej czasu niż teraz), mógłbym przepisać wiele ciał funkcyjnych, aby były bardziej czytelne.
maniak zapadkowy
7

Powiedziałbym, że nie jesteś przyzwyczajony do sposobu, w jaki kodują, jest przynajmniej częścią obrazu. Jestem w podobnej sytuacji jak ty (wchodzę z C # do F # i pracuję z ludźmi z tłem Haskell) i chociaż uważam to za ciekawe doświadczenie, mam chwile, kiedy uderzam głową o ścianę, rozplątując szczególnie skomplikowana kompozycja funkcji bez punktów, aby po prostu zrozumieć, co się tam dzieje. To kwestia kulturowa.

Co do tego, czy ten konkretny kod dodaje programowi złożoności - nie wiem. Zgodzono się, że ogólny fragment kodu może być sam w sobie złożony. Ale to narzędzie, a nie część logiki biznesowej. Jeśli chcesz wiedzieć, czy baza kodu jest bardziej złożona, czy prostsza, musisz wyobrazić sobie, jak wyglądałaby bez tego fragmentu kodu. Często dzieje się tak w przypadku takich ogólnych konstrukcji, że same są złożone, ale jest to złożoność, którą można zmieścić na jednym ekranie. Jednocześnie upraszczają całą bazę kodu. Dotyczy to szczególnie monad.

Z drugiej strony może to być także „sztuka dla sztuki”, jak sugeruje @Doc Brown. Nie można tego również wykluczyć.

scrwtp
źródło
5

Twierdziłbym, że ogólnie programowanie funkcjonalne zmniejsza złożoność poprzez eliminację stanu zmiennego, a tym samym zmniejszenie liczby przypadków, które należy wziąć pod uwagę, próbując zrozumieć, jak działa sekcja kodu.

Jednak programowanie funkcjonalne umożliwia uzyskanie wyższego stopnia abstrakcji i chociaż wysoce abstrakcyjny kod może być niezwykle przydatny, może być również trudny do zrozumienia, ponieważ z definicji jest on oddzielony od kontekstu, który zwykle służyłby do zrozumienia. Twój komentarz „Wszystkie mają wyraźne znaczenie i cel” na temat obiektów biznesowych jest niewątpliwie prawdziwy, ale tak naprawdę odzwierciedla fakt, że ta logika jest bardzo specyficzna dla potrzeb i kontekstu, który już rozumiesz. Istnienie konstrukcji takiej jak Monada pozwala na zrobienie czegoś bardzo przydatnego przy niewielkim wysiłku, ale sieć jest pełna stron próbujących wyjaśnić, czym jest Monada. To dla ciebie abstrakcja.

Scalaz napisali także ludzie, którzy od dawna jedli i oddychali FP; chcieli wprowadzić funkcjonalność dostępną w Haskell do Scali. Czyniąc to, nie starali się być pedagogami. Scalaz używa słownictwa i stylu, które wydają się jasne i proste dla autorów, ale obce dla niewtajemniczonych. Metody, które są dla nas zastanawiające, wydawały się autorom tak oczywiste, biorąc pod uwagę ich pochodzenie z Haskell, że nawet nie uzasadnili komentarza.

Ponadto, jako funkcjonalny język programowania, Scala ma pewne niedociągnięcia (częściowo dlatego, że JVM ma wady), które w niektórych przypadkach zmusiły autorów Scalaz do napisania brzydszego kodu. Na przykład brak ogólnej eliminacji wywołań ogonkowych wymusza użycie trampolin w niektórych kodach, a brak „dobrego systemu” może komplikować podpisy typu.

I wreszcie Scalaz świetnie się wykorzystuje. Można to uznać za znak jego siły, ale dla niewtajemniczonych może uczynić źródło łamigłówką - każdy losowy fragment kodu, na który patrzysz, prawdopodobnie wykorzysta coś innego, co wygląda ci obco.

Powieś tam. I to może pomóc.

AmigoNico
źródło
-4

Czy to komplikuje program, czy może po prostu nie jesteś przyzwyczajony do tego sposobu programowania?

Dlaczego uważasz, że te możliwości to nie to samo?

Dobrze napisany kod mogą odczytać ludzie, którzy nie znają konkretnego języka programowania. Niektóre języki (BASIC, Pascal itp.) Mogą być czytane i rozumiane przez dzieci w wieku szkolnym, które nigdy wcześniej nie widziały żadnych języków programowania.

Jeśli ktoś, kto ma doświadczenie z innymi językami i doświadczenie ze Scalą (i który, jak zakładam, pracował ze Scalazem przez co najmniej tydzień i ma kolegów, którzy wyjaśnią trudniejsze rzeczy) jest nadal zdezorientowany; to dowód na to, że zwiększył złożoność.

Brendan
źródło
11
To po prostu nieprawda:"Well written code can be read by people who aren't familiar with the specific programming language."
Andres F.
@Andres: To prawda ... do pewnego stopnia. Dobrze napisany kod oddzieli logikę biznesową od szczegółów implementacyjnych, a logika biznesowa powinna być czytelna, ponieważ większość jej działań polega na wykonywaniu prostych wywołań do dobrze nazwanych funkcji pomocniczych. Ci pomocnicy mogą oczywiście korzystać z wszelkiego rodzaju funkcji językowych i wymagają dużego doświadczenia z językiem i bibliotekami, aby zrozumieć.
Ben Voigt
4
Wierzę, że następuje to idiomatyczne APL: x[⍋x←6?40]. Jak myślisz, co to robi? Na pewno bym nie wiedział…
bdesham
3
@Brendan Tylko w ramach tego samego paradygmatu (a nawet wtedy, czasem nie). Na przykład Prolog, Java i APL są tak różne, że twierdzę, że jeśli znasz tylko jeden z nich (i żadnych innych języków), nie możesz odczytać pozostałych dwóch, bez względu na to, jak dobrze znasz pierwszy. (Poważnie, w przykładzie Bdeshama, jak, do diabła, należy interpretować „choinkę”, jeśli nie znacie żadnej APL?)
Izkata,
1
Brendan, twoja dobrze napisana definicja jest niestandardowa. Dobrze napisany jest zawsze związany z językiem i jego społecznością. Program w języku X jest dobrze napisany, jeśli nie zawiera błędów, jest wydajny i przejrzysty ... dla danej publiczności! Nawiasem mówiąc, dotyczy to ogólnie języka pisanego: zawsze poznaj swoich odbiorców. To, co jest odpowiednie dla (powiedzmy) artykułu naukowego, prawdopodobnie nie nadaje się do wysłania wiadomości e-mail do twojej mamy.
Andres F.,