Czytelność programowania funkcjonalnego [zamknięte]

13

Jestem tego ciekawy, ponieważ pamiętam, zanim jeszcze nauczyłem się funkcjonalnych języków, uważałem je wszystkie okropnie, okropnie, strasznie nieczytelne. Teraz, gdy znam Haskella i f #, stwierdzam, że czytanie kodu zajmuje trochę więcej czasu, ale ten mały kod robi znacznie więcej niż równoważna ilość w języku imperatywnym, więc wydaje mi się, że to zysk netto i nie jestem nadzwyczaj praktykowany w zakresie funkcjonalnym.

Oto moje pytanie, ciągle słyszę od ludzi z OOP, że funkcjonalny styl jest strasznie nieczytelny. Jestem ciekawy, czy tak jest i łudzę się, czy jeśli poświęcili czas na naukę funkcjonalnego języka, cały styl nie byłby już bardziej nieczytelny niż OOP?

Czy ktoś widział jakieś dowody lub miał jakieś anegdoty, w których widział, że dzieje się tak czy inaczej z częstotliwością wystarczającą do powiedzenia? Jeśli pisanie funkcjonalne naprawdę ma mniejszą czytelność, to nie chcę go nadal używać, ale tak naprawdę nie wiem, czy tak jest, czy nie ...

Jimmy Hoffa
źródło
1
Pamiętam, jak patrzyłem na kod dla „Super Nario Brothers” svn.coderepos.org/share/lang/haskell/nario, kiedy ledwo wiedziałem o programowaniu funkcjonalnym i pamiętam, że pomyślałem „Wow. Wydaje się to naprawdę czytelne ”
WuHoUnited
3
@BlackICE Linq i lambdas są dalekie od zdefiniowania „programowania funkcjonalnego”. Programowanie funkcjonalne często obejmuje systemy typów, ponieważ kiedy zaczynasz rozpoznawać funkcje jako typy, a nie tylko obiekty / klasy / rekordy jako typy, otrzymujesz wiele nowych umiejętności i technik. Wyrażenia LINQ i Lambda (lub funkcje listy monad i wyższych rzędów) to tylko dwie specyficzne techniki wynikowe, których jest znacznie więcej.
Jimmy Hoffa

Odpowiedzi:

15

Czytelność kodu jest bardzo subiektywna . Z tego powodu różni ludzie uważają ten sam kod za czytelny lub nieczytelny.

Zadanie x = x + 1brzmi dziwnie dla matematyka, ponieważ zadanie wygląda myląco podobnie do porównania.

Prosty wzór projektowy OOP byłby całkowicie niejasny dla kogoś, kto nie zna tych wzorów. Zastanów się nad singletonem . Ktoś zapytałby: „Dlaczego potrzebuję prywatnego konstruktora? Jaka jest tajemnicza metoda getInstance() ? Dlaczego nie stworzymy zmiennej globalnej i nie przypiszemy do niej obiektu?”

To samo dotyczy kodu FP. Jeśli nie znasz logicznych wzorców za kulisami, bardzo trudno będzie zrozumieć coś bardzo podstawowego, np. Jak przekazać funkcję jako argument do innej funkcji.

Powiedziawszy to, należy zrozumieć, że FP nie jest srebrną kulą. Istnieje wiele aplikacji, w których FP byłby trudny do zastosowania i dlatego doprowadziłoby to do gorszego czytelnego kodu .

bytebuster
źródło
Ludzie powinni stworzyć coś podobnego do siebie (ludzkości). Np. Nawet w wizji komputerowej mamy teraz sieci neuronowe. Ciało ludzkie składa się z przedmiotów, atrybutów, metod, a my opisujemy inne rzeczy za pomocą przedmiotów, atrybutów, metod. Więc programowanie funkcjonalne nie ma sensu
25
12

Krótka odpowiedź:

Jednym z elementów, które sprawiają, że ludzie mówią, że funkcjonalny kod programowania jest trudny do odczytania, jest to, że preferuje bardziej zwartą składnię.

Długa odpowiedź:

Sam programowanie funkcjonalne nie może być czytelne ani nieczytelne, ponieważ jest to paradygmat, a nie styl pisania kodu. Na przykład w języku C # programowanie funkcjonalne wygląda następująco:

return this.Data.Products
    .Where(c => c.IsEnabled)
    .GroupBy(c => c.Category)
    .Select(c => new PricesPerCategory(category: c.Key, minimum: c.Min(d => d.Price), maximum: c.Max(d => d.Price)));

i zostałby uznany za czytelny przez każdą osobę z wystarczającym doświadczeniem w Javie, C # lub podobnych językach.

Z drugiej strony, składnia językowa jest bardziej zwarta dla wielu języków funkcjonalnych (w tym Haskell i F #) w porównaniu do popularnych języków OOP, dając pierwszeństwo symbolom, a nie słowom zwykłym angielskim.

Dotyczy to także języków poza FP. Jeśli porównasz popularne języki OOP z niektórymi mniej popularnymi, które zwykle używają więcej angielskich słów, te ostatnie będą łatwiejsze do zrozumienia dla osób bez doświadczenia w programowaniu.

Porównać:

public void IsWithinRanges<T>(T number, param Range<T>[] ranges) where T : INumeric
{
    foreach (var range in ranges)
    {
        if (number >= range.Left && number <= range.Right)
        {
            return true;
        }
    }

    return false;
}

do:

public function void IsWithinRanges
    with parameters T number, param array of (Range of T) ranges
    using generic type T
    given that T implements INumeric
{
    for each (var range in ranges)
    {
        if (number is from range.Left to range.Right)
        {
            return true;
        }
    }

    return false;
}

W ten sam sposób:

var a = ((b - c) in 1..n) ? d : 0;

można wyrazić w wyimaginowanym języku jako:

define variable a being equal to d if (b minus c) is between 1 and n or 0 otherwise;

Kiedy krótsza składnia jest lepsza?

Podczas gdy bardziej złożona składnia jest łatwiejsza do zrozumienia dla początkującego, bardziej zwarta składnia jest łatwiejsza dla doświadczonych programistów. Krótszy kod oznacza mniej znaków do pisania, co oznacza większą wydajność.

Szczególnie nie ma sensu zmuszanie osoby do wpisania słowa kluczowego w celu wskazania czegoś, co można wywnioskować ze składni.

Przykłady:

  • W PHP musisz pisać functionprzed każdą funkcją lub metodą, bez konkretnego powodu.

  • Ada zawsze mnie przerażała za zmuszanie programistów do pisania wielu angielskich słów, zwłaszcza, że ​​nie ma prawidłowego IDE z autouzupełnianiem. Ostatnio nie korzystałem z Ady, a ich oficjalny samouczek nie działa, więc nie mogę dać przykładu; jeśli ktoś ma przykład, proszę zmodyfikować moją odpowiedź.

  • Składnia 1..nużywana w wielu FP (i Matlabie) może być zastąpiona przez between 1, nlub between 1 and nlub between (1, n). Słowo betweenkluczowe znacznie ułatwia zrozumienie dla osób, które nie znają składni języka, ale mimo to dwie kropki są znacznie szybsze.

Arseni Mourzenko
źródło
8
Zgadzam się z sentymentem, ale z różnych powodów. To nie o tym, jak łatwo ekspert może napisać kod, jak czytać kod jest ogólnie znacznie częściej niż to jest napisane. Krótszy kod może również pomóc w czytelności, nie marnując cennego czasu, miejsca na ekranie i zdolności umysłowych, jeśli coś byłoby już oczywiste na podstawie krótszego wskaźnika (na przykład znaki nowej linii zamiast średników, i tak zbieg okoliczności). Może również zaszkodzić, gdy stanie się zbyt tajemniczy (na przykład wolę for x in xsponad for x xs, ponieważ pomaga mi odróżnić zmienną pętli i kontener).
Wiele osób, które znalazłem, narzeka na FP narzeka również na fragment C #, który wymieniłeś, jak powiedziałeś, jest to również przykład FP. Być może jest to bardziej kwestia moich kolegów niż czytelność, jeśli większość osób OOP uważa, że ​​ten fragment jest czytelny, chociaż nie jestem całkowicie przekonany, że większość osób OOP robi to na podstawie moich doświadczeń ... dlatego też nie jestem pewien co do tego tematu.
Jimmy Hoffa,
@Jimmy Hoffa: Zobacz programmers.stackexchange.com/a/158721/6605, gdzie omawiam dokładnie fragment kodu C # i jego postrzeganie przez początkujących programistów.
Arseni Mourzenko
1
@Sergiy: w Javie lub C # podpis metody nie zawiera methodsłowa kluczowego. Przykład: public int ComputePrice(Product product). Dodanie takiego słowa kluczowego, jak to zrobił PHP, po prostu dodaje bałaganu zamiast ułatwiać czytanie. Jeśli programista nie jest w stanie zrozumieć, że funkcja jest funkcją z jej podpisu, albo ten programista nie ma żadnego doświadczenia, albo kod jest znacznie bardziej nieczytelny niż najgorszy kod, jaki kiedykolwiek widziałem.
Arseni Mourzenko
1
@Sergiy: są przypadki, w których gadatliwość może pomóc (w przeciwnym razie widzielibyśmy języki i kod, np. p i ComputePrice(product): _.unitPrice * ~.cart[_]), Ale niektóre języki posuwają się za daleko. Przykład zbędnego functionsłowa kluczowego w PHP jest dobrym przykładem.
Arseni Mourzenko
6

Myślę, że jednym z powodów jest to, że programowanie funkcjonalne ma tendencję do pracy z bardziej abstrakcyjnymi koncepcjami. To sprawia, że ​​kod jest krótszy i bardziej czytelny dla osób, które znają i rozumieją pojęcia (ponieważ trafnie wyrażają istotę problemu), ale jest nieczytelny dla osób, które ich nie znają.

Na przykład dla funkcjonalnego programisty naturalne jest napisanie kodu, który może zawieść w różnych punktach Maybemonady lub Eithermonady. Lub napisać stanowe obliczenia za pomocą Statemonady. Ale dla kogoś, kto nie rozumie pojęcia monad, wszystko to wydaje się czymś w rodzaju czarnej magii.

Sądzę więc, że rozsądne byłoby użycie fragmentów programowania funkcjonalnego nawet w zespole osób pracujących w OOP, o ile używa się pojęć, które są łatwe do zrozumienia lub nauki, takich jak mapowanie kolekcji z funkcją lub zamknięciami .

(I oczywiście zależy to również od programisty, który napisał jakiś fragment kodu. Możesz pisać strasznie nieczytelny kod w dowolnym języku, a także pisać w ładnym i czystym stylu).

Petr Pudlák
źródło
2

Czytelność nie ma nic wspólnego z paradygmatami, modelami itp. Im bardziej kod odzwierciedla semantykę domeny problemowej, tym bardziej działa terminologia i idiomy takiej domeny problemowej, tym bardziej jest ona czytelna.

To jest powód, dla którego kod OOP wcale nie jest tak czytelny: nie tak wiele problemów w świecie rzeczywistym jest naturalnie wyrażanych w kategoriach hierarchicznych taksonomii. Przedmioty przekazujące sobie wiadomości to dziwna koncepcja i bardzo dalekie od wszystkiego, co „prawdziwe”. Zaskakujące jest, że istnieje wiele innych koncepcji ze świata rzeczywistego, które można naturalnie odwzorować na funkcjonalne idiomy. Problemy zwykle określa się deklaratywnie, dlatego rozwiązanie deklaratywne jest bardziej naturalne.

Chociaż istnieje wiele innych semantyek, które mogą być bliższe rzeczywistemu światu niż czysta funkcjonalna, czysta OOP, czysty przepływ danych lub cokolwiek, co kiedykolwiek będzie. Z tego powodu podejście do rozwiązywania problemów zorientowane na język daje najbardziej czytelny kod, pokonując wszystkie istniejące „czyste” paradygmaty. W przypadku języków specyficznych dla domeny każdy problem wyraża się w jego naturalnej terminologii. Języki funkcjonalne są nieco lepsze od głównego nurtu OOP w ułatwianiu implementacji DSL (chociaż dostępne są znacznie lepsze podejścia).

Logika SK
źródło
1

Czytelność kodu jest subiektywna. Jestem pewien, że niektórzy krytykują to z braku zrozumienia. Istnieją naturalnie różne idiomy w sposobie realizacji wspólnych wzorców. Na przykład wzorzec gościa nie ma sensu w języku z dopasowaniem wzorca .

Wnioskowanie typu może być trudną funkcją. Zwykle jest to świetny akcelerator rozwoju, jednak czasami może być trudno zorientować się, jakie typy są zaangażowane z powodu braku kontekstu prowadzącego do pomyłek. Nie jest to ograniczone do języków programowania funkcjonalnego - widziałem to również w szablonach C ++!

Współczesne języki są zazwyczaj paradygmatem . Dotyczy to zarówno C #, jak i F #. Możliwe jest napisanie stylu funkcjonalnego w języku C # i stylu imperatywnego w języku F #. Ci sami, którzy krytykują języki funkcjonalne, prawdopodobnie piszą kod funkcjonalny w swoim języku obiektowym.

Rozwój komercyjny w językach funkcjonalnych jest wciąż stosunkowo niedojrzały. Widziałem wiele błędów, które można by uznać za złą formę w dowolnym języku. Jak na przykład:

  1. Zmiana stylu podczas nauki języka (a nie refaktoryzacja) prowadząca do niespójności.
  2. Za dużo w jednym pliku.
  3. Zbyt wiele instrukcji w jednym wierszu.
Dave Hillier
źródło
W odniesieniu do błędów dodam również: 4. Nadużywanie symboli, np .: ~ @ # $% ^ & * () -_ + = \ | /.,; 5. Funkcje niejawne: opcjonalne deklaracje / słowa kluczowe / oświadczenia, niejawne konwersje itp .;
Sergiy Sokolenko