Programación funcional

Compartir Imprimir Citar

En informática, la programación funcional es un paradigma de programación donde los programas se construyen aplicando y componiendo funciones. Es un paradigma de programación declarativa en el que las definiciones de funciones son árboles de expresiones que asignan valores a otros valores, en lugar de una secuencia de declaraciones imperativas que actualizan el estado de ejecución del programa.

En la programación funcional, las funciones se tratan como ciudadanos de primera clase, lo que significa que pueden vincularse a nombres (incluidos los identificadores locales), pasarse como argumentos y devolverse desde otras funciones, al igual que cualquier otro tipo de datos. Esto permite que los programas se escriban en un estilo declarativo y componible, donde las funciones pequeñas se combinan de manera modular.

La programación funcional a veces se trata como sinónimo de programación puramente funcional, un subconjunto de la programación funcional que trata todas las funciones como funciones matemáticas deterministas o funciones puras. Cuando se llama a una función pura con algunos argumentos dados, siempre devolverá el mismo resultado y no se verá afectada por ningún estado mutable u otros efectos secundarios. Esto contrasta con los procedimientos impuros, comunes en la programación imperativa, que pueden tener efectos secundarios (como modificar el estado del programa o recibir información de un usuario). Los defensores de la programación puramente funcional afirman que al restringir los efectos secundarios, los programas pueden tener menos errores, ser más fáciles de depurar y probar y ser más adecuados para la verificación formal.

La programación funcional tiene sus raíces en la academia, evolucionando del cálculo lambda, un sistema formal de computación basado solo en funciones. Históricamente, la programación funcional ha sido menos popular que la programación imperativa, pero muchos lenguajes funcionales se utilizan hoy en día en la industria y la educación, incluidos Common Lisp, Scheme, Clojure, Wolfram Language, Racket, Erlang, Elixir, OCaml, Haskell y F#. La programación funcional también es clave para algunos lenguajes que han tenido éxito en dominios específicos, como JavaScript en la Web, R en estadísticas, J, K y Q en análisis financiero y XQuery/XSLT para XML.Los lenguajes declarativos específicos de dominio como SQL y Lex/Yacc usan algunos elementos de programación funcional, como no permitir valores mutables. Además, muchos otros lenguajes de programación admiten la programación en un estilo funcional o han implementado características de la programación funcional, como C++11, C#, Kotlin, Perl, PHP, Python, Go, Rust, Raku, Scala y Java (desde JAVA 8).

Historia

El cálculo lambda, desarrollado en la década de 1930 por Alonzo Church, es un sistema formal de cálculo construido a partir de la aplicación de funciones. En 1937, Alan Turing demostró que el cálculo lambda y las máquinas de Turing son modelos equivalentes de computación, demostrando que el cálculo lambda es Turing completo. El cálculo lambda constituye la base de todos los lenguajes de programación funcionales. Moses Schönfinkel y Haskell Curry desarrollaron una formulación teórica equivalente, la lógica combinatoria, en las décadas de 1920 y 1930.

Más tarde, Church desarrolló un sistema más débil, el cálculo lambda de tipo simple, que amplió el cálculo lambda asignando un tipo a todos los términos. Esto forma la base para la programación funcional estáticamente tipada.

El primer lenguaje de programación funcional de alto nivel, LISP, fue desarrollado a fines de la década de 1950 para la serie IBM 700/7000 de computadoras científicas por John McCarthy mientras trabajaba en el Instituto de Tecnología de Massachusetts (MIT). Las funciones LISP se definieron utilizando la notación lambda de Church, ampliada con una construcción de etiqueta para permitir funciones recursivas.Lisp introdujo por primera vez muchas características paradigmáticas de la programación funcional, aunque los primeros Lisps eran lenguajes de múltiples paradigmas e incorporaron soporte para numerosos estilos de programación a medida que evolucionaban nuevos paradigmas. Dialectos posteriores, como Scheme y Clojure, y ramificaciones como Dylan y Julia, buscaron simplificar y racionalizar Lisp en torno a un núcleo funcional limpio, mientras que Common Lisp fue diseñado para preservar y actualizar las características paradigmáticas de los numerosos dialectos más antiguos que reemplazó.

El lenguaje de procesamiento de información (IPL), 1956, a veces se cita como el primer lenguaje de programación funcional basado en computadora. Es un lenguaje de estilo ensamblador para manipular listas de símbolos. Tiene una noción de generador, que equivale a una función que acepta una función como argumento y, dado que es un lenguaje de nivel ensamblador, el código puede ser datos, por lo que se puede considerar que IPL tiene funciones de orden superior. Sin embargo, se basa en gran medida en la estructura de la lista mutante y características imperativas similares.

Kenneth E. Iverson desarrolló APL a principios de la década de 1960, descrito en su libro de 1962 Un lenguaje de programación (ISBN 9780471430148). APL fue la principal influencia en la FP de John Backus. A principios de la década de 1990, Iverson y Roger Hui crearon J. A mediados de la década de 1990, Arthur Whitney, que había trabajado anteriormente con Iverson, creó K, que se usa comercialmente en las industrias financieras junto con su descendiente Q.

John Backus presentó FP en su conferencia del Premio Turing de 1977 "¿Se puede liberar la programación del estilo von Neumann? Un estilo funcional y su álgebra de programas". Define los programas funcionales como construidos de forma jerárquica mediante "formas combinadas" que permiten un "álgebra de programas"; en lenguaje moderno, esto significa que los programas funcionales siguen el principio de composicionalidad. El artículo de Backus popularizó la investigación sobre programación funcional, aunque enfatizó la programación a nivel de función en lugar del estilo de cálculo lambda ahora asociado con la programación funcional.

El lenguaje ML de 1973 fue creado por Robin Milner en la Universidad de Edimburgo, y David Turner desarrolló el lenguaje SASL en la Universidad de St Andrews. También en Edimburgo en la década de 1970, Burstall y Darlington desarrollaron el lenguaje funcional NPL. NPL se basó en Kleene Recursion Equations y se introdujo por primera vez en su trabajo sobre transformación de programas. Burstall, MacQueen y Sannella luego incorporaron la verificación de tipos polimórficos de ML para producir el lenguaje Hope. ML eventualmente se convirtió en varios dialectos, los más comunes ahora son OCaml y Standard ML.

En la década de 1970, Guy L. Steele y Gerald Jay Sussman desarrollaron Scheme, como se describe en Lambda Papers y en el libro de texto de 1985 Structure and Interpretation of Computer Programs. Scheme fue el primer dialecto de lisp en usar alcance léxico y requerir optimización de llamadas de cola, características que fomentan la programación funcional.

En la década de 1980, Per Martin-Löf desarrolló la teoría de tipos intuicionista (también llamada teoría de tipos constructiva), que asociaba programas funcionales con pruebas constructivas expresadas como tipos dependientes. Esto condujo a nuevos enfoques para la demostración interactiva de teoremas y ha influido en el desarrollo de lenguajes de programación funcionales posteriores.

El lenguaje funcional perezoso, Miranda, desarrollado por David Turner, apareció inicialmente en 1985 y tuvo una fuerte influencia en Haskell. Con Miranda siendo propietario, Haskell comenzó con un consenso en 1987 para formar un estándar abierto para la investigación de programación funcional; Las versiones de implementación han estado en curso desde 1990.

Más recientemente, ha encontrado uso en nichos como CAD paramétrico, cortesía del lenguaje OpenSCAD creado en el marco de geometría CSG, aunque su restricción en la reasignación de valores (todos los valores se tratan como constantes) ha generado confusión entre los usuarios que no están familiarizados con la programación funcional. como concepto.

La programación funcional continúa utilizándose en entornos comerciales.

Conceptos

Una serie de conceptos y paradigmas son específicos de la programación funcional y, en general, ajenos a la programación imperativa (incluida la programación orientada a objetos). Sin embargo, los lenguajes de programación a menudo se adaptan a varios paradigmas de programación, por lo que los programadores que usan lenguajes "principalmente imperativos" pueden haber utilizado algunos de estos conceptos.

Funciones de primera clase y de orden superior

Las funciones de orden superior son funciones que pueden tomar otras funciones como argumentos o devolverlas como resultados. En cálculo, un ejemplo de una función de orden superior es el operador diferencial d/dx, que devuelve la derivada de una función F.

Las funciones de orden superior están estrechamente relacionadas con las funciones de primera clase en el sentido de que tanto las funciones de orden superior como las funciones de primera clase permiten funciones como argumentos y resultados de otras funciones. La distinción entre los dos es sutil: "orden superior" describe un concepto matemático de funciones que operan en otras funciones, mientras que "primera clase" es un término informático para entidades de lenguaje de programación que no tienen restricciones en su uso (por lo tanto, primero Las funciones de clase pueden aparecer en cualquier parte del programa que otras entidades de primera clase como los números pueden aparecer, incluso como argumentos para otras funciones y como sus valores de retorno).

Las funciones de orden superior permiten la aplicación parcial o curry, una técnica que aplica una función a sus argumentos uno a la vez, y cada aplicación devuelve una nueva función que acepta el siguiente argumento. Esto permite que un programador exprese sucintamente, por ejemplo, la función de sucesor como el operador de suma aplicado parcialmente al número uno natural.

Funciones puras

Las funciones (o expresiones) puras no tienen efectos secundarios (memoria o E/S). Esto significa que las funciones puras tienen varias propiedades útiles, muchas de las cuales se pueden usar para optimizar el código:

Si bien la mayoría de los compiladores para lenguajes de programación imperativos detectan funciones puras y realizan la eliminación de subexpresiones comunes para llamadas de funciones puras, no siempre pueden hacer esto para bibliotecas precompiladas, que generalmente no exponen esta información, lo que evita las optimizaciones que involucran esas funciones externas. Algunos compiladores, como gcc, agregan palabras clave adicionales para que un programador marque explícitamente las funciones externas como puras, para permitir tales optimizaciones. Fortran 95 también permite que las funciones se designen puras. C++11 constexprpalabra clave agregada con semántica similar.

Recursividad

La iteración (bucle) en lenguajes funcionales generalmente se logra a través de la recursividad. Las funciones recursivas se invocan a sí mismas, permitiendo que una operación se repita hasta llegar al caso base. En general, la recursión requiere mantener una pila, que consume espacio en una cantidad lineal a la profundidad de la recursión. Esto podría hacer que la recursividad sea prohibitivamente costosa de usar en lugar de los bucles imperativos. Sin embargo, un compilador puede reconocer y optimizar una forma especial de recursión conocida como recursión de cola en el mismo código que se usa para implementar la iteración en lenguajes imperativos. La optimización de recurrencia de cola se puede implementar transformando el programa en un estilo de paso de continuación durante la compilación, entre otros enfoques.

El estándar del lenguaje Scheme requiere implementaciones para admitir la recursividad de cola adecuada, lo que significa que deben permitir un número ilimitado de llamadas de cola activas. La recursión de cola adecuada no es simplemente una optimización; es una característica del lenguaje que asegura a los usuarios que pueden usar la recursividad para expresar un bucle y hacerlo sería seguro para el espacio. Además, contrariamente a su nombre, da cuenta de todas las llamadas de cola, no solo de la recursión de cola. Si bien la recursión de cola adecuada generalmente se implementa al convertir el código en bucles imperativos, las implementaciones pueden implementarlo de otras maneras. Por ejemplo, CHICKEN mantiene intencionalmente una pila y deja que se desborde. Sin embargo, cuando esto sucede, su recolector de basura reclamará espacio de vuelta,permitiendo un número ilimitado de llamadas de cola activas aunque no convierte la recursión de cola en un bucle.

Los patrones comunes de recursividad se pueden abstraer utilizando funciones de orden superior, siendo los catamorfismos y anamorfismos (o "pliegues" y "despliegues") los ejemplos más obvios. Dichos esquemas de recurrencia juegan un papel análogo a las estructuras de control integradas, como los bucles en los lenguajes imperativos.

La mayoría de los lenguajes de programación funcional de propósito general permiten la recursividad sin restricciones y son Turing completos, lo que hace que el problema de detención sea indecidible, puede causar falta de solidez en el razonamiento ecuacional y, en general, requiere la introducción de inconsistencias en la lógica expresada por el sistema de tipos del lenguaje. Algunos lenguajes de propósito especial, como Coq, solo permiten una recursividad bien fundamentada y están fuertemente normalizados (los cálculos sin terminación solo se pueden expresar con flujos infinitos de valores llamados codata). Como consecuencia, estos lenguajes no logran ser Turing completos y expresar ciertas funciones en ellos es imposible, pero aún pueden expresar una amplia clase de cálculos interesantes evitando los problemas introducidos por la recursividad sin restricciones.

Evaluación estricta versus no estricta

Los lenguajes funcionales se pueden categorizar según usen una evaluación estricta (ansiosa) o no estricta (perezosa), conceptos que se refieren a cómo se procesan los argumentos de función cuando se evalúa una expresión. La diferencia técnica está en la semántica denotacional de las expresiones que contienen cálculos fallidos o divergentes. Bajo evaluación estricta, falla la evaluación de cualquier término que contenga un subtérmino defectuoso. Por ejemplo, la expresión:

longitud de impresión ([2+1, 3*2, 1/0, 5-4])

falla bajo evaluación estricta debido a la división por cero en el tercer elemento de la lista. En la evaluación perezosa, la función de longitud devuelve el valor 4 (es decir, el número de elementos de la lista), ya que al evaluarla no se intenta evaluar los términos que componen la lista. En resumen, la evaluación estricta siempre evalúa completamente los argumentos de la función antes de invocar la función. La evaluación diferida no evalúa los argumentos de la función a menos que se requieran sus valores para evaluar la llamada de función en sí.

La estrategia de implementación habitual para la evaluación perezosa en lenguajes funcionales es la reducción de gráficos. La evaluación diferida se usa de forma predeterminada en varios lenguajes funcionales puros, incluidos Miranda, Clean y Haskell.

Hughes 1984 aboga por la evaluación perezosa como mecanismo para mejorar la modularidad del programa a través de la separación de intereses, al facilitar la implementación independiente de productores y consumidores de flujos de datos. Launchbury 1993 describe algunas dificultades que presenta la evaluación perezosa, particularmente al analizar los requisitos de almacenamiento de un programa, y ​​propone una semántica operativa para ayudar en dicho análisis. Harper 2009 propone incluir tanto la evaluación estricta como la perezosa en el mismo idioma, utilizando el sistema de tipos del idioma para distinguirlas.

Tipo de sistemas

Especialmente desde el desarrollo de la inferencia de tipo Hindley-Milner en la década de 1970, los lenguajes de programación funcional han tendido a utilizar cálculo lambda tipificado, rechazando todos los programas inválidos en el momento de la compilación y arriesgándose a errores falsos positivos, a diferencia del cálculo lambda no tipificado, que acepta todos los programas válidos. programas en tiempo de compilación y corre el riesgo de errores falsos negativos, utilizados en Lisp y sus variantes (como Scheme), ya que rechazan todos los programas inválidos en tiempo de ejecución cuando la información es suficiente para no rechazar programas válidos. El uso de tipos de datos algebraicos hace conveniente la manipulación de estructuras de datos complejas; la presencia de una sólida verificación de tipos en tiempo de compilación hace que los programas sean más confiables en ausencia de otras técnicas de confiabilidad como el desarrollo basado en pruebas,

Algunos lenguajes funcionales orientados a la investigación, como Coq, Agda, Cayenne y Epigram, se basan en la teoría de tipos intuicionista, que permite que los tipos dependan de los términos. Estos tipos se denominan tipos dependientes. Estos sistemas de tipo no tienen inferencia de tipo decidible y son difíciles de entender y programar.Pero los tipos dependientes pueden expresar proposiciones arbitrarias en lógica de orden superior. A través del isomorfismo de Curry-Howard, los programas bien tipificados en estos lenguajes se convierten en un medio para escribir pruebas matemáticas formales a partir de las cuales un compilador puede generar código certificado. Si bien estos lenguajes son principalmente de interés en la investigación académica (incluidas las matemáticas formalizadas), también han comenzado a usarse en ingeniería. Compcert es un compilador para un subconjunto del lenguaje de programación C que está escrito en Coq y verificado formalmente.

Una forma limitada de tipos dependientes llamados tipos de datos algebraicos generalizados (GADT) se puede implementar de una manera que proporciona algunos de los beneficios de la programación con tipos dependientes y evita la mayoría de sus inconvenientes. Los GADT están disponibles en el compilador Haskell de Glasgow, en OCaml y en Scala, y se han propuesto como adiciones a otros lenguajes, incluidos Java y C#.

Transparencia referencial

Los programas funcionales no tienen declaraciones de asignación, es decir, el valor de una variable en un programa funcional nunca cambia una vez definido. Esto elimina cualquier posibilidad de efectos secundarios porque cualquier variable se puede reemplazar con su valor real en cualquier punto de ejecución. Entonces, los programas funcionales son referencialmente transparentes.

Considere la declaración de asignación de C x = x * 10, esto cambia el valor asignado a la variable x. Digamos que el valor inicial de xfue 1, luego dos evaluaciones consecutivas de la variable xrendimientos 10y 100respectivamente. Claramente, reemplazar x = x * 10con 10o 100le da a un programa un significado diferente, por lo que la expresión no es referencialmente transparente. De hecho, las sentencias de asignación nunca son referencialmente transparentes.

Ahora, considere otra función como es transparente, ya que no cambia implícitamente la entrada x y, por lo tanto, no tiene tales efectos secundarios. Los programas funcionales utilizan exclusivamente este tipo de función y por lo tanto son referencialmente transparentes. int plusone(int x) {return x+1;}

Estructuras de datos

Las estructuras de datos puramente funcionales a menudo se representan de una manera diferente a sus contrapartes imperativas.Por ejemplo, la matriz con tiempos constantes de acceso y actualización es un componente básico de la mayoría de los lenguajes imperativos, y muchas estructuras de datos imperativas, como la tabla hash y el montón binario, se basan en matrices. Los arreglos pueden ser reemplazados por mapas o listas de acceso aleatorio, que admiten una implementación puramente funcional, pero tienen tiempos de acceso y actualización logarítmicos. Las estructuras de datos puramente funcionales tienen persistencia, una propiedad de mantener las versiones anteriores de la estructura de datos sin modificar. En Clojure, las estructuras de datos persistentes se utilizan como alternativas funcionales a sus contrapartes imperativas. Los vectores persistentes, por ejemplo, utilizan árboles para la actualización parcial. Llamar al método de inserción dará como resultado la creación de algunos nodos, pero no de todos.

Comparación con la programación imperativa

La programación funcional es muy diferente de la programación imperativa. Las diferencias más significativas provienen del hecho de que la programación funcional evita los efectos secundarios, que se utilizan en la programación imperativa para implementar estado y E/S. La programación funcional pura evita por completo los efectos secundarios y proporciona transparencia referencial.

Las funciones de orden superior rara vez se usan en la programación imperativa más antigua. Un programa imperativo tradicional podría usar un bucle para recorrer y modificar una lista. Un programa funcional, por otro lado, probablemente usaría una función de "mapa" de orden superior que toma una función y una lista, generando y devolviendo una nueva lista aplicando la función a cada elemento de la lista.

Programación imperativa vs. funcional

Los siguientes dos ejemplos (escritos en JavaScript) logran el mismo efecto: multiplican todos los números pares en una matriz por 10 y los suman todos, almacenando la suma final en la variable "resultado".

Bucle imperativo tradicional:

const  numList  =  [ 1,  2,  3,  4,  5,  6,  7,  8,  9,  10 ]; 
sea  ​​resultado  =  0; 
for  (sea  i  =  0;  i  <  numList. length;  i ++)  { 
  if  (numList [ i ]  %  2  ===  0)  {
    resultado  +=  listanumeros [ i ]  *  10; 
  } 
}

Programación funcional con funciones de orden superior:

 resultado  constante =  [ 1,  2,  3,  4,  5,  6,  7,  8,  9,  10 ] 
. filtrar (n  =>  n  %  2  ===  0) 
. mapa (a  =>  a  *  10) 
. reducir ((a,  b)  =>  a  +  b);

Simulando estado

Hay tareas (por ejemplo, mantener el saldo de una cuenta bancaria) que a menudo parecen implementarse más naturalmente con el estado. La programación funcional pura realiza estas tareas y las tareas de E/S, como aceptar la entrada del usuario e imprimir en la pantalla, de una manera diferente.

El lenguaje de programación puramente funcional Haskell los implementa usando mónadas, derivadas de la teoría de categorías. Las mónadas ofrecen una forma de abstraer ciertos tipos de patrones computacionales, incluido (pero no limitado a) el modelado de cálculos con estado mutable (y otros efectos secundarios como E/S) de manera imperativa sin perder pureza. Mientras que las mónadas existentes pueden ser fáciles de aplicar en un programa, dadas las plantillas y ejemplos apropiados, a muchos estudiantes les resulta difícil entenderlos conceptualmente, por ejemplo, cuando se les pide que definan nuevas mónadas (lo que a veces es necesario para ciertos tipos de bibliotecas).

Los lenguajes funcionales también simulan estados pasando estados inmutables. Esto se puede hacer haciendo que una función acepte el estado como uno de sus parámetros y devuelva un nuevo estado junto con el resultado, dejando el estado anterior sin cambios.

Los lenguajes funcionales impuros generalmente incluyen un método más directo para administrar el estado mutable. Clojure, por ejemplo, utiliza referencias administradas que se pueden actualizar aplicando funciones puras al estado actual. Este tipo de enfoque permite la mutabilidad al mismo tiempo que promueve el uso de funciones puras como la forma preferida de expresar los cálculos.

Se han desarrollado métodos alternativos como la lógica Hoare y la unicidad para rastrear los efectos secundarios en los programas. Algunos lenguajes de investigación modernos utilizan sistemas de efectos para hacer explícita la presencia de efectos secundarios.

Problemas de eficiencia

Los lenguajes de programación funcionales suelen ser menos eficientes en el uso de la CPU y la memoria que los lenguajes imperativos como C y Pascal. Esto está relacionado con el hecho de que algunas estructuras de datos mutables, como las matrices, tienen una implementación muy sencilla utilizando el hardware actual. Se puede acceder a las matrices planas de manera muy eficiente con CPU profundamente canalizadas, precargadas de manera eficiente a través de cachés (sin una persecución de puntero compleja) o manejadas con instrucciones SIMD. Tampoco es fácil crear sus contrapartes inmutables de propósito general igualmente eficientes. Para lenguajes puramente funcionales, la ralentización en el peor de los casos es logarítmica en el número de celdas de memoria utilizadas, porque la memoria mutable puede representarse mediante una estructura de datos puramente funcional con tiempo de acceso logarítmico (como un árbol equilibrado).Sin embargo, tales ralentizaciones no son universales. Para los programas que realizan cálculos numéricos intensivos, los lenguajes funcionales como OCaml y Clean son solo un poco más lentos que C según The Computer Language Benchmarks Game. Para los programas que manejan matrices grandes y bases de datos multidimensionales, se diseñaron lenguajes funcionales de matriz (como J y K) con optimizaciones de velocidad.

La inmutabilidad de los datos puede, en muchos casos, conducir a la eficiencia de la ejecución al permitir que el compilador haga suposiciones que no son seguras en un lenguaje imperativo, lo que aumenta las oportunidades de expansión en línea.

La evaluación perezosa también puede acelerar el programa, incluso asintóticamente, mientras que puede ralentizarlo como máximo por un factor constante (sin embargo, puede introducir fugas de memoria si se usa incorrectamente). Launchbury 1993 analiza cuestiones teóricas relacionadas con las fugas de memoria de la evaluación perezosa, y O'Sullivan et al. 2008 dan algunos consejos prácticos para analizarlos y corregirlos. Sin embargo, las implementaciones más generales de evaluación diferida que hacen un uso extensivo de código y datos desreferenciados funcionan mal en los procesadores modernos con canalizaciones profundas y cachés de varios niveles (donde una falta de caché puede costar cientos de ciclos).

Programación funcional en lenguajes no funcionales

Es posible usar un estilo funcional de programación en lenguajes que tradicionalmente no se consideran lenguajes funcionales. Por ejemplo, tanto D como Fortran 95 admiten explícitamente funciones puras.

JavaScript, Lua, Python y Go tuvieron funciones de primera clase desde sus inicios. Python tenía soporte para "lambda", "mapa", "reducir" y "filtro" en 1994, así como cierres en Python 2.2, aunque Python 3 relegó "reducir" al functoolsmódulo de biblioteca estándar. Se han introducido funciones de primera clase en otros lenguajes principales como PHP 5.3, Visual Basic 9, C# 3.0, C++11 y Kotlin.

En PHP, las clases anónimas, los cierres y las lambdas son totalmente compatibles. Se están desarrollando bibliotecas y extensiones de lenguaje para estructuras de datos inmutables para ayudar a la programación en el estilo funcional.

En Java, las clases anónimas a veces se pueden usar para simular cierres; sin embargo, las clases anónimas no siempre son reemplazos adecuados de los cierres porque tienen capacidades más limitadas. Java 8 admite expresiones lambda como reemplazo de algunas clases anónimas.

En C#, las clases anónimas no son necesarias porque los cierres y lambdas son totalmente compatibles. Se están desarrollando bibliotecas y extensiones de lenguaje para estructuras de datos inmutables para ayudar a programar en el estilo funcional en C#.

Muchos patrones de diseño orientados a objetos se pueden expresar en términos de programación funcional: por ejemplo, el patrón de estrategia simplemente dicta el uso de una función de orden superior, y el patrón de visitante corresponde aproximadamente a un catamorfismo o pliegue.

De manera similar, la idea de datos inmutables de la programación funcional a menudo se incluye en lenguajes de programación imperativos, por ejemplo, la tupla en Python, que es una matriz inmutable, y Object.freeze() en JavaScript.

Aplicaciones

Hojas de calculo

Las hojas de cálculo pueden considerarse una forma de sistema de programación funcional puro, de orden cero y de evaluación estricta. Sin embargo, las hojas de cálculo generalmente carecen de funciones de orden superior, así como de reutilización de código y, en algunas implementaciones, también carecen de recursividad. Se han desarrollado varias extensiones para programas de hojas de cálculo para habilitar funciones reutilizables y de orden superior, pero hasta ahora siguen siendo principalmente de naturaleza académica.

Academia

La programación funcional es un área activa de investigación en el campo de la teoría del lenguaje de programación. Hay varios lugares de publicación revisados ​​por pares que se centran en la programación funcional, incluida la Conferencia Internacional sobre Programación Funcional, el Diario de Programación Funcional y el Simposio sobre Tendencias en Programación Funcional.

Industria

La programación funcional se ha utilizado en una amplia variedad de aplicaciones industriales. Por ejemplo, Erlang, que fue desarrollado por la empresa sueca Ericsson a fines de la década de 1980, se usó originalmente para implementar sistemas de telecomunicaciones tolerantes a fallas, pero desde entonces se ha vuelto popular para crear una variedad de aplicaciones en empresas como Nortel, Facebook, Électricité de Francia y WhatsApp. Scheme, un dialecto de Lisp, se utilizó como base para varias aplicaciones en las primeras computadoras Apple Macintosh y se ha aplicado a problemas como el software de simulación de entrenamiento y el control de telescopios. OCaml, que se introdujo a mediados de la década de 1990, ha tenido un uso comercial en áreas como el análisis financiero,verificación de controladores, programación de robots industriales y análisis estático de software integrado. Haskell, aunque inicialmente se pensó como un lenguaje de investigación, también ha sido aplicado por una variedad de empresas, en áreas como sistemas aeroespaciales, diseño de hardware y programación web.

Otros lenguajes de programación funcionales que se han utilizado en la industria incluyen Scala, F#, Wolfram Language, Lisp, Standard ML y Clojure.

Las "plataformas" funcionales han sido populares en las finanzas para el análisis de riesgos (particularmente con los bancos de inversión más grandes). Los factores de riesgo se codifican como funciones que forman gráficos interdependientes (categorías) para medir las correlaciones en los cambios del mercado, no muy diferentes a las optimizaciones de la base de Gröbner, sino también para el cumplimiento normativo, como el análisis y la revisión integral del capital. Dado el uso de variaciones de OCAML o CAML en finanzas, estos sistemas a veces se consideran relacionados con una máquina abstracta categórica o CAM. De hecho, la programación funcional está fuertemente influenciada por la teoría de categorías.

Educación

Muchas universidades enseñan o han enseñado programación funcional como parte de sus títulos universitarios en Ciencias de la Computación. Algunos lo usan como introducción a la programación, mientras que otros lo enseñan después de enseñar programación imperativa.

Fuera de la informática, la programación funcional se utiliza como método para enseñar resolución de problemas, álgebra y conceptos geométricos. También se ha utilizado como herramienta para la enseñanza de la mecánica clásica en Estructura e Interpretación de la Mecánica Clásica.