Po nauczeniu się programowania funkcjonalnego w języku Haskell i F #, paradygmat OOP wydaje się działać wstecz w stosunku do klas, interfejsów, obiektów. Jakie aspekty PR mogę wnieść do pracy, które moi współpracownicy mogą zrozumieć? Czy są jakieś style FP warte rozmowy z moim szefem na temat przekwalifikowania mojego zespołu, abyśmy mogli z nich korzystać?
Możliwe aspekty PR:
- Niezmienność
- Częściowe zastosowanie i curry
- Funkcje pierwszej klasy (wskaźniki funkcji / obiekty funkcjonalne / wzorzec strategii)
- Leniwa ocena (i monady)
- Pure Functions (bez skutków ubocznych)
- Wyrażenia (vs. Instrukcje - każdy wiersz kodu generuje wartość zamiast lub oprócz powodowania efektów ubocznych)
- Rekurencja
- Dopasowywanie wzorów
Czy jest to darmowe rozwiązanie, w którym możemy robić wszystko, co obsługuje język programowania, do poziomu, w jakim język go obsługuje? Czy jest lepsza wytyczna?
Odpowiedzi:
Programowanie funkcjonalne jest innym paradygmatem niż programowanie obiektowe (inny sposób myślenia i inny sposób myślenia o programach). Zacząłeś zdawać sobie sprawę, że jest więcej niż jeden sposób (obiektowy) myślenia o problemach i ich rozwiązaniach. Są inne (przychodzi na myśl programowanie proceduralne i ogólne). To, jak zareagujesz na tę nową wiedzę, niezależnie od tego, czy zaakceptujesz i zintegrujesz te nowe narzędzia i podejścia z zestawem umiejętności, określi, czy rozwijasz się i stajesz się bardziej kompletnym, wykwalifikowanym programistą.
Wszyscy jesteśmy przeszkoleni do obsługi i czujemy się komfortowo z pewnym poziomem złożoności. Lubię nazywać to osoby hrair limitu (od Watership Down, jak wysoko można liczyć). Wspaniale jest rozwinąć swój umysł, zdolność do rozważenia większej liczby opcji i posiadania większej liczby narzędzi do podejścia i rozwiązywania problemów. Ale to zmiana i wyciąga cię ze strefy komfortu.
Jednym z problemów, które możesz napotkać, jest to, że będziesz mniej zadowolony z podążania za tłumem „wszystko jest przedmiotem”. Być może będziesz musiał wykazać się cierpliwością podczas pracy z ludźmi, którzy mogą nie rozumieć (lub chcą zrozumieć), dlaczego funkcjonalne podejście do tworzenia oprogramowania działa dobrze w przypadku niektórych problemów. Podobnie jak ogólne podejście programistyczne działa dobrze w przypadku niektórych problemów.
Powodzenia!
źródło
Programowanie funkcjonalne zapewnia bardzo praktyczną, przyziemną produktywność w codziennym pisaniu kodu: niektóre funkcje sprzyjają zwięzłości, co jest świetne, ponieważ im mniej piszesz kodu, tym mniej błędów popełniasz i wymaga mniej konserwacji.
Jako matematyk uważam, że fantazyjne funkcje są bardzo atrakcyjne, ale zwykle są przydatne podczas projektowania aplikacji: struktury te mogą kodować w strukturze programu wiele niezmienników programu, bez reprezentowania tych niezmienników przez zmienne.
Moja ulubiona kombinacja może wyglądać dość trywialnie, jednak uważam, że ma bardzo duży wpływ na wydajność. Ta kombinacja to Częściowa Aplikacja i Funkcje Currying i Pierwszej Klasy, którą chciałbym ponownie opatrzyć etykietą, nigdy więcej nie napisać pętli for : zamiast tego przekaż ciało pętli do funkcji iteracji lub mapowania. Niedawno zostałem zatrudniony do pracy w C ++ i zabawnie zauważyłem, że całkowicie straciłem nawyk pisania dla pętli!
Połączenie Rekurencji i Dopasowywania Wzorów eliminuje potrzebę tego wzorca projektowego Odwiedzającego . Wystarczy porównać kod potrzebny do zaprogramowania ewaluatora wyrażeń boolowskich: w dowolnym funkcjonalnym języku programowania powinno to być około 15 wierszy kodu, w OOP właściwym rozwiązaniem jest użycie wzorca projektu Visitor , który zamienia ten przykład zabawki w obszerny esej. Korzyści są oczywiste i nie jestem świadomy żadnych niedogodności.
źródło
list.forEach(System.out::println);
z punktu widzenia PR,println
jest funkcją biorąc dwa argumenty, celPrintStream
i wartośćObject
aleCollection
„sforEach
metody oczekuje funkcję z jednym argumentem tylko które można zastosować do każdego elementu. Tak więc pierwszy argument jest powiązany z instancją znajdującą się wSystem.out
nowej funkcji z jednym argumentem. To prostsze niżBiConsumer<…> c=PrintStream::println; PrintStream a1=System.out; list.forEach(a2 -> c.accept(a1, a2));
Być może będziesz musiał ograniczyć to, z jakiej części swojej wiedzy korzystasz w pracy, tak jak Superman udaje, że jest Clarkiem Kentem, aby cieszyć się przywilejami normalnego życia. Ale wiedza więcej nigdy cię nie skrzywdzi. To powiedziawszy, niektóre aspekty programowania funkcjonalnego są odpowiednie dla sklepu zorientowanego obiektowo, a inne aspekty mogą być warte rozmowy z szefem, abyś mógł podnieść średni poziom wiedzy swojego sklepu i dzięki temu napisać lepszy kod.
FP i OOP nie wykluczają się wzajemnie. Spójrz na Scalę. Niektórzy uważają, że jest najgorszy, ponieważ jest to nieczyste FP, ale niektórzy uważają, że jest najlepszy z tego samego powodu.
Oto kilka aspektów, które świetnie współpracują z OOP:
Funkcje Pure (bez skutków ubocznych) - obsługuje każdy język programowania, który znam. Sprawiają, że Twój kod jest o wiele łatwiejszy do rozumowania i powinien być używany, gdy tylko jest to praktyczne. Nie musisz nazywać tego FP. Po prostu nazwij to dobrą praktyką kodowania.
Niezmienność: Ciąg jest prawdopodobnie najczęściej używanym obiektem Java i jest niezmienny. I okładka Niezmienne obiekty Java i niezmienne Java Collections na moim blogu. Niektóre z nich mogą dotyczyć Ciebie.
Funkcje pierwszej klasy (wskaźniki funkcji / obiekty funkcjonalne / wzorzec strategii) - Java ma spłaszczoną, zmutowaną wersję tego od wersji 1.1 z większością klas API (i są ich setki), które implementują interfejs Listener. Runnable jest prawdopodobnie najczęściej używanym obiektem funkcjonalnym. Funkcje pierwszej klasy wymagają więcej pracy przy kodowaniu w języku, który nie obsługuje ich natywnie, ale czasem jest warty dodatkowego wysiłku, gdy upraszczają inne aspekty twojego kodu.
Rekurencja jest przydatna do przetwarzania drzew. W sklepie OOP jest to prawdopodobnie podstawowe właściwe zastosowanie rekurencji. Korzystanie z rekurencji dla zabawy w OOP powinno być raczej obrzydzone, jeśli z innego powodu niż w większości języków OOP domyślnie nie ma miejsca na stos, aby uczynić to dobrym pomysłem.
Wyrażenia (vs. Instrukcje - każdy wiersz kodu wytwarza wartość zamiast lub oprócz powodowania skutków ubocznych) - Jedynym operatorem oceniającym w C, C ++ i Javie jest operator trójskładnikowy . Omawiam odpowiednie użycie na moim blogu. Może się okazać, że piszesz kilka prostych funkcji, które nadają się do wielokrotnego użytku i oceniają.
Leniwa ocena (i monady) - głównie ograniczona do leniwej inicjalizacji w OOP. Bez obsługujących je funkcji językowych możesz znaleźć przydatne interfejsy API, ale napisanie własnego jest trudne. Zamiast tego zmaksymalizuj wykorzystanie strumieni - zobacz przykłady interfejsów Writer i Reader.
Częściowe zastosowanie i curry - niepraktyczne bez funkcji pierwszej klasy.
Dopasowywanie wzorców - ogólnie odradzane w OOP.
Podsumowując, nie uważam, że praca powinna być czymś darmowym, w którym możesz robić wszystko, co obsługuje język programowania, do tego stopnia, że język go obsługuje. Myślę, że czytelność twoich współpracowników powinna być sprawdzianem lakmusowym kodu przygotowanego na wynajem. Tam, gdzie najbardziej cię to denerwuje, chciałbym rozpocząć naukę w pracy, aby poszerzyć horyzonty swoich współpracowników.
źródło
public interface BiConsumer<T, U> { public void accept(T t, U u); }
Istnieją inne przydatne interfejsy funkcjonalne w java.util.function.Most OOP languages don't have the stack space for it
Naprawdę? Wszystko czego potrzebujesz to 30 poziomów rekurencji do zarządzania miliardami węzłów w zrównoważonym drzewie binarnym. Jestem prawie pewien, że moje miejsce na stosie jest odpowiednie na wiele więcej poziomów niż to.Oprócz programowania funkcjonalnego i programowania obiektowego istnieje również programowanie deklaratywne (SQL, XQuery). Poznanie każdego stylu pomaga uzyskać nowe informacje i nauczysz się wybierać odpowiednie narzędzie do pracy.
Ale tak, pisanie kodu w języku może być bardzo frustrujące i wiedzieć, że jeśli używasz czegoś innego, możesz być znacznie bardziej produktywny w przypadku określonej problematycznej domeny. Jednak nawet jeśli używasz języka takiego jak Java, możliwe jest zastosowanie pojęć z FP do kodu Java, aczkolwiek na różne sposoby. Na przykład część Guava robi to częściowo.
źródło
Jako programista uważam, że nigdy nie powinieneś przestać się uczyć. To powiedziawszy, to bardzo interesujące, że uczenie się FP osłabia twoje umiejętności OOP. Uczę się OOP jako nauki jazdy na rowerze; nigdy nie zapomnisz, jak to zrobić.
Kiedy poznałem tajniki FP, zacząłem myśleć bardziej matematycznie i zyskałem lepszą perspektywę środków, w których piszę oprogramowanie. To moje osobiste doświadczenie.
W miarę zdobywania doświadczenia podstawowe koncepcje programowania będą znacznie trudniejsze do stracenia. Sugeruję więc, abyś rozluźnił FP, dopóki koncepcje OOP nie zostaną całkowicie utrwalone w twoim umyśle. FP jest zdecydowaną zmianą paradygmatu. Powodzenia!
źródło
Jest już wiele dobrych odpowiedzi, więc moje odniesie się do części twojego pytania; mianowicie, zastanawiam się nad założeniem twojego pytania, ponieważ OOP i funkcje nie wykluczają się wzajemnie.
Jeśli używasz C ++ 11, istnieje wiele tego rodzaju funkcji programowania funkcjonalnego wbudowanych w bibliotekę językową / standardową, które dobrze (ładnie) współdziałają z OOP. Oczywiście nie jestem pewien, jak dobrze TMP będzie odbierany przez twojego szefa lub współpracowników, ale chodzi o to, że możesz uzyskać wiele z tych funkcji w takiej czy innej formie w niefunkcjonalnych / OOP językach, takich jak C ++.
Używanie szablonów z rekurencją czasu kompilacji zależy od pierwszych 3 punktów,
W tym, że wartości szablonu są niezmienne (stałe czasu kompilacji), każda iteracja jest wykonywana przy użyciu rekurencji, a rozgałęzianie odbywa się przy użyciu (mniej więcej) dopasowywania wzorców, w postaci rozdzielczości przeciążenia.
Jeśli chodzi o inne punkty, użycie
std::bind
istd::function
daje częściową aplikację funkcji, a wskaźniki funkcji są wbudowane w język. Obiekty wywoływalne są obiektami funkcjonalnymi (a także częściową aplikacją funkcji). Zauważ, że przez obiekty, które można wywoływać, mam na myśli te, które je definiująoperator ()
.Leniwa ocena i czyste funkcje byłyby nieco trudniejsze; dla funkcji czystych można użyć funkcji lambda, które przechwytują tylko wartości, ale nie jest to idealne.
Wreszcie, oto przykład użycia rekurencji w czasie kompilacji z aplikacją funkcji częściowej. Jest to nieco wymyślony przykład, ale pokazuje większość powyższych punktów. Rekurencyjnie powiąże wartości w danej krotce z daną funkcją i wygeneruje obiekt funkcji (na żądanie)
źródło