В последние несколько лет анонимные функции (лямбда-функции AKA) стали очень популярной языковой конструкцией, и почти каждый основной / основной язык программирования ввел их или планирует ввести их в предстоящем пересмотре стандарта.
Тем не менее, анонимные функции - это очень старая и очень хорошо известная концепция в математике и компьютерных науках (изобретенная математиком Алонзо Черчем около 1936 года и используемая языком программирования Лисп с 1958 года, см., Например, здесь ).
Так почему же современные общепринятые языки программирования (многие из которых возникли 15–20 лет назад) не поддерживают лямбда-функции с самого начала и только вводят их позже?
И что вызвало массовое внедрение анонимных функций в последние несколько лет? Есть ли какое-то конкретное событие, новое требование или метод программирования, которые начали это явление?
ВАЖНАЯ ЗАМЕТКА
Основное внимание в этом вопросе уделяется внедрению анонимных функций в современных основных (и, возможно, за некоторыми исключениями, нефункциональных) языках. Также обратите внимание, что анонимные функции (блоки) присутствуют в Smalltalk, который не является функциональным языком, и что обычные именованные функции присутствовали даже в процедурных языках, таких как C и Pascal, в течение длительного времени.
Пожалуйста, не обобщайте ваши ответы, говоря о «принятии функциональной парадигмы и ее преимуществах», потому что это не тема вопроса.
источник
Ответы:
Конечно, есть заметная тенденция к функциональному программированию или, по крайней мере, к некоторым его аспектам. Некоторые из популярных языков, которые в какой-то момент приняли анонимные функции: C ++ ( C ++ 11 ), PHP ( PHP 5.3.0 ), C # ( C # v2.0 ), Delphi (с 2009 года), Objective C ( блоки ), в то время как Java 8 принесет поддержку лямбда-языку . И есть популярные языки, которые, как правило, не считаются функциональными, но поддерживают анонимные функции с самого начала или, по крайней мере, на раннем этапе, ярким примером является JavaScript.
Как и во всех тенденциях, попытка найти единственное событие, вызвавшее их, вероятно, является пустой тратой времени, обычно это комбинация факторов, большинство из которых не поддаются количественной оценке. Практический Common Lisp , опубликованный в 2005 году, возможно, сыграл важную роль в привлечении нового внимания к Lisp как к практическому языку, поскольку в течение довольно долгого времени Lisp был в основном языком, с которым вы могли бы встретиться в академической среде или на очень специфических нишевых рынках. Популярность JavaScript, возможно, также сыграла важную роль в привлечении нового внимания к анонимным функциям, как объясняет Мунтейл в своем ответе .
Помимо принятия функциональных концепций из многоцелевых языков, также есть заметный сдвиг в сторону функциональных (или в основном функциональных) языков. Такие языки, как Erlang (1986 г.), Haskell (1990 г.), OCaml (1996 г.), Scala (2003 г.), F # (2005 г.), Clojure (2007 г.), и даже специфичные для предметной области языки, такие как R (1993 г.), по-видимому, сильно завоевали популярность. после того, как они были введены. Общая тенденция привлекла новое внимание к более старым функциональным языкам, таким как Scheme (1975) и, очевидно, Common Lisp.
Я думаю, что еще одним важным событием является принятие функционального программирования в отрасли. Я абсолютно не знаю, почему это не имело место, но мне кажется, что в какой-то момент в начале и середине 90-х годов функциональное программирование начало находить свое место в отрасли, начиная (возможно) с распространением Эрланга в телекоммуникации и внедрение Haskell в аэрокосмический и аппаратный дизайн .
Джоэл Спольски написал очень интересный пост в блоге «Опасности JavaSchools» , в котором он опровергает (тогда) тенденцию университетов отдавать предпочтение Java по сравнению с другими, возможно, более сложными в изучении языками. Хотя сообщение в блоге имеет мало общего с функциональным программированием, оно определяет ключевую проблему:
Я до сих пор помню, как сильно я ненавидел Лисп, когда впервые встретил ее в студенческие годы. Это определенно суровая любовница, и это не тот язык, на котором вы можете быть продуктивным (ну, по крайней мере, я не смог). По сравнению с Lisp, Haskell (например) намного более дружелюбен, вы можете работать продуктивно без особых усилий и ощущения себя полным идиотом, что также может быть важным фактором перехода к функциональному программированию.
В общем, это хорошая вещь. Несколько многоцелевых языков принимают концепции парадигмы, которые раньше могли показаться загадочными для большинства их пользователей, и разрыв между основными парадигмами сокращается.
Смежные вопросы:
Дальнейшее чтение:
источник
Мне кажется интересным, насколько популярность функционального программирования сопровождается ростом и распространением Javascript. Javascript имеет много радикальных особенностей в спектре функционального программирования, которые на момент его создания (1995) не были очень популярны среди основных языков программирования (C ++ / Java). Он был внедрен внезапно в мейнстрим как единственный клиентский язык веб-программирования. Внезапно многим программистам просто нужно было знать Javascript, и поэтому вам нужно было кое-что узнать о функциональных возможностях языка программирования.
Интересно, насколько популярными были бы функциональные языки / функции, если бы не внезапный рост Javascript?
источник
JavaScript и DOM обработчики событий означает , что миллионы программистов было узнать по крайней мере немного о первых функций класса для того , чтобы делать какие - либо интерактивности в Интернете.
Оттуда это относительно короткий шаг к анонимным функциям. Поскольку JavaScript не закрывается
this
, он также настоятельно рекомендует вам также узнать о замыканиях. И тогда вы великолепны: вы понимаете анонимные функции первого класса, которые закрывают окружающие лексические области.Как только вы освоитесь с этим, вы захотите использовать его на каждом используемом вами языке.
источник
Это, конечно, не единственный фактор, но я укажу на популярность Ruby. Не сказать, что это важнее, чем любой из шести ответов на доске, но я думаю, что многие вещи произошли одновременно и что полезно перечислить их все.
Ruby не является функциональным языком, и его лямбда-выражения, модули и блоки кажутся неуклюжими, когда вы используете что-то вроде ML, но на самом деле он популяризировал понятие отображения и превращает его в поколение молодых программистов, бегущих из Java и PHP для хипперов. пастбища. Лямбды на нескольких языках кажутся защитными движениями больше всего на свете («Держись! У нас тоже есть!)
Но синтаксис блока и его интеграция с .each, .map, .reduce и т. Д. Привели к появлению идеи анонимной функции, даже если это действительно синтаксическая конструкция, которая ведет себя как сопрограмма. А простое преобразование в процесс через & делает его шлюзом для функционального программирования.
Я утверждаю, что программисты Ruby on Rails, пишущие JavaScript, уже были заняты выполнением задач в легком функциональном стиле. Соедините это с блоггингом программистов, изобретением Reddit, хакерских News и Stack Overflow в одно и то же время, и идеи распространяются по Интернету быстрее, чем во времена новостных групп.
TL; DR: Ruby, Rails, JavaScript, ведение блогов и Reddit / Hacker News / Stack Overflow выдвинули функциональные идеи на массовый рынок, поэтому все хотели, чтобы они существовали на существующих языках, чтобы предотвратить дальнейшие сбои.
источник
Как указал Яннис , существует ряд факторов, которые повлияли на принятие функций высокого порядка в языках, которые ранее не использовались. Одним из важных моментов, которые он лишь слегка коснулся, является распространение многоядерных процессоров и, следовательно, стремление к более параллельной и параллельной обработке.
Стиль функционального программирования map / filter / Reduce очень удобен для распараллеливания, что позволяет программисту легко использовать несколько ядер без написания какого-либо явного кода многопоточности.
Как отмечает Джорджио, функциональное программирование - это нечто большее, чем просто функции высокого порядка. Функции, а также шаблон программирования карта / фильтр / сокращение и неизменность являются ядром функционального программирования. Вместе эти вещи создают мощные инструменты параллельного и параллельного программирования. К счастью, многие языки уже поддерживают некоторое понятие неизменности, и, даже если они этого не делают, программисты могут воспринимать вещи как неизменяемые, позволяя библиотекам и компилятору создавать и управлять асинхронными или параллельными операциями.
Добавление функций высокого порядка к языку является важным шагом для упрощения параллельного программирования.
Обновить
Я добавлю пару более подробных примеров, чтобы решить проблемы, отмеченные Локи.
Рассмотрим следующий код C #, который пересекает коллекцию виджетов, создавая новый список цен на виджеты.
Для большой коллекции виджетов или для вычислительно-интенсивного метода CalculateWidgetPrice (Widget) этот цикл не будет эффективно использовать любые доступные ядра. Чтобы выполнить расчеты цены на разных ядрах, программисту придется явно создавать потоки и управлять ими, обойти работу и собрать результаты вместе.
Рассмотрим решение, как только функции высокого порядка были добавлены в C #:
Цикл foreach был перемещен в метод Select, скрывая детали его реализации. Все, что остается программисту, это сказать Select, какую функцию применять к каждому элементу. Это позволило бы реализации Select выполнять вычисления в parellel, обрабатывая все проблемы синхронизации и управления потоками без участия программиста.
Но, конечно же, Select не работает параллельно. Вот тут-то и возникает неизменность. Реализация Select не знает, что предоставленная функция (CalculateWidgets выше) не имеет побочных эффектов. Функция может изменить состояние программы вне представления Select и его синхронизации, нарушая все. Например, в этом случае значение salesTax может быть изменено по ошибке. Чистые функциональные языки обеспечивают неизменность, поэтому функция Select (map) может точно знать, что состояние не меняется.
C # решает эту проблему, предоставляя PLINQ в качестве альтернативы Linq. Это будет выглядеть так:
Который в полной мере использует все ядра вашей системы без явного управления этими ядрами.
источник
cause
иperceived affect
не объясняетеcorrelation
. Последняя строчка ИМО - о чем вопрос; но ты не ответил на это. Почему это упрощает параллельное программирование?Я согласен со многими ответами здесь, но что интересно, когда я узнал о лямбдах и запрыгнул на них, это было не по какой-либо из причин, упомянутых другими.
Во многих случаях лямбда-функции просто улучшают читабельность вашего кода. Перед лямбдами, когда вы вызываете метод, который принимает указатель на функцию (или функцию, или делегат), вы должны были определить тело этой функции где-то еще, поэтому, когда у вас была конструкция «foreach», ваш читатель должен был перейти к другому часть кода, чтобы увидеть, что именно вы планируете делать с каждым элементом.
Если тело функции, которая обрабатывает элементы, состоит всего из нескольких строк, я бы использовал анонимную функцию, потому что теперь, когда вы читаете код, функциональность остается неизменной, но читателю не нужно переходить назад и вперед, вся реализация прямо перед ним.
Многие из методов функционального программирования и распараллеливания могут быть достигнуты без анонимных функций; просто объявите обычный и передайте ссылку на это всякий раз, когда вам нужно. Но с лямбдами значительно улучшается простота написания кода и простота чтения кода.
источник
Я немного поучаствовал в недавней истории и считаю, что одним из факторов было добавление дженериков в Java и .NET. Это естественно приводит к Func < , > и другим строго типизированным вычислительным абстракциям (Task < >, Async < > и т. Д.)
В мире .NET мы добавили эти функции именно для поддержки FP. Это вызвало каскадный набор языковых работ, связанных с функциональным программированием, особенно C # 3.0, LINQ, Rx и F #. Это развитие повлияло и на другие экосистемы и продолжается до сих пор в C #, F # и TypeScript.
Конечно, это помогает работать на Haskell в MSR :)
Конечно, было и много других влияний (конечно, JS), и на эти шаги, в свою очередь, повлияли многие другие - но добавление дженериков к этим языкам помогло сломать жесткую ортодоксальность OO конца 90-х годов во многих частях мира программного обеспечения и помогло открыть дверь для ФП.
Дон Сайм
ps F # был 2003, а не 2005 - хотя мы бы сказали, что он не достиг 1.0 до 2005. Мы также делали прототип Haskell.NET в 2001-02.
источник
Это не должно быть серьезным ответом, но этот вопрос напомнил мне о забавном юмористическом посте Джеймса Ири - «Краткая, неполная и в основном неправильная история языков программирования», которая включает следующую фразу:
«Лямбды относят к относительной безвестности, пока Java не делает их популярными, не имея их».
источник
Из того, что я вижу, большинство ответов сконцентрировано на объяснении того, почему функциональное программирование в целом вернулось и вошло в мейнстрим. Я чувствовал, что это, однако, на самом деле не отвечает на вопрос об анонимных функциях, в частности, и почему они внезапно стали настолько популярными.
Что действительно завоевало популярность, так это замыкания . Поскольку в большинстве случаев замыкания являются одноразовыми функциями, передаваемыми переменными, очевидно, что для них имеет смысл использовать синтаксис анонимных функций. И на самом деле в некоторых языках это единственный способ создать замыкание.
Почему затворы приобрели популярность? Потому что они полезны в программировании, управляемом событиями, при создании функций обратного вызова . В настоящее время это способ написания клиентского кода JavaScript (фактически это способ написания любого кода GUI). В настоящее время это также способ написания высокоэффективного внутреннего кода, а также системного кода, поскольку код, написанный в парадигме, управляемой событиями, обычно асинхронный и неблокирующий . Для серверной части это стало популярным решением проблемы C10K .
источник
Я думаю, что причина заключается в растущей распространенности параллельного и распределенного программирования, где ядро объектно-ориентированного программирования (инкапсуляция изменяющегося состояния с объектами) больше не применяется. В случае распределенной системы, потому что это не общие государственные (и программные абстракции этой концепции не дырявые) и в случае параллельной системы, так как правильно синхронизировать доступ к общему состоянию был доказана громоздким и подвержены ошибкам. То есть одно из ключевых преимуществ объектно-ориентированного программирования больше не применимо ко многим программам, что делает объектно-ориентированную парадигму гораздо менее полезной, чем раньше.
Напротив, функциональная парадигма не использует изменяемое состояние. Таким образом, любой опыт, полученный нами в отношении функциональных парадигм и паттернов, более быстро переносится на параллельные и распределенные вычисления. И вместо того, чтобы изобретать велосипед, индустрия теперь заимствует эти модели и языковые возможности для удовлетворения своих потребностей.
источник
Если можно прибавить свои 0,02 евро, хотя я согласен с важностью введения этой концепции в JavaScript, я думаю, что вместо параллельного программирования я бы винил нынешний способ асинхронного программирования. При выполнении асинхронных вызовов (необходимых для веб-страниц) простые анонимные функции настолько очевидно полезны, что каждому веб-программисту (то есть каждому программисту) нужно было очень хорошо ознакомиться с этой концепцией.
источник
Другой действительно старый пример чего-то похожего на анонимные функции / лямбды - это вызов по имени в Algol 60. Однако обратите внимание, что вызов по имени ближе к передаче макросов в качестве параметров, чем к передаче истинных функций, и он более хрупок / сложнее понять в результате.
источник
Здесь родословная насколько я знаю.
источник
Анонимные функции хороши тем, что называть вещи сложно, и если вы используете функцию только в одном месте, ей не нужно имя.
Лямбда-функции только недавно стали мейнстримом, потому что до недавнего времени большинство языков не поддерживали замыкания.
Я бы предположил, что Javascript подтолкнул этот мейнстрим. Это универсальный язык, который не может выразить параллелизм, а анонимные функции упрощают использование моделей обратного вызова. Кроме того, такие популярные языки как Ruby и Haskell внесли свой вклад.
источник