Czy niewielkie ilości programowania funkcjonalnego są zrozumiałe dla osób spoza programu ramowego? [Zamknięte]

43

Przypadek : Pracuję w firmie, pisząc aplikację w języku Python, która obsługuje wiele danych w tablicach. Obecnie jestem jedynym programistą tego programu, ale prawdopodobnie będzie on używany / modyfikowany / rozszerzany w przyszłości (1-3 lata) przez innego programistę, w tej chwili mi nieznanego. Prawdopodobnie nie będę tam bezpośrednio, aby pomóc, ale może udzielę wsparcia przez e-mail, jeśli mam na to czas.

Tak więc, jako programista, który nauczył się programowania funkcjonalnego (Haskell), mam tendencję do rozwiązywania, na przykład, filtrowania w następujący sposób:

filtered = filter(lambda item: included(item.time, dur), measures)

Reszta kodu to OO, to tylko niektóre małe przypadki, w których chcę go rozwiązać w ten sposób, ponieważ według mnie jest znacznie prostszy i piękniejszy.

Pytanie : Czy pisanie takiego kodu dzisiaj jest w porządku?

  • Jak programista, który nie napisał / nie nauczył się FP, reaguje na taki kod?
  • Czy to jest czytelne?
  • Modyfikowalny?
  • Czy powinienem napisać dokumentację, taką jak wyjaśnienie dziecku, co robi linia?

     # Filter out the items from measures for which included(item.time, dur) != True

Zapytałem mojego szefa, a on po prostu powiedział: „FP to czarna magia, ale jeśli działa i jest najbardziej wydajnym rozwiązaniem, to można z niej korzystać”.

Jaka jest twoja opinia na ten temat? Jako programista inny niż FP, jak reagujesz na kod? Czy kod jest „googable”, abyś mógł zrozumieć, co robi? Chciałbym poznać opinie na ten temat.

kd35a
źródło
14
Moim zdaniem wiele współczesnych języków pozwala na pewne funkcjonalne programowanie, a korzystanie z nich staje się coraz bardziej powszechne. Osobiście powiedziałbym: idź do niego (przynajmniej do stosunkowo prostych zadań filtrowania / mapowania).
Joachim Sauer
Nie mogę dać ci żadnej odpowiedzi, ponieważ lubię FP. Dobrym pomysłem byłoby dodanie komentarza, jeśli nie jest to jednak proste.
Bruno Schäpper
3
Twój komentarz może być jaśniejszy; filtr zawiera elementy, dla których test ma wartość Prawda, dzięki czemu Twój komentarz mógłby przeczytać: # Select the item's from measures for which included(item.time, dur) == Trueunikanie podwójnego negatywu zawsze poprawia zrozumienie.
Martijn Pieters
6
Czy zamiast tego rozważałeś użycie list? Często są one uważane za bardziej „pytoniczne” niż mapa / filtr.
phant0m
2
@MatjazMuhic Cóż, to jest piękne ...;)
kd35a

Odpowiedzi:

50

Czy to jest czytelne?

Dla mnie: Tak, ale doszedłem do wniosku, że społeczność Python często wydaje się, że rozumienie list jest rozwiązaniem czystszym niż używanie map()/ filter().

W rzeczywistości GvR rozważał nawet całkowite usunięcie tych funkcji.

Rozważ to:

filtered = [item for item in measures if included(item.time, dur)]

Ponadto ma to tę zaletę, że zrozumienie listy zawsze zwraca listę. map()a filter()z drugiej strony zwraca iterator w Pythonie 3.

Uwaga: jeśli zamiast tego chcesz mieć iterator, wystarczy zastąpić []go ():

filtered = (item for item in measures if included(item.time, dur))

Szczerze mówiąc, niewiele widzę żadnego powodu do stosowania map()lub filter()w Pythonie.

Czy to można modyfikować?

Tak, z pewnością jednak jest jedna rzecz, która to ułatwia: uczyń to funkcją, a nie lambda.

def is_included(item):
    return included(item.time, dur)
filtered = filter(is_included, measures)
filtered = [item for item in measures if is_included(item)]

Jeśli twój stan stanie się bardziej złożony, skaluje się on znacznie łatwiej, a także pozwala ponownie użyć czeku. (Pamiętaj, że możesz tworzyć funkcje wewnątrz innych funkcji, dzięki czemu może znajdować się bliżej miejsca, w którym jest używany).

Jak programista, który nie napisał / nie nauczył się FP, reaguje na taki kod?

Przegląda dokumentację Pythona i wie, jak to działa pięć minut później. W przeciwnym razie nie powinien programować w języku Python.

map()i filter()niezwykle proste. To nie tak, że prosisz ich o zrozumienie monad. Dlatego nie sądzę, że musisz pisać takie komentarze. Używaj dobrych nazw zmiennych i funkcji, wtedy kod jest prawie oczywisty. Nie możesz przewidzieć, jakie funkcje językowe nie zna programista. Z tego co wiesz, następny programista może nie wiedzieć, czym jest słownik.

To, czego nie rozumiemy, zwykle nie jest dla nas czytelne. Dlatego możesz argumentować, że jest nie mniej czytelny niż zrozumienie listy, jeśli nigdy wcześniej nie widziałeś żadnego z nich. Ale jak wspomniał Joshua w swoim komentarzu, również uważam, że ważne jest, aby zachować spójność z tym, co używają inni programiści - przynajmniej jeśli alternatywa nie zapewnia znaczącej przewagi.

phant0m
źródło
5
Może warto też wspomnieć wyrażenia generatorów , które działają tak samo jak listowych ale generatorów zwrotnych zamiast list, jak mapi filterzrobić w Pythonie 3.
James
@James: Świetny pomysł, dodałem je do mojego postu. Dzięki!
phant0m
2
Zwykle przywołuję reduceprzykład przeciwny do tego, że zawsze możesz użyć argumentu ze zrozumieniem listy , ponieważ stosunkowo trudno jest zastąpić reducego wbudowanym generatorem lub zrozumieniem listy.
kojiro
4
+1 za zanotowanie preferowanego idiomu danego języka. Spójność IMHO ma ogromną wartość i użycie języka w sposób, w jaki znaczna część „mówców” może zapewnić większą łatwość konserwacji niż czasem nawet komentarze.
Joshua Drake
4
+1 za „On przegląda dokumentację Pythona i wie, jak to działa pięć minut później. W przeciwnym razie nie powinien programować w Pythonie”.
Bjarke Freund-Hansen
25

Ponieważ społeczność programistów odzyskuje zainteresowanie programowaniem funkcjonalnym, nie jest niczym niezwykłym zobaczenie programowania funkcjonalnego w językach, które pierwotnie były w pełni obiektowe. Dobrym przykładem jest C #, w którym typy anonimowe i wyrażenia lambda pozwalają być znacznie krótsze i bardziej ekspresyjne dzięki programowaniu funkcjonalnemu.

To powiedziawszy, programowanie funkcjonalne jest dziwne dla początkujących. Na przykład, kiedy podczas szkolenia wyjaśniłem początkującym, w jaki sposób mogą ulepszyć swój kod poprzez programowanie funkcjonalne w języku C #, niektórzy z nich nie byli przekonani, a niektórzy stwierdzili, że oryginalny kod był dla nich łatwiejszy do zrozumienia. Dałem im przykład:

Kod przed refaktoryzacją:

var categorizedProducts = new Dictionary<string, List<Product>>();

// Get only enabled products, filtering the disabled ones, and group them by categories.
foreach (var product in this.Data.Products)
{
    if (product.IsEnabled)
    {
        if (!categorizedProducts.ContainsKey(product.Category))
        {
            // The category is missing. Create one.
            categorizedProducts.Add(product.Category, new List<Product>());
        }

        categorizedProducts[product.Category].Add(product);
    }
}

// Walk through the categories.
foreach (var productsInCategory in categorizedProducts)
{
    var minimumPrice = double.MaxValue;
    var maximumPrice = double.MinValue;

    // Walk through the products in a category to search for the maximum and minimum prices.
    foreach (var product in productsInCategory.Value)
    {
        if (product.Price < minimumPrice)
        {
            minimumPrice = product.Price;
        }

        if (product.Price > maximumPrice)
        {
            maximumPrice = product.Price;
        }
    }

    yield return new PricesPerCategory(category: productsInCategory.Key, minimum: minimumPrice, maximum: maximumPrice);
}

Ten sam kod po refaktoryzacji przy użyciu FP:

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

To sprawia, że ​​myślę, że:

  • nie powinieneś się martwić, jeśli wiesz, że następny programista, który będzie utrzymywał twój kod, będzie miał wystarczającą ogólną wiedzę i trochę wiedzy na temat programowania funkcjonalnego, ale:

  • w przeciwnym razie unikaj programowania funkcjonalnego lub wstaw swój komentarz wyjaśniający jednocześnie składnię, zalety i możliwe zastrzeżenia twojego podejścia w porównaniu do niefunkcjonalnego programowania.

Arseni Mourzenko
źródło
8
+1, jeśli FP nie uczyni twojego kodu bardziej czytelnym dla nikogo , robisz to źle.
György Andrasek
7
Widzę, jak w izolacji ktoś, kto nigdy wcześniej nie widział FP, mógł uznać pierwszy fragment za bardziej czytelny niż drugi. Ale co, jeśli pomnożymy to przez 100? Czytając krótkie zwięzłe 100 prawie angielsko-jak zdań opisujących co vs tysięcy linii kanalizacji jak kod. Sama objętość kodu, bez względu na to, jak dobrze znana, powinna być tak przytłaczająca, że ​​znajomość nie jest już tak wielką wygraną. W pewnym momencie każdemu musi być łatwiej zaznajomić się z elementami FP, a następnie czytać garść linii zamiast czytać tysiące wierszy znanego kodu.
Esailija,
7
Najbardziej niepokoi mnie to, że jesteśmy zadowoleni, że deweloperzy nie mogą uczyć się funkcjonalnego stylu. Zalety zostały wielokrotnie udowodnione, paradygmat jest wspierany przez języki głównego nurtu. Jeśli ludzie nie są w stanie zrozumieć technik funkcjonalnych, powinni się uczyć lub powinni się uczyć, nawet jeśli nie zamierzają ich używać. Nienawidzę XSLT z płonącą pasją, ale to nie powstrzymało mnie od nauki go jako profesjonalisty.
Sprague,
2
@MainMa Twój punkt widzenia jest całkowicie poprawny, powinienem był być bardziej szczegółowy. Chodzi mi o to, że należy oczekiwać, że programista w danym języku będzie skutecznie korzystać z funkcji tych języków. Na przykład użycie „stylu funkcjonalnego” w języku C # (użycie filtru / mapy / zmniejszenie stylu programowania lub przekazywanie / zwracanie funkcji) nie jest niczym nowym, dlatego uważam, że każdy programista C #, który nie jest w stanie odczytać takich instrukcji, powinien zaktualizować swoje umiejętności ... prawdopodobnie bardzo szybko. Z pewnością uważam, że każdy programista powinien nauczyć się trochę Haskell, ale tylko z powodów akademickich. To inna sprawa.
Sprague,
2
@Praga: Rozumiem twój punkt widzenia i zgadzam się z tym. Po prostu nie możemy się spodziewać na przykład, że programiści C # zaczną używać paradygmatów z FP, kiedy zbyt wielu z nich nie używa nawet generycznych. Dopóki nie będzie tylu niewykwalifikowanych programistów, poprzeczka w naszym zawodzie będzie niska, zwłaszcza, że ​​większość osób bez wiedzy technicznej w ogóle nie rozumie, na czym polega rozwój.
Arseni Mourzenko
20

Nie jestem programistą FP, a ostatnio miałem zmodyfikować kod mojego kolegi w JavaScript. Pojawiło się żądanie HTTP z oddzwonieniem, które wyglądało bardzo podobnie do dołączonego przez ciebie oświadczenia. Muszę powiedzieć, że zajęło mi to trochę czasu (jak pół godziny), aby to wszystko rozgryźć (w sumie kod nie był zbyt duży).

Nie było komentarzy i myślę, że nie było takiej potrzeby. Nawet nie poprosiłem mojego kolegi o pomoc w zrozumieniu jego kodu.

Biorąc pod uwagę, że pracuję od około 1,5 roku, myślę, że większość programistów będzie w stanie zrozumieć i zmodyfikować taki kod, odkąd to zrobiłem.

Poza tym, jak powiedział Joachim Sauer w swoim komentarzu, często są fragmenty FP w wielu językach, na przykład C # (na przykład indexOf). Tak wielu programistów spoza FP zajmuje się tym dość często, a dołączony fragment kodu nie jest czymś strasznym ani niezrozumiałym.

superM
źródło
1
Dzięki za komentarz! Dało mi to więcej informacji na temat innej perspektywy. Łatwo jest stać się domem ślepe, a potem nie wiem, jak to wyglądało, kiedy nie wiem :) FP
kd35a
1
@ kd35a, nie ma za co. Na szczęście mogę być tutaj obiektywny))
superM
1
+1 za wskazanie, że wiele języków zawiera teraz elementy FP.
Joshua Drake
LINQ jest podstawowym przykładem FP w C #
Konrad Morawski
3

Powiedziałbym zdecydowanie tak!

Istnieje wiele aspektów programowania funkcjonalnego, a używanie funkcji wyższego rzędu, jak w twoim przykładzie, jest tylko jednym z nich.

Na przykład uważam, że pisanie czystych funkcji jest niezwykle ważne dla każdego oprogramowania napisanego w dowolnym języku (gdzie przez „czysty” nie mam żadnych skutków ubocznych), ponieważ:

  • są łatwiejsze do testowania jednostkowego
  • są znacznie łatwiejsze do skomponowania niż funkcje niepożądane
  • łatwiej je debugować

Często też unikam mutowania wartości i zmiennych - kolejna koncepcja zapożyczona z FP.

Obie te techniki działają dobrze w języku Python i innych językach, które zwykle nie są klasyfikowane jako funkcjonalne. Często są nawet obsługiwane przez sam język (tj. finalZmienne w Javie). Zatem przyszli programiści zajmujący się konserwacją nie napotkają ogromnej bariery w zrozumieniu kodu.


źródło
2

Tę samą dyskusję przeprowadziliśmy w firmie, w której pracowałem w zeszłym roku.

Dyskusja dotyczyła „magicznego kodu” i tego, czy należy go zachęcać, czy nie. Przyglądając się temu nieco bardziej, wydawało się, że ludzie mieli bardzo różne poglądy na temat tego, co tak naprawdę było „magicznym kodem”. Ci, którzy podjęli dyskusję, zdawali się oznaczać głównie, że wyrażenia (w PHP), które używały stylu funkcjonalnego, były „magicznym kodem”, podczas gdy programiści, którzy wywodzili się z innych języków, którzy używali więcej stylu FP w swoim kodzie, wydają się sądzić, że magiczny kod był raczej kiedy dynamicznie włączasz pliki za pomocą nazw plików i tak dalej.

Nigdy nie doszliśmy do żadnych dobrych wniosków na ten temat, ponadto ludzie w większości uważają, że kod, który wydaje się nieznany, jest „magiczny” lub trudny do odczytania. Czy warto unikać kodu, który nie jest znany innym użytkownikom? Myślę, że to zależy od tego, gdzie jest używane. Powstrzymałbym się od używania wyrażeń w stylu fp (dynamiczne dołączanie plików itp.) W głównej metodzie (lub ważnych centralnych częściach aplikacji), w której dane powinny być tunelowane w jasny i łatwy do odczytania, intuicyjny sposób. Z drugiej strony, nie sądzę, że należy bać się przesunąć kopertę, inni programiści prawdopodobnie szybko nauczą się FP, jeśli napotkają kod FP i mogą mieć jakieś dobre wewnętrzne zasoby do konsultacji w tych kwestiach.

TL; DR: Unikaj w centralnej części aplikacji wysokiego poziomu (które należy przeczytać, aby zapoznać się z przeglądem funkcjonalności aplikacji). W przeciwnym razie użyj go.

Christopher Käck
źródło
Warto unikać używania go w kodzie wyższego poziomu!
kd35a
Zawsze myślałem, że brakuje formalizmu i bardzo dużo okazji w definiowaniu technik programowania w różnych typach bloków (takich jak metody statyczne, metody publiczne, konstruktory, itp.) Dobra odpowiedź ... skłaniam się do ponownej wizyty tych myśli.
Sprague,
2

Społeczność C ++ ostatnio otrzymała również lambda i uważam, że mają mniej więcej to samo pytanie. Odpowiedź może jednak nie być taka sama. Odpowiednikiem C ++ byłoby:

std::copy_if(measures.begin(), measures.end(), inserter(filter),
  [dur](Item i) { return included(i, dur) } );

Teraz std::copynie jest nowy, a _ifwarianty też nie są nowe, ale lambda jest. Jednak jest on zdefiniowany raczej jasno w kontekście: durjest przechwytywany, a zatem stały, Item izmienia się w pętli, a pojedyncza returninstrukcja wykonuje całą pracę.

To wygląda na akceptowalne dla wielu programistów C ++. Nie próbowałem jednak opinii na temat lambda wyższego rzędu i spodziewałbym się znacznie mniejszej akceptacji.

MSalters
źródło
Interesujące jest to, że mogą istnieć różne odpowiedzi w zależności od języka. Prawdopodobnie jest to związane z postem @Christopher Käck o tym, w jaki sposób koderzy PHP mieli więcej problemów z tego rodzaju rzeczami niż kodery Python.
kd35a
0

Opublikuj fragment kodu do innego programisty, który nie jest tak płynny w Pythonie, i zapytaj go, czy może poświęcić 5 minut na sprawdzenie kodu, aby zobaczyć, czy on rozumie.

Jeśli tak, prawdopodobnie dobrze jest iść. Jeśli nie, powinieneś spojrzeć na to, aby było jaśniej.

Czy twój kolega może być głupi i nie rozumieć czegoś, co powinno być oczywiste? Tak, ale zawsze należy programować zgodnie z KISS.

Może Twój kod jest bardziej wydajny / przystojny / elegancki niż prostsze, idiotyczne podejście? Następnie musisz zadać sobie pytanie: czy muszę to zrobić? Ponownie, jeśli odpowiedź brzmi „nie”, nie rób tego!

Jeśli po tym wszystkim nadal uważasz, że potrzebujesz i chcesz to zrobić FP, to zrób to wszystko. Zaufaj swoim instynktom, są one lepiej dostosowane do twoich potrzeb niż większość ludzi na jakimkolwiek forum :)

Arnab Datta
źródło
nie krępuj się wyjaśnić przyczyny przegłosowania
Arnab Datta