В информатике функциональное программирование — это парадигма программирования , в которой программы создаются путем применения и составления функций . Это парадигма декларативного программирования , в которой определения функций представляют собой деревья выражений , сопоставляющие значения другим значениям, а не последовательность императивных операторов , которые обновляют рабочее состояние программы.
В функциональном программировании функции рассматриваются как первоклассные элементы , то есть их можно привязывать к именам (включая локальные идентификаторы ), передавать в качестве аргументов и возвращать из других функций, как и любой другой тип данных . Это позволяет писать программы в декларативном и компонуемом стиле, где небольшие функции объединяются модульным образом.
Функциональное программирование иногда рассматривается как синоним чисто функционального программирования , подмножества функционального программирования, которое рассматривает все функции как детерминированные математические функции или чистые функции . Когда чистая функция вызывается с некоторыми заданными аргументами, она всегда будет возвращать один и тот же результат, и на нее не могут повлиять какие-либо изменяемые состояния или другие побочные эффекты . Это контрастирует с нечистыми процедурами , распространенными в императивном программировании , которые могут иметь побочные эффекты (например, изменение состояния программы или получение входных данных от пользователя). Сторонники чисто функционального программирования утверждают, что, ограничивая побочные эффекты, программы могут иметь меньше ошибок , их легче отлаживать и тестировать , а также они больше подходят для формальной проверки . [1] [2]
Функциональное программирование уходит своими корнями в академические круги , развившись из лямбда-исчисления , формальной системы вычислений, основанной только на функциях. Функциональное программирование исторически было менее популярным, чем императивное программирование, но многие функциональные языки сегодня находят применение в промышленности и образовании, включая Common Lisp , Scheme , [3] [4] [5] [6] Clojure , Wolfram Language , [7] [8] Racket , [9] Erlang , [10] [11] [12] Elixir , [13] OCaml , [14] [15] Haskell , [16] [17] и F# . [18] [19] Функциональное программирование также является ключом к некоторым языкам, которые добились успеха в определенных областях, таких как JavaScript в Интернете, [20] R в статистике, [21] [22] J , K и Q в финансовом анализе, и XQuery / XSLT для XML . [23] [24] Специализированные декларативные языки, такие как SQL и Lex / Yacc, используют некоторые элементы функционального программирования, например, не позволяют изменять изменяемые значения . [25] Кроме того, многие другие языки программирования поддерживают программирование в функциональном стиле или реализуют функции функционального программирования, такие как C++11 , C# , [26] Kotlin , [27] Perl , [28] PHP , [29] ] Python , [30] Go , [31] Rust , [32] Raku , [33] Scala , [34] и Java (начиная с Java 8) . [35]
Лямбда -исчисление , разработанное в 1930-х годах Алонзо Чёрчем , представляет собой формальную систему вычислений, построенную на основе применения функций . В 1937 году Алан Тьюринг доказал, что лямбда-исчисление и машины Тьюринга являются эквивалентными моделями вычислений, [36] показав, что лямбда-исчисление является полным по Тьюрингу . Лямбда-исчисление лежит в основе всех функциональных языков программирования. Эквивалентная теоретическая формулировка, комбинаторная логика , была разработана Мозесом Шенфинкелем и Хаскеллом Карри в 1920-х и 1930-х годах. [37]
Позже Чёрч разработал более слабую систему — просто типизированное лямбда-исчисление , которое расширило лямбда-исчисление, назначив тип данных всем терминам. [38] Это формирует основу статически типизированного функционального программирования.
Первый язык функционального программирования высокого уровня , Lisp , был разработан в конце 1950-х годов для серии научных компьютеров IBM 700/7000 Джоном Маккарти , работавшим в Массачусетском технологическом институте (MIT). [39] Функции Лиспа были определены с использованием лямбда-нотации Чёрча, дополненной конструкцией метки, позволяющей использовать рекурсивные функции. [40] Лисп впервые представил множество парадигматических особенностей функционального программирования, хотя ранние Лиспы были мультипарадигмальными языками и включали поддержку многочисленных стилей программирования по мере развития новых парадигм. Более поздние диалекты, такие как Scheme и Clojure , а также ответвления, такие как Dylan и Julia , стремились упростить и рационализировать Lisp вокруг чисто функционального ядра, в то время как Common Lisp был разработан для сохранения и обновления парадигматических особенностей многочисленных старых диалектов, которые он заменил. [41]
Язык обработки информации (IPL), 1956 год, иногда называют первым компьютерным языком функционального программирования. [42] Это язык ассемблера для управления списками символов. У него есть понятие генератора , который представляет собой функцию, которая принимает функцию в качестве аргумента, и, поскольку это язык уровня ассемблера, код может быть данными, поэтому IPL можно рассматривать как имеющий функции более высокого порядка. Однако он в значительной степени опирается на структуру изменяющегося списка и аналогичные императивные функции.
Кеннет Э. Айверсон разработал APL в начале 1960-х годов, описанный в его книге 1962 года «Язык программирования» ( ISBN 9780471430148 ). APL оказала основное влияние на FP Джона Бэкуса . В начале 1990-х Айверсон и Роджер Хуэй создали J. В середине 1990-х годов Артур Уитни , ранее работавший с Айверсоном, создал K , который используется коммерчески в финансовых отраслях вместе со своим потомком Q.
В середине 1960-х годов Питер Ландин изобрел SECD-машину , [43] первую абстрактную машину для функционального языка программирования, [44] описал соответствие между АЛГОЛом 60 и лямбда-исчислением , [45] [46] и предложил программирование ISWIM . язык. [47]
Джон Бэкус представил FP в своей лекции на Премии Тьюринга 1977 года «Можно ли программирование освободиться от стиля фон Неймана ? Функциональный стиль и его алгебра программ». [48] Он определяет функциональные программы как построенные иерархически посредством «комбинирования форм», которые позволяют создать «алгебру программ»; на современном языке это означает, что функциональные программы следуют принципу композиционности . [ нужна цитация ] Статья Бэкуса популяризировала исследования в области функционального программирования, хотя в ней особое внимание уделялось программированию на уровне функций , а не стилю лямбда-исчисления, который сейчас ассоциируется с функциональным программированием.
Язык ML был создан Робином Милнером в Эдинбургском университете в 1973 году , а Дэвид Тёрнер разработал язык SASL в Сент-Эндрюсском университете . Также в Эдинбурге в 1970-х Берстолл и Дарлингтон разработали функциональный язык NPL . [49] NPL был основан на уравнениях рекурсии Клини и впервые был использован в их работе по преобразованию программ. [50] Затем Берстолл, Маккуин и Саннелла внедрили проверку полиморфных типов из ML для создания языка Hope . [51] ML со временем развился в несколько диалектов, наиболее распространенными из которых сейчас являются OCaml и Standard ML .
В 1970-х годах Гай Л. Стил и Джеральд Джей Сассман разработали Scheme , как описано в Lambda Papers и учебнике 1985 года « Структура и интерпретация компьютерных программ» . Scheme был первым диалектом Lisp, который использовал лексическую область видимости и требовал оптимизации хвостовых вызовов — функций, которые поощряют функциональное программирование.
В 1980-х годах Пер Мартин-Лёф разработал интуиционистскую теорию типов (также называемую конструктивной теорией типов), которая связывала функциональные программы с конструктивными доказательствами, выраженными как зависимые типы . Это привело к появлению новых подходов к интерактивному доказательству теорем и повлияло на развитие последующих языков функционального программирования. [ нужна цитата ]
Ленивый функциональный язык Miranda , разработанный Дэвидом Тёрнером, первоначально появился в 1985 году и оказал сильное влияние на Haskell . Поскольку Miranda была собственностью компании, в 1987 году Haskell начал с консенсуса по формированию открытого стандарта для исследований в области функционального программирования; выпуски реализации продолжались с 1990 года.
Совсем недавно он нашел применение в таких нишах, как параметрическое САПР на языке OpenSCAD , построенном на платформе CGAL , хотя его ограничение на переназначение значений (все значения рассматриваются как константы) привело к путанице среди пользователей, незнакомых с функциональным программированием как с концепция. [52]
Функциональное программирование продолжает использоваться в коммерческих целях. [53] [54] [55]
Ряд концепций [56] и парадигм специфичны для функционального программирования и вообще чужды императивному программированию (включая объектно-ориентированное программирование ). Однако языки программирования часто соответствуют нескольким парадигмам программирования, поэтому программисты, использующие «в основном императивные» языки, возможно, использовали некоторые из этих концепций. [57]
Функции высшего порядка — это функции, которые могут либо принимать другие функции в качестве аргументов, либо возвращать их в качестве результатов. В исчислении примером функции высшего порядка является дифференциальный оператор , который возвращает производную функции .
Функции высшего порядка тесно связаны с функциями первого класса в том смысле, что функции высшего порядка и функции первого класса допускают использование функций в качестве аргументов и результатов других функций. Различие между ними неуловимо: «высший порядок» описывает математическую концепцию функций, которые оперируют другими функциями, а «первый класс» — это термин информатики, обозначающий сущности языка программирования, которые не имеют ограничений на их использование (таким образом, первый класс). Функции -класса могут появляться в любом месте программы, где могут появляться другие объекты первого класса, такие как числа, в том числе в качестве аргументов других функций и их возвращаемых значений).
Функции высшего порядка допускают частичное применение или каррирование — метод, который применяет функцию к ее аргументам по одному, при этом каждое приложение возвращает новую функцию, которая принимает следующий аргумент. Это позволяет программисту, например, кратко выразить функцию-преемник как оператор сложения, частично примененный к натуральному числу один.
Чистые функции (или выражения) не имеют побочных эффектов (память или ввод-вывод). Это означает, что чистые функции обладают несколькими полезными свойствами, многие из которых можно использовать для оптимизации кода:
Хотя большинство компиляторов императивных языков программирования обнаруживают чистые функции и выполняют исключение общих подвыражений для вызовов чистых функций, они не всегда могут сделать это для предварительно скомпилированных библиотек, которые обычно не раскрывают эту информацию, что предотвращает оптимизацию, включающую эти внешние функции. Некоторые компиляторы, такие как gcc , добавляют дополнительные ключевые слова, позволяющие программисту явно пометить внешние функции как чистые и включить такую оптимизацию. Фортран 95 также позволяет обозначать функции как чистые . [58] В C++11 добавлено constexpr
ключевое слово с похожей семантикой.
Итерация (цикл) в функциональных языках обычно выполняется посредством рекурсии . Рекурсивные функции вызывают сами себя, позволяя операции повторяться до тех пор, пока она не достигнет базового случая . В общем, рекурсия требует поддержания стека , который потребляет пространство в линейном количестве в зависимости от глубины рекурсии. Это может сделать использование рекурсии вместо императивных циклов непомерно дорогим. Однако особая форма рекурсии, известная как хвостовая рекурсия , может быть распознана и оптимизирована компилятором в тот же код, который используется для реализации итерации в императивных языках. Оптимизацию хвостовой рекурсии можно реализовать, среди прочего, путем преобразования программы в стиль передачи продолжения во время компиляции.
Стандарт языка Scheme требует, чтобы реализации поддерживали правильную хвостовую рекурсию, то есть они должны допускать неограниченное количество активных хвостовых вызовов. [59] [60] Правильная хвостовая рекурсия — это не просто оптимизация; это языковая функция, которая гарантирует пользователям, что они могут использовать рекурсию для выражения цикла, и это будет безопасно для пространства. [61] Более того, вопреки названию, он учитывает все хвостовые вызовы, а не только хвостовую рекурсию. Хотя правильная хвостовая рекурсия обычно реализуется путем превращения кода в императивные циклы, реализации могут реализовывать ее другими способами. Например, Chicken намеренно поддерживает стек и позволяет ему переполниться . Однако, когда это произойдет, его сборщик мусора потребует пространство обратно, [62] позволяя неограниченное количество активных хвостовых вызовов, даже если он не превращает хвостовую рекурсию в цикл.
Общие шаблоны рекурсии можно абстрагировать с помощью функций более высокого порядка, причем наиболее очевидными примерами являются катаморфизмы и анаморфизмы (или «складки» и «развертывания»). Такие рекурсивные схемы играют роль, аналогичную встроенным структурам управления, таким как циклы в императивных языках .
Большинство языков функционального программирования общего назначения допускают неограниченную рекурсию и являются полными по Тьюрингу , что делает проблему остановки неразрешимой , может вызвать необоснованность эквациональных рассуждений и обычно требует внесения несогласованности в логику, выраженную системой типов языка . Некоторые языки специального назначения, такие как Coq , допускают только обоснованную рекурсию и строго нормализуют (непрерывные вычисления могут быть выражены только с помощью бесконечных потоков значений, называемых кодата ). Как следствие, эти языки не являются полными по Тьюрингу, и выражение в них определенных функций невозможно, но они все же могут выражать широкий класс интересных вычислений, избегая при этом проблем, возникающих из-за неограниченной рекурсии. Функциональное программирование, ограниченное обоснованной рекурсией с некоторыми другими ограничениями, называется полным функциональным программированием . [63]
Функциональные языки можно разделить на категории по тому, используют ли они строгие (торопливые) или нестрогие (ленивые) оценки — концепции, которые относятся к тому, как обрабатываются аргументы функции при вычислении выражения. Техническое различие заключается в денотационной семантике выражений, содержащих неудачные или расходящиеся вычисления. При строгой оценке оценка любого термина, содержащего неверный подтерм, не удалась. Например, выражение:
длина печати([2+1, 3*2, 1/0, 5-4])
не проходит строгую оценку из-за деления на ноль в третьем элементе списка. При ленивом вычислении функция длины возвращает значение 4 (т. е. количество элементов в списке), поскольку при ее вычислении не предпринимаются попытки оценить термины, составляющие список. Короче говоря, строгая оценка всегда полностью оценивает аргументы функции перед ее вызовом. Отложенная оценка не оценивает аргументы функции, если только их значения не требуются для оценки самого вызова функции.
Обычная стратегия реализации ленивых вычислений в функциональных языках — сокращение графа . [64] Отложенное вычисление используется по умолчанию в нескольких чисто функциональных языках, включая Miranda , Clean и Haskell .
Хьюз 1984 утверждает, что отложенное вычисление является механизмом улучшения модульности программы за счет разделения задач и облегчения независимой реализации производителей и потребителей потоков данных. [2] Launchbury 1993 описывает некоторые трудности, которые создает ленивая оценка, особенно при анализе требований программы к памяти, и предлагает операционную семантику , помогающую в таком анализе. [65] Харпер 2009 предлагает включать в один и тот же язык как строгую, так и ленивую оценку, используя систему типов языка для их различения. [66]
Особенно после разработки вывода типа Хиндли-Милнера в 1970-х годах, языки функционального программирования имеют тенденцию использовать типизированное лямбда-исчисление , отклоняя все недопустимые программы во время компиляции и рискуя ложноположительными ошибками , в отличие от нетипизированного лямбда-исчисления , которое принимает все допустимые ошибки. программ во время компиляции и рискует ложноотрицательными ошибками , используемыми в Lisp и его вариантах (таких как Scheme ), поскольку они отклоняют все недопустимые программы во время выполнения, когда информации достаточно, чтобы не отклонять действительные программы. Использование алгебраических типов данных делает удобным манипулирование сложными структурами данных; наличие строгой проверки типов во время компиляции делает программы более надежными при отсутствии других методов обеспечения надежности, таких как разработка через тестирование , в то время как вывод типов освобождает программиста от необходимости вручную объявлять типы компилятору в большинстве случаев.
Некоторые ориентированные на исследования функциональные языки, такие как Coq , Agda , Cayenne и Epigram , основаны на интуиционистской теории типов , которая позволяет типам зависеть от термов. Такие типы называются зависимыми типами . Эти системы типов не имеют разрешимого вывода типов, их трудно понять и программировать. [67] [68] [69] [70] Но зависимые типы могут выражать произвольные предложения в логике высшего порядка . Таким образом, благодаря изоморфизму Карри-Говарда хорошо типизированные программы на этих языках становятся средством написания формальных математических доказательств , на основе которых компилятор может генерировать сертифицированный код . Хотя эти языки представляют в основном интерес для академических исследований (в том числе в формализованной математике ), они начали использоваться и в технике. Compcert — это компилятор подмножества языка программирования C , написанного на Coq и формально проверенного. [71]
Ограниченная форма зависимых типов, называемая обобщенными алгебраическими типами данных (GADT), может быть реализована таким образом, чтобы обеспечить некоторые преимущества зависимо типизированного программирования, избегая при этом большинства его неудобств. [72] GADT доступны в компиляторе Glasgow Haskell , в OCaml [73] и в Scala , [74] и были предложены в качестве дополнения к другим языкам, включая Java и C#. [75]
В функциональных программах нет операторов присваивания, то есть значение переменной в функциональной программе никогда не меняется после ее определения. Это исключает любые побочные эффекты, поскольку любую переменную можно заменить ее фактическим значением в любой точке выполнения. Итак, функциональные программы ссылочно прозрачны. [76]
Рассмотрим оператор присваивания Cx=x * 10
, он изменяет значение, присвоенное переменной x
. Допустим, начальное значение x
было 1
, затем два последовательных вычисления переменной x
дают 10
и 100
соответственно. Очевидно, что замена x=x * 10
на 10
или 100
придает программе другое значение, и поэтому выражение не является ссылочно прозрачным. Фактически, операторы присваивания никогда не бывают ссылочно прозрачными.
Теперь рассмотрим другую функцию, например, прозрачную , поскольку она не меняет неявно входной сигнал x и, следовательно, не имеет таких побочных эффектов . Функциональные программы используют исключительно функции этого типа и поэтому являются ссылочно прозрачными.int plusone(int x) {return x+1;}
Чисто функциональные структуры данных часто представляются иначе, чем их императивные аналоги. [77] Например, массив с постоянным временем доступа и обновления является базовым компонентом большинства императивных языков, а многие императивные структуры данных, такие как хеш- таблица и двоичная куча , основаны на массивах. Массивы можно заменить картами или списками произвольного доступа, которые допускают чисто функциональную реализацию, но имеют логарифмическое время доступа и обновления. Чисто функциональные структуры данных обладают постоянством — свойством сохранять предыдущие версии структуры данных неизменными. В Clojure постоянные структуры данных используются как функциональные альтернативы своим императивным аналогам. Например, постоянные векторы используют деревья для частичного обновления. Вызов метода вставки приведет к созданию некоторых, но не всех узлов. [78]
Функциональное программирование сильно отличается от императивного программирования . Наиболее существенные различия связаны с тем, что функциональное программирование позволяет избежать побочных эффектов , которые используются в императивном программировании для реализации состояния и ввода-вывода. Чисто функциональное программирование полностью предотвращает побочные эффекты и обеспечивает ссылочную прозрачность.
Функции высшего порядка редко используются в старых императивных программах. Традиционная императивная программа может использовать цикл для обхода и изменения списка. С другой стороны, функциональная программа, вероятно, будет использовать функцию «карты» более высокого порядка, которая принимает функцию и список, генерирует и возвращает новый список, применяя функцию к каждому элементу списка.
Следующие два примера (написанные на JavaScript ) достигают одного и того же эффекта: они умножают все четные числа в массиве на 10 и складывают их все, сохраняя окончательную сумму в переменной «result».
Традиционный императивный цикл:
const numList = [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ]; пусть результат = 0 ; for ( let i = 0 ; i < numList . length ; i ++ ) { if ( numList [ i ] % 2 === 0 ) { result += numList [ i ] * 10 ; } }
Функциональное программирование с функциями высшего порядка:
константный результат = [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ] . фильтр ( n => n % 2 === 0 ) . карта ( а => а * 10 ) . уменьшить (( a , b ) => a + b , 0 );
Есть задачи (например, поддержание баланса банковского счета), которые часто кажутся наиболее естественными для реализации с помощью государства. Чистое функциональное программирование выполняет эти задачи, а также задачи ввода-вывода, такие как прием пользовательского ввода и вывод на экран, другим способом.
Чисто функциональный язык программирования Haskell реализует их с использованием монад , полученных из теории категорий . [79] Монады предлагают способ абстрагировать определенные типы вычислительных шаблонов, включая (но не ограничиваясь) моделирование вычислений с изменяемым состоянием (и другими побочными эффектами, такими как ввод-вывод) императивным образом без потери чистоты. Хотя существующие монады можно легко применить в программе при наличии соответствующих шаблонов и примеров, многим студентам их трудно понять концептуально, например, когда их просят определить новые монады (что иногда необходимо для определенных типов библиотек). [80]
Функциональные языки также моделируют состояния, передавая неизменяемые состояния. Это можно сделать, заставив функцию принимать состояние в качестве одного из своих параметров и возвращать новое состояние вместе с результатом, оставляя старое состояние неизменным. [81]
Нечистые функциональные языки обычно включают более прямой метод управления изменяемым состоянием. Clojure , например, использует управляемые ссылки, которые можно обновлять, применяя чистые функции к текущему состоянию. Такой подход обеспечивает возможность изменения, но при этом способствует использованию чистых функций в качестве предпочтительного способа выражения вычислений. [ нужна цитата ]
Альтернативные методы, такие как логика Хоара и уникальность, были разработаны для отслеживания побочных эффектов в программах. Некоторые современные исследовательские языки используют системы эффектов , чтобы сделать присутствие побочных эффектов явным. [ нужна цитата ]
Функциональные языки программирования обычно менее эффективны в использовании процессора и памяти, чем императивные языки, такие как C и Pascal . [82] Это связано с тем фактом, что некоторые изменяемые структуры данных, такие как массивы, имеют очень простую реализацию с использованием существующего оборудования. К плоским массивам можно очень эффективно обращаться с помощью процессоров с глубокой конвейеризацией, эффективно выполнять предварительную выборку через кэши (без сложной погони за указателями) или обрабатывать их с помощью SIMD-инструкций. Также непросто создать их столь же эффективные неизменяемые аналоги общего назначения. Для чисто функциональных языков наихудшее замедление является логарифмическим по количеству используемых ячеек памяти, поскольку изменяемая память может быть представлена чисто функциональной структурой данных с логарифмическим временем доступа (например, сбалансированным деревом). [83] Однако такое замедление не является универсальным. По данным The Computer Language Benchmarks Game, для программ, выполняющих интенсивные числовые вычисления, функциональные языки, такие как OCaml и Clean , лишь немного медленнее, чем C. [84] Для программ, которые обрабатывают большие матрицы и многомерные базы данных , были разработаны функциональные языки работы с массивами (такие как J и K ) с оптимизацией скорости.
Неизменяемость данных во многих случаях может привести к повышению эффективности выполнения, позволяя компилятору делать предположения, которые небезопасны в императивном языке, тем самым увеличивая возможности для встроенного расширения . [85]
Ленивые вычисления также могут ускорить работу программы, даже асимптотически, тогда как они могут замедлить ее максимум на постоянный коэффициент (однако при неправильном использовании они могут привести к утечкам памяти ). Launchbury 1993 [65] обсуждает теоретические проблемы, связанные с утечками памяти из-за ленивых вычислений, а O'Sullivan et al. 2008 [86] дают несколько практических советов по их анализу и исправлению. Однако наиболее общие реализации ленивых вычислений, широко использующие разыменованный код и данные , плохо работают на современных процессорах с глубокими конвейерами и многоуровневыми кэшами (где промах в кэше может стоить сотен циклов ) .
Функциональный стиль программирования можно использовать на языках, которые традиционно не считаются функциональными. [87] Например, и D [88] , и Fortran 95 [58] явно поддерживают чистые функции.
JavaScript , Lua , [89] Python и Go [90] с самого начала имели первоклассные функции . [91] Python имел поддержку « лямбда », « map », « reduce » и « filter » в 1994 году, а также замыканий в Python 2.2, [92] хотя Python 3 отнес «reduce» к functools
стандартному библиотечному модулю. [93] Первоклассные функции были введены в другие основные языки, такие как PHP 5.3, Visual Basic 9 , C# 3.0, C++11 и Kotlin . [27] [ нужна ссылка ]
В PHP полностью поддерживаются анонимные классы , замыкания и лямбды. Библиотеки и языковые расширения для неизменяемых структур данных разрабатываются, чтобы облегчить программирование в функциональном стиле.
В Java анонимные классы иногда могут использоваться для имитации замыканий; [94] однако анонимные классы не всегда являются подходящей заменой замыканиям, поскольку их возможности более ограничены. [95] Java 8 поддерживает лямбда-выражения в качестве замены некоторых анонимных классов. [96]
В C# анонимные классы не нужны, поскольку замыкания и лямбда-выражения полностью поддерживаются. Библиотеки и языковые расширения для неизменяемых структур данных разрабатываются для облегчения программирования в функциональном стиле на C#.
Многие шаблоны объектно-ориентированного проектирования выражаются в терминах функционального программирования: например, шаблон стратегии просто диктует использование функции высшего порядка, а шаблон посетителя примерно соответствует катаморфизму или свертыванию .
Аналогичным образом, идея неизменяемых данных из функционального программирования часто включается в императивные языки программирования, [97] например, кортеж в Python, который представляет собой неизменяемый массив, и Object.freeze() в JavaScript. [98]
Логическое программирование можно рассматривать как обобщение функционального программирования, в котором функции являются частным случаем отношений. [99] Например, функция мать(X) = Y (каждый X имеет только одну мать Y) может быть представлена отношением мать(X, Y). В то время как функции имеют строгий шаблон ввода-вывода аргументов, отношения могут быть запрошены с любым шаблоном входов и выходов. Рассмотрим следующую логическую программу:
мать ( Чарльз , Элизабет ). мать ( Гарри , Диана ).
Программу можно запросить, как функциональную программу, для создания матерей из детей:
?- мать ( Гарри , Икс ). Х = Диана . ?- мать ( Чарльз , X ). Х = Элизабет .
Но его также можно запросить в обратном направлении , чтобы сгенерировать дочерние элементы:
?- мать ( X , Элизабет ). Х = Чарльз . ?- мать ( X , Диана ). Х = Гарри .
Его даже можно использовать для генерации всех экземпляров материнского отношения:
?- мать ( X , Y ). X = Чарльз , Y = Элизабет . X = Гарри , Y = Диана .
По сравнению с реляционным синтаксисом функциональный синтаксис является более компактной записью вложенных функций. Например, определение бабушки по материнской линии в функциональном синтаксисе можно записать во вложенной форме:
maternal_grandmother ( X ) = мать ( мать ( X )).
Это же определение в реляционной нотации нужно записать в невложенной форме:
maternal_grandmother ( X , Y ) :- мать ( X , Z ), мать ( Z , Y ).
Здесь :-
означает если и ,
означает и .
Однако разница между этими двумя представлениями просто синтаксическая. В Ciao Prolog отношения могут быть вложенными, как функции в функциональном программировании: [100]
дедушка ( X ) := родитель ( родитель ( X )). родитель ( X ) := мать ( X ). родитель ( X ) := отец ( X ).мать ( Чарльз ) := Элизабет . отец ( Чарльз ) := Филипп . мать ( Гарри ) := Диана . отец ( Гарри ) := Чарльз .?- дедушка ( X , Y ). X = Гарри , Y = Элизабет . X = Гарри , Y = Филипп .
Чао преобразует подобную функции запись в реляционную форму и выполняет полученную логическую программу, используя стандартную стратегию выполнения Пролога.
Электронные таблицы можно рассматривать как форму чистой системы функционального программирования нулевого порядка со строгой оценкой. [101] Однако в электронных таблицах обычно отсутствуют функции высшего порядка, а также возможность повторного использования кода, а в некоторых реализациях также отсутствует рекурсия. Для программ работы с электронными таблицами было разработано несколько расширений, позволяющих использовать функции более высокого порядка и многократного использования, но пока они остаются в основном академическими по своему характеру. [102]
Функциональное программирование — активная область исследований в области теории языков программирования . Существует несколько рецензируемых площадок для публикаций, посвященных функциональному программированию, в том числе Международная конференция по функциональному программированию , Журнал функционального программирования и Симпозиум по тенденциям в функциональном программировании .
Функциональное программирование используется в широком спектре промышленных приложений. Например, Erlang , разработанный шведской компанией Ericsson в конце 1980-х годов, первоначально использовался для реализации отказоустойчивых телекоммуникационных систем [11] , но с тех пор стал популярен для создания ряда приложений в таких компаниях, как Nortel , Facebook . , Электрисите де Франс и WhatsApp . [10] [12] [103] [104] [105] Scheme , диалект Lisp , использовался в качестве основы для нескольких приложений на ранних компьютерах Apple Macintosh [3] [4] и применялся для решения таких задач, как обучение - программное обеспечение для моделирования [5] и управления телескопом . [6] OCaml , представленный в середине 1990-х годов, нашел коммерческое применение в таких областях, как финансовый анализ, [14] проверка драйверов , программирование промышленных роботов и статический анализ встроенного программного обеспечения . [15] Haskell , хотя изначально задумывался как исследовательский язык, [17] также применялся в таких областях, как аэрокосмические системы, проектирование аппаратного обеспечения и веб-программирование. [16] [17]
Другие языки функционального программирования, которые нашли применение в промышленности, включают Scala , [106] F# , [18] [19] Wolfram Language , [7] Lisp , [107] Standard ML [108] [109] и Clojure. [110]
Функциональные «платформы» популярны в финансовой сфере для анализа рисков (особенно среди крупных инвестиционных банков). Факторы риска кодируются как функции, которые образуют взаимозависимые графики (категории) для измерения корреляций в рыночных сдвигах, аналогично оптимизации на основе Грёбнера , но также и для нормативных рамок, таких как комплексный анализ и обзор капитала . Учитывая использование вариаций OCaml и Caml в финансах, эти системы иногда считают связанными с категориальной абстрактной машиной . Функциональное программирование находится под сильным влиянием теории категорий . [ нужна цитата ]
Во многих университетах преподают функциональное программирование. [111] [112] [113] [114] Некоторые рассматривают это как вводную концепцию программирования [114] , в то время как другие сначала обучают методам императивного программирования. [113] [115]
Помимо информатики, функциональное программирование используется для обучения решению задач, алгебраическим и геометрическим понятиям. [116] Его также использовали для преподавания классической механики, как в книге « Структура и интерпретация классической механики» .
Haskell имеет широкий спектр коммерческого использования: от аэрокосмической и оборонной промышленности до финансов, веб-стартапов, фирм по разработке оборудования и производителей газонокосилок.
{{cite web}}
: CS1 maint: numeric names: authors list (link)Эффективная Скала.
{{cite journal}}
: Требуется цитировать журнал |journal=
( помощь ){{cite journal}}
: Требуется цитировать журнал |journal=
( помощь ){{cite book}}
: |work=
игнорируется ( помощь ) источник цитированияCS1 maint: location missing publisher (link)Метод Object.freeze() замораживает объект. Замороженный объект больше нельзя изменить; Замораживание объекта предотвращает добавление к нему новых свойств, удаление существующих свойств, предотвращает изменение перечисляемости, возможности настройки или записи существующих свойств, а также предотвращает изменение значений существующих свойств. Кроме того, замораживание объекта также предотвращает изменение его прототипа. Freeze() возвращает тот же объект, который был передан.