Programación funcional - Functional programming

En informática , la programación funcional es un paradigma de programación en el que 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 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 pequeñas funciones 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 puede verse 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 a partir del cálculo lambda , un sistema formal de cálculo basado únicamente en funciones. La programación funcional ha sido históricamente menos popular que la programación imperativa, pero muchos lenguajes funcionales se están utilizando 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 utilizan 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 , 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 cálculo, lo que demuestra que el cálculo lambda es Turing completo . El cálculo lambda forma la base de todos los lenguajes de programación funcionales. Una formulación teórica equivalente, la lógica combinatoria , fue desarrollada por Moses Schönfinkel y Haskell Curry en las décadas de 1920 y 1930.

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

El primer lenguaje de programación funcional, LISP , fue desarrollado a fines de la década de 1950 para la serie de computadoras científicas IBM 700/7000 por John McCarthy mientras estaba 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 evolucionaron nuevos paradigmas. Los dialectos posteriores, como Scheme y Clojure , y ramificaciones como Dylan y Julia , buscaron simplificar y racionalizar Lisp en torno a un núcleo limpiamente funcional, 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ó.

Information Processing Language (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 A Programming Language ( ISBN  9780471430148 ). APL fue la principal influencia en el FP de John Backus . A principios de 1990, Iverson y Roger Hui crearon J . A mediados de la década de 1990, Arthur Whitney , que había trabajado previamente con Iverson, creado K , que se utiliza comercialmente en la industria financiera, junto con su descendiente Q .

John Backus presentó FP en su conferencia del Premio Turing de 1977 "¿Puede la programación liberarse 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 el 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 las ecuaciones de recurrencia de Kleene y se introdujo por primera vez en su trabajo sobre la transformación de programas. Luego, Burstall, MacQueen y Sannella incorporaron la verificación de tipos polimórficos de ML para producir el lenguaje Hope . ML eventualmente se desarrolló en varios dialectos, los más comunes de los cuales 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 los Lambda Papers y en el libro de texto de 1985 Structure and Interpretation of Computer Programs . Scheme fue el primer dialecto de lisp que utilizó el alcance léxico y requirió optimización de llamadas finales , características que fomentan la programación funcional.

En la década de 1980, Per Martin-Löf desarrolló la teoría de tipos intuicionistas (también llamada teoría de tipos constructivos ), 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 como 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 construido 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 llevado a confusión entre los usuarios que no están familiarizados con la programación funcional. como concepto.

La programación funcional sigue utilizándose en entornos comerciales.

Conceptos

Varios 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 utilizan 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 función de orden superior es el operador diferencial , que devuelve la derivada de una función .

Las funciones de orden superior están estrechamente relacionadas con las funciones de primera clase en el sentido de que las funciones de orden superior y 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 las entidades del lenguaje de programación que no tienen restricciones en su uso (por lo tanto, primero -las funciones de clase pueden aparecer en cualquier lugar del programa que otras entidades de primera clase como los números, incluso como argumentos para otras funciones y como sus valores de retorno).

Las funciones de orden superior permiten la aplicación parcial o el procesamiento , una técnica que aplica una función a sus argumentos de uno en uno, 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 natural uno.

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 pueden usarse para optimizar el código:

  • Si no se utiliza el resultado de una expresión pura, se puede eliminar sin afectar a otras expresiones.
  • Si se llama a una función pura con argumentos que no causan efectos secundarios, el resultado es constante con respecto a esa lista de argumentos (a veces se llama transparencia referencial o idempotencia ), es decir, llamar a la función pura de nuevo con los mismos argumentos devuelve el mismo resultado. (Esto puede habilitar optimizaciones de almacenamiento en caché como la memorización ).
  • Si no hay dependencia de datos entre dos expresiones puras, su orden se puede invertir o se pueden realizar en paralelo y no pueden interferir entre sí (en otros términos, la evaluación de cualquier expresión pura es segura para subprocesos ).
  • Si todo el lenguaje no permite efectos secundarios, entonces se puede utilizar cualquier estrategia de evaluación; esto le da al compilador la libertad de reordenar o combinar la evaluación de expresiones en un programa (por ejemplo, usando la deforestación ).

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 a funciones puras, no siempre pueden hacerlo para bibliotecas precompiladas, que generalmente no exponen esta información, evitando así optimizaciones que involucran esas funciones externas. Algunos compiladores, como gcc , agregan palabras clave adicionales para que un programador marque explícitamente funciones externas como puras, para habilitar tales optimizaciones. Fortran 95 también permite designar funciones puras . C ++ 11 agregó una constexprpalabra clave con semántica similar.

Recursividad

La iteración (bucle) en lenguajes funcionales generalmente se logra mediante recursividad . Las funciones recursivas se invocan a sí mismas, permitiendo que una operación se repita hasta que llega al caso base . En general, la recursividad requiere mantener una pila , que consume espacio en una cantidad lineal hasta la profundidad de la recursividad. Esto podría hacer que la recursividad sea prohibitivamente costosa de usar en lugar de bucles imperativos. Sin embargo, una forma especial de recursividad conocida como recursividad de cola puede ser reconocida y optimizada por un compilador en el mismo código utilizado para implementar la iteración en lenguajes imperativos. La optimización de la recursividad 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 recursividad 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, representa todas las llamadas de cola, no solo la recursividad de cola. Si bien la recursividad de cola adecuada generalmente se implementa convirtiendo el código en bucles imperativos, las implementaciones pueden implementarlo de otras maneras. Por ejemplo, CHICKEN mantiene intencionalmente una pila y deja que la pila se desborde . Sin embargo, cuando esto sucede, su recolector de basura reclamará espacio, permitiendo un número ilimitado de llamadas de cola activas aunque no convierta la recursividad 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. Estos esquemas de recursividad 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 completo , lo que hace que el problema de la detención sea indecidible , puede causar fallas en el razonamiento de ecuaciones y generalmente 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 la recursividad bien fundada y se normalizan fuertemente (los cálculos no terminales se pueden expresar solo con flujos infinitos de valores llamados codatos ). Como consecuencia, estos lenguajes no son completos de Turing y es imposible expresar ciertas funciones en ellos, pero aún pueden expresar una amplia clase de cálculos interesantes evitando los problemas introducidos por la recursividad irrestricta. La programación funcional limitada a la recursividad bien fundada con algunas otras restricciones se denomina programación funcional total .

Evaluación estricta versus no estricta

Los lenguajes funcionales pueden clasificarse en función de si utilizan 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 expresiones que contienen cálculos erróneos o divergentes. Bajo una evaluación estricta, la evaluación de cualquier término que contenga un subterráneo fallido falla. Por ejemplo, la expresión:

print length([2+1, 3*2, 1/0, 5-4])

falla bajo una 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 sus valores sean necesarios para evaluar la propia llamada a la función.

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

Hughes 1984 aboga por la evaluación perezosa como un mecanismo para mejorar la modularidad del programa a través de la separación de preocupaciones , facilitando la implementación independiente de productores y consumidores de flujos de datos. Launchbury 1993 describe algunas dificultades que introduce 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 distinguirlos.

Sistemas de tipos

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 el cálculo lambda tipado , rechazando todos los programas inválidos en el momento de la compilación y arriesgándose a errores de falsos positivos , a diferencia del cálculo lambda no tipado , que acepta todos los valores válidos. programas en tiempo de compilación y se arriesgan a errores falsos negativos , usados ​​en Lisp y sus variantes (como Scheme ), aunque 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 que la manipulación de estructuras de datos complejas sea conveniente; 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 , mientras que la inferencia de tipos libera al programador de la necesidad de declarar tipos manualmente al compilador en la mayoría de los casos.

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 términos. Estos tipos se denominan tipos dependientes . Estos sistemas de tipos no tienen una inferencia de tipos 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 Curry-Howard , los programas bien escritos 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 de interés principalmente para la investigación académica (incluidas las matemáticas formalizadas ), también han comenzado a utilizarse en ingeniería. Compcert es un compilador para un subconjunto del lenguaje de programación C que está escrito en Coq y verificado formalmente.

Se puede implementar una forma limitada de tipos dependientes denominada tipos de datos algebraicos generalizados (GADT) de una manera que proporcione algunos de los beneficios de la programación de tipo dependiente y evite la mayor parte de sus inconvenientes. Los GADT están disponibles en Glasgow Haskell Compiler , 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 puede reemplazarse con su valor real en cualquier punto de ejecución. Entonces, los programas funcionales son referencialmente transparentes.

Considere la declaración de asignación Cx = 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 declaraciones 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 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 acceso constante y tiempos de 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. Las matrices se pueden reemplazar por mapas o listas de acceso aleatorio, que admiten una implementación puramente funcional, pero tienen acceso logarítmico y tiempos de actualización. Las estructuras de datos puramente funcionales tienen persistencia , una propiedad de mantener sin modificar las versiones anteriores de la estructura de datos. En Clojure, las estructuras de datos persistentes se utilizan como alternativas funcionales a sus contrapartes imperativas. Los vectores persistentes, por ejemplo, usan árboles para actualizaciones parciales. Llamar al método de inserción dará como resultado la creación de algunos nodos, pero no 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 el estado y la 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 utilizan 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 frente a programación 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];
let result = 0;
for (let i = 0; i < numList.length; i++) {
  if (numList[i] % 2 === 0) {
    result += numList[i] * 10;
  }
}

Programación funcional con funciones de orden superior:

const result = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
               .filter(n => n % 2 === 0)
               .map(a => a * 10)
               .reduce((a, b) => a + b);

Simulando estado

Hay tareas (por ejemplo, mantener el saldo de una cuenta bancaria) que a menudo parecen implementadas de manera más natural 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 funcional puro 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. Si bien las mónadas existentes pueden ser fáciles de aplicar en un programa, dadas las plantillas y los ejemplos adecuados, a muchos estudiantes les resulta difícil comprenderlos 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 suelen incluir un método más directo de gestionar el estado mutable. Clojure , por ejemplo, usa 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 cálculos.

Se han desarrollado métodos alternativos como la lógica de Hoare y la singularidad 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 funcional 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 con 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 búsqueda compleja de punteros ) o manejadas con instrucciones SIMD. Tampoco es fácil crear sus homólogos inmutables de uso general igualmente eficientes. Para lenguajes puramente funcionales, la desaceleración en el peor de los casos es logarítmica en el número de celdas de memoria utilizadas, porque la memoria mutable se puede representar 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 matrices (como J y K ) con optimizaciones de velocidad.

En muchos casos, la inmutabilidad de los datos puede conducir a la eficiencia de la ejecución al permitir que el compilador haga suposiciones que no son seguras en un lenguaje imperativo, aumentando así 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 en un factor constante (sin embargo, puede introducir pérdidas de memoria si se usa incorrectamente). Launchbury 1993 analiza cuestiones teóricas relacionadas con las pérdidas 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 procesadores modernos con canalizaciones profundas y cachés multinivel (donde una pérdida de caché puede costar cientos de ciclos).

Programación funcional en lenguajes no funcionales

Es posible utilizar 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 tenían funciones de primera clase desde sus inicios. Python tenía soporte para " lambda ", " mapa ", " reducir " y " filtrar " 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 convencionales 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 , a veces se pueden usar clases anónimas 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 la programación 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 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 cálculo

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 permitir funciones de orden superior y reutilizables, 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 , la Revista 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 compañía sueca Ericsson a fines de la década de 1980, se utilizó originalmente para implementar sistemas de telecomunicaciones tolerantes a fallas , pero desde entonces se ha vuelto popular para construir una variedad de aplicaciones en compañías 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, la verificación de controladores , la programación de robots industriales y el 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 funcional 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 (categorías) interdependientes para medir las correlaciones en los cambios del mercado, no a diferencia de las optimizaciones de base de Gröbner, sino también para el cumplimiento normativo, como Análisis y revisión de capital integral . Dado el uso de variaciones OCAML o CAML en las 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 utilizan como introducción a la programación, mientras que otros lo enseñan después de enseñar programación imperativa.

Fuera de las ciencias de la computación, la programación funcional se utiliza como un 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 .

Ver también

Referencias

Otras lecturas

enlaces externos

Escuche este artículo ( 28 minutos )
Icono de Wikipedia hablado
Este archivo de audio se creó a partir de una revisión de este artículo con fecha del 25 de agosto de 2011 y no refleja ediciones posteriores. ( 25-08-2011 )