W ciągu ostatnich kilku lat anonimowe funkcje (funkcje AKA lambda) stały się bardzo popularnym konstruktem językowym i prawie każdy główny / główny język programowania wprowadził je lub planuje wprowadzić w nadchodzącej rewizji standardu.
Jednak anonimowe funkcje są bardzo starą i bardzo dobrze znaną koncepcją matematyki i informatyki (wymyśloną przez matematyka Alonzo Church około 1936 r. I używaną przez język programowania Lisp od 1958 r., Patrz np. Tutaj ).
Dlaczego więc dzisiejsze główne języki programowania (z których wiele pochodzi od 15 do 20 lat temu) od samego początku nie obsługiwały funkcji lambda i wprowadziły je dopiero później?
A co spowodowało masowe przyjęcie anonimowych funkcji w ciągu ostatnich kilku lat? Czy istnieje jakieś szczególne zdarzenie, nowe wymaganie lub technika programowania, która zapoczątkowała to zjawisko?
WAŻNA UWAGA
Celem tego pytania jest wprowadzenie anonimowych funkcji we współczesnych językach głównego nurtu (a zatem, z kilkoma wyjątkami, niefunkcjonalnych) językach. Zauważ też, że anonimowe funkcje (bloki) są obecne w Smalltalk, który nie jest językiem funkcjonalnym, i że normalnie nazwane funkcje są obecne nawet w językach proceduralnych, takich jak C i Pascal, od dłuższego czasu.
Nie przesadzaj z generalizacją swoich odpowiedzi, mówiąc o „przyjęciu paradygmatu funkcjonalnego i jego zaletach”, ponieważ nie jest to przedmiotem pytania.
źródło
Odpowiedzi:
Z pewnością zauważalny jest trend w kierunku programowania funkcjonalnego lub przynajmniej niektórych jego aspektów. Niektóre popularne języki, które w pewnym momencie przyjęły funkcje anonimowe to C ++ ( C ++ 11 ), PHP ( PHP 5.3.0 ), C # ( C # v2.0 ), Delphi (od 2009), Objective C ( bloki ) podczas Java 8 przyniesie wsparcie dla lambdas w języku . Są też popularne języki, które na ogół nie są uważane za funkcjonalne, ale obsługiwane anonimowe funkcje od samego początku lub przynajmniej na początku, czego świetnym przykładem jest JavaScript.
Podobnie jak w przypadku wszystkich trendów, próba znalezienia jednego zdarzenia, które je wywołało, jest prawdopodobnie stratą czasu, zwykle jest to kombinacja czynników, z których większość nie jest wymierna. Praktyczny Common Lisp , opublikowany w 2005 roku, mógł odegrać ważną rolę w zwróceniu uwagi na Lisp jako język praktyczny , ponieważ przez dłuższy czas Lisp był głównie językiem, który można spotkać w środowisku akademickim lub na bardzo specyficznych niszowych rynkach. Popularność JavaScript mogła również odegrać ważną rolę w zwróceniu uwagi na anonimowe funkcje, jak to wyjaśnia wspaniała odpowiedź .
Oprócz przyjęcia koncepcji funkcjonalnych z języków wielofunkcyjnych, zauważalne jest także przejście do języków funkcjonalnych (lub głównie funkcjonalnych). Języki takie jak Erlang (1986), Haskell (1990), OCaml (1996), Scala (2003), F # (2005), Clojure (2007), a nawet języki specyficzne dla domeny, takie jak R (1993), wydają się zyskać silną pozycję po ich wprowadzeniu. Ogólny trend zwrócił nową uwagę na starsze języki funkcjonalne, takie jak Scheme (1975) i oczywiście Common Lisp.
Myślę, że najważniejszym wydarzeniem jest przyjęcie programowania funkcjonalnego w branży. Absolutnie nie mam pojęcia, dlaczego tak się nie stało, ale wydaje mi się, że w pewnym momencie na początku i w połowie lat 90. funkcjonalne programowanie zaczęło znajdować swoje miejsce w branży, zaczynając (być może) od rozprzestrzeniania się Erlanga w telekomunikacja i przyjęcie Haskell w branży lotniczej i projektowaniu sprzętu .
Joel Spolsky napisał bardzo interesujący post na blogu, The Perils of JavaSchools , w którym opowiada się przeciwko (ówczesnemu) trendowi uniwersytetów, by faworyzować Javę nad innymi, być może trudniejszymi do nauki językami. Chociaż post na blogu ma niewiele wspólnego z programowaniem funkcjonalnym, wskazuje na kluczowy problem:
Nadal pamiętam, jak bardzo nienawidziłem Lisp, kiedy pierwszy raz ją spotkałem podczas studiów. To zdecydowanie surowa kochanka i nie jest to język, w którym można od razu być produktywnym (cóż, przynajmniej ja nie mogłem). W porównaniu do Lispa, Haskell (na przykład) jest znacznie bardziej przyjazny, możesz być produktywny bez większego wysiłku i bez poczucia się jak kompletny idiota, a to może być również ważnym czynnikiem w przejściu na programowanie funkcjonalne.
W sumie to dobra rzecz. Kilka języków wielozadaniowych przyjmuje koncepcje paradygmatu, które mogły wydawać się tajemnicze dla większości ich użytkowników, a przepaść między głównymi paradygmatami się zmniejsza.
Powiązane pytania:
Dalsza lektura:
źródło
Myślę, że interesujące jest to, jak bardzo popularność programowania funkcjonalnego zrównoważyła wzrost i rozpowszechnianie Javascript. JavaScript ma wiele radykalnych cech w spektrum programowania funkcjonalnego, które w momencie jego tworzenia (1995) nie były zbyt popularne wśród głównych języków programowania (C ++ / Java). Został nagle wprowadzony do głównego nurtu jako jedyny język programowania WWW po stronie klienta. Nagle wielu programistów po prostu musiało znać Javascript, dlatego musieliście wiedzieć o funkcjonalnych funkcjach języka programowania.
Zastanawiam się, jak popularne byłyby języki / funkcje funkcjonalne, gdyby nie nagły wzrost Javascript.
źródło
JavaScript i DOM obsługi zdarzeń sprawiły, że miliony programistów musiał nauczyć się przynajmniej trochę o funkcji pierwszej klasy w tym celu dowolny interaktywność w internecie.
Stamtąd jest to stosunkowo krótki krok do anonimowych funkcji. Ponieważ JavaScript się nie zamyka
this
, zdecydowanie zachęca również do zapoznania się z zamknięciami. A potem jesteś złoty: rozumiesz anonimowe funkcje pierwszej klasy, które zamykają się wokół obejmujących zakresy leksykalne.Gdy czujesz się komfortowo, potrzebujesz go w każdym używanym języku.
źródło
Z pewnością nie jest to jedyny czynnik, ale zwrócę uwagę na popularność Ruby. Nie mówię, że jest to ważniejsze niż jakakolwiek z sześciu odpowiedzi już na tablicy, ale myślę, że wiele rzeczy wydarzyło się jednocześnie i że warto je wszystkie wymienić.
Ruby nie jest językiem funkcjonalnym, a jego lambdas, dźgnięcia i bloki wydają się nieporadne, gdy użyłeś czegoś takiego jak ML, ale w rzeczywistości spopularyzował pojęcie mapowania i zredukowania do pokolenia młodych programistów uciekających z Javy i PHP dla hippera pastwiska. Lambdy w kilku językach wydają się bardziej defensywnymi ruchami niż cokolwiek innego („Trzymaj się! Też mamy !!”)
Ale składnia bloku i sposób jej integracji z .each, .map, .reduce i tak dalej upowszechniły ideę anonimowej funkcji, nawet jeśli jest to naprawdę konstrukcja składniowa, która zachowuje się jak coroutine. A łatwa konwersja do proc poprzez i sprawia, że jest to lek bramkowy do programowania funkcjonalnego.
Twierdzę, że programiści Ruby on Rails piszący JavaScript byli już włączeni do robienia rzeczy w lekkim, funkcjonalnym stylu. Połącz to z programowaniem blogów, wynalazkiem Reddit, wiadomościami hakerów i przepełnieniem stosu mniej więcej w tym samym czasie, a pomysły rozprzestrzeniają się szybciej w Internecie niż w czasach grup dyskusyjnych.
TL; DR: Ruby, Rails, JavaScript, blogowanie i Reddit / Hacker News / Stack Overflow wypchnęły funkcjonalne pomysły na masowy rynek, więc wszyscy chcieli je w istniejących językach, aby zapobiec dalszym wadom.
źródło
Jak zauważył Yannis , istnieje wiele czynników, które wpłynęły na przyjęcie funkcji wyższego rzędu w językach, w których wcześniej nie było. Jednym z ważnych elementów, które dotknął jedynie lekko, jest rozprzestrzenianie się procesorów wielordzeniowych, a wraz z tym pragnienie bardziej równoległego i równoległego przetwarzania.
Styl mapowania / filtrowania / zmniejszania programowania funkcjonalnego jest bardzo przyjazny dla równoległości, umożliwiając programistom łatwe korzystanie z wielu rdzeni bez pisania wyraźnego kodu wątkowego.
Jak zauważa Giorgio, funkcjonalne programowanie to coś więcej niż tylko funkcje wyższego rzędu. Funkcje oraz mapa / filtr / redukcja wzorca programowania i niezmienność są rdzeniem programowania funkcjonalnego. Razem te rzeczy tworzą potężne narzędzia programowania równoległego i współbieżnego. Na szczęście wiele języków obsługuje już pewne pojęcie niezmienności, a nawet jeśli nie, programiści mogą traktować rzeczy jako niezmienne, umożliwiając bibliotekom i kompilatorowi tworzenie i zarządzanie operacjami asynchronicznymi lub równoległymi.
Dodanie funkcji wysokiego rzędu do języka jest ważnym krokiem do uproszczenia programowania współbieżnego.
Aktualizacja
Dodam kilka bardziej szczegółowych przykładów, aby odpowiedzieć na obawy Loki.
Rozważ następujący kod C #, który przegląda kolekcję widżetów, tworząc nową listę cen widżetów.
W przypadku dużej kolekcji widżetów lub intensywnej obliczeniowo metody CalculateWidgetPrice (Widget) ta pętla nie wykorzysta dobrze dostępnych rdzeni. Wykonanie obliczeń cen dla różnych rdzeni wymagałoby od programisty jawnego tworzenia wątków i zarządzania nimi, przekazywania pracy i zbierania wyników razem.
Rozważ rozwiązanie po dodaniu funkcji wysokiego rzędu do C #:
Pętla foreach została przeniesiona do metody Select, ukrywając szczegóły jej implementacji. Jedyne, co pozostaje dla programisty, to powiedzieć Wybierz, jaką funkcję zastosować do każdego elementu. Pozwoliłoby to implementacji Select na uruchamianie obliczeń w trybie parellel, zajmując się wszystkimi problemami z synchronizacją i zarządzaniem wątkami bez udziału programisty.
Ale oczywiście Select nie wykonuje swojej pracy równolegle. Tam właśnie pojawia się niezmienność. Implementacja Select nie wie, że podana funkcja (powyżej CalculateWidgets) nie ma skutków ubocznych. Funkcja może zmienić stan programu poza widokiem Select i jego synchronizacją, psując wszystko. Na przykład w tym przypadku wartość saleTax może zostać zmieniona przez pomyłkę. Czyste języki funkcjonalne zapewniają niezmienność, więc funkcja Wybierz (mapa) może mieć pewność, że żaden stan się nie zmienia.
C # rozwiązuje ten problem, dostarczając PLINQ jako alternatywę dla Linq. To wyglądałoby jak:
Który w pełni wykorzystuje wszystkie rdzenie twojego systemu bez wyraźnego zarządzania tymi rdzeniami.
źródło
cause
iperceived affect
bez wyjaśnianiacorrelation
. Ostatnie zdanie IMO dotyczy tego pytania; ale nie odpowiedziałeś na to. Dlaczego upraszcza programowanie współbieżne.Zgadzam się z wieloma odpowiedziami tutaj, ale ciekawe jest to, że kiedy dowiedziałem się o lambdach i wskoczyłem na nie, nie było to z żadnego powodu, o którym wspominali inni.
W wielu przypadkach funkcje lambda po prostu poprawiają czytelność kodu. Przed lambdami, kiedy wywołujesz metodę, która zaakceptowała wskaźnik funkcji (lub funkcję, lub delegację), musiałeś zdefiniować treść tej funkcji gdzieś indziej, więc kiedy miałeś konstrukcję „foreach”, twój czytelnik musiałby przejść do innego część kodu, aby zobaczyć, co dokładnie planujesz zrobić z każdym elementem.
Jeśli treść funkcji przetwarzającej elementy zawiera tylko kilka wierszy, użyłbym funkcji anonimowej, ponieważ teraz, gdy czytasz kod, funkcjonalność pozostaje niezmieniona, ale czytnik nie musi przeskakiwać tam iz powrotem, cała implementacja jest tuż przed nim.
Wiele technik programowania funkcjonalnego i równoległości można by osiągnąć bez anonimowych funkcji; po prostu zadeklaruj zwykły i podaj odniesienie do tego, ilekroć potrzebujesz. Ale dzięki lambdas znacznie poprawiono łatwość pisania kodu i łatwość odczytu kodu.
źródło
Biorąc udział w najnowszej historii tutaj, uważam, że jednym z czynników było dodanie dodatków generycznych do Java i .NET. To oczywiście prowadzi do Func < , > i innych silnie wpisanych abstrakcji obliczeniowych (Zadanie < >, Async < > itp.)
W świecie .NET dodaliśmy te funkcje właśnie w celu obsługi FP. Wywołało to kaskadowy zestaw prac językowych związanych z programowaniem funkcjonalnym, zwłaszcza C # 3.0, LINQ, Rx i F #. Postęp ten wpłynął również na inne ekosystemy i nadal trwa do dziś w C #, F # i TypeScript.
Oczywiście pomaga również Haskellowi pracować w MSR :)
Oczywiście było też wiele innych wpływów (JS na pewno), a na te kroki wpłynęło wiele innych rzeczy - ale dodanie do tych języków generyków pomogło przełamać sztywną ortodoksję OO z późnych lat 90. w dużej części świata oprogramowania i pomogło otworzyć drzwi do FP.
Don Syme
ps F # był 2003, a nie 2005 - choć powiedzielibyśmy, że nie osiągnął 1.0 do 2005 roku. Zrobiliśmy również prototyp Haskell.NET w latach 2001-02.
źródło
To nie jest poważna odpowiedź, ale pytanie przypomniało mi fajny humorystyczny post Jamesa Iry'ego - Krótka, niekompletna i przeważnie zła historia języków programowania, która zawiera następujące zdanie:
„Lambdy są relegowane do względnego zaciemnienia, dopóki Java nie uczyni ich popularnymi przez ich brak”.
źródło
Z tego, co widzę, większość odpowiedzi koncentruje się na wyjaśnieniu, dlaczego programowanie funkcjonalne w ogóle powróciło i stało się głównym nurtem. Czułem, że to jednak tak naprawdę nie odpowiada na pytanie o anonimowe funkcje i dlaczego nagle stały się tak popularne.
To, co naprawdę zyskało na popularności, to zamknięcia . Ponieważ w większości przypadków zamknięciami są funkcje odrzucania przekazywane zmienne, oczywiście ma sens zastosowanie do nich anonimowej składni funkcji. W rzeczywistości w niektórych językach jest to jedyny sposób na zamknięcie.
Dlaczego zamknięcia zyskały popularność? Ponieważ są przydatne w programowaniu sterowanym zdarzeniami, podczas tworzenia funkcji zwrotnych . Obecnie jest to sposób pisania kodu klienta JavaScript (w rzeczywistości jest to sposób pisania dowolnego kodu GUI). Obecnie jest to również sposób pisania wysoce wydajnego kodu zaplecza, a także kodu systemowego, ponieważ kod zapisany w paradygmacie sterowanym zdarzeniami jest zwykle asynchroniczny i nieblokujący . W przypadku back-endu stało się to popularne jako rozwiązanie problemu C10K .
źródło
Myślę, że powodem jest rosnąca częstość programowania współbieżnego i rozproszonego, gdzie rdzeń programowania obiektowego (hermetyzowanie zmieniającego się stanu za pomocą obiektów) nie ma już zastosowania. W przypadku systemu rozproszonego, ponieważ nie ma żadnych dodanych państwowe (i programowe abstrakcje tej koncepcji są nieszczelne), aw przypadku systemu współbieżnego, bo właściwie synchronizowania dostęp do wspólnego państwa Udowodniono uciążliwe i podatne na błędy. Oznacza to, że jedna z kluczowych korzyści programowania obiektowego nie ma już zastosowania do wielu programów, co sprawia, że paradygmat zorientowany obiektowo jest o wiele mniej pomocny niż kiedyś.
W przeciwieństwie do tego, paradygmat funkcjonalny nie wykorzystuje stanu zmiennego. Każde doświadczenie, które zdobyliśmy w zakresie funkcjonalnych paradygmatów i wzorców, jest zatem łatwiejsze do przeniesienia na obliczenia współbieżne i rozproszone. Zamiast na nowo wynaleźć koło, przemysł zapożycza teraz te wzory i funkcje językowe, aby zaspokoić swoje potrzeby.
źródło
Jeśli mogę dodać moje 0,02 €, chociaż zgadzam się ze znaczeniem JavaScript, który wprowadza tę koncepcję, myślę, że bardziej niż równoczesne programowanie obwiniłbym obecny sposób programowania asynchronicznego. Podczas wykonywania wywołań asynchronicznych (niezbędnych na stronach internetowych) proste anonimowe funkcje są tak oczywiście przydatne, że każdy programista WWW (tj. Każdy programista) musiał bardzo dobrze zapoznać się z tą koncepcją.
źródło
Innym naprawdę starym przykładem czegoś podobnego do anonimowych funkcji / lambdas jest call-by-name w Algolu 60. Zauważ jednak, że call-by-name jest bliższy przekazywaniu makr jako parametrów niż przekazywaniu prawdziwych funkcji i jest bardziej kruchy / trudniejszy do rozumiem w rezultacie.
źródło
Oto pochodzenie według mojej najlepszej wiedzy.
źródło
Funkcje anonimowe są fajne, ponieważ nazywanie rzeczy jest trudne, a jeśli używasz funkcji tylko w jednym miejscu, nie potrzebuje ona nazwy.
Funkcje lambda dopiero niedawno stały się głównym nurtem, ponieważ do niedawna większość języków nie obsługiwała zamknięć.
Sugerowałbym, że Javascript wprowadził ten główny nurt. Jest to uniwersalny język, w którym nie ma możliwości wyrażenia równoległości, a anonimowe funkcje ułatwiają korzystanie z modeli wywołań zwrotnych. Dodatkowo przyczyniły się popularne języki, takie jak Ruby i Haskell.
źródło