En los últimos años, las funciones anónimas (funciones AKA lambda) se han convertido en una construcción de lenguaje muy popular y casi todos los lenguajes de programación principales / principales las han introducido o se planea introducirlas en una próxima revisión del estándar.
Sin embargo, las funciones anónimas son un concepto muy antiguo y muy conocido en Matemáticas e Informática (inventado por el matemático Alonzo Church alrededor de 1936, y utilizado por el lenguaje de programación Lisp desde 1958, véase, por ejemplo, aquí ).
Entonces, ¿por qué los lenguajes de programación convencionales de hoy (muchos de los cuales se originaron hace 15 a 20 años) admitían las funciones lambda desde el principio y solo las introdujeron más tarde?
¿Y qué provocó la adopción masiva de funciones anónimas en los últimos años? ¿Hay algún evento específico, un nuevo requisito o una técnica de programación que inició este fenómeno?
NOTA IMPORTANTE
El objetivo de esta pregunta es la introducción de funciones anónimas en lenguajes modernos, mainstream (y, por lo tanto, tal vez con algunas excepciones, no funcionales). Además, tenga en cuenta que las funciones anónimas (bloques) están presentes en Smalltalk, que no es un lenguaje funcional, y que las funciones con nombre normales han estado presentes incluso en lenguajes de procedimiento como C y Pascal durante mucho tiempo.
No generalice en exceso sus respuestas al hablar sobre "la adopción del paradigma funcional y sus beneficios", porque este no es el tema de la pregunta.
fuente
Respuestas:
Ciertamente hay una tendencia notable hacia la programación funcional, o al menos ciertos aspectos de la misma. Algunos de los lenguajes populares que en algún momento adoptaron funciones anónimas son C ++ ( C ++ 11 ), PHP ( PHP 5.3.0 ), C # ( C # v2.0 ), Delphi (desde 2009), Objective C ( bloques ) mientras Java 8 traerá soporte para lambdas al idioma . Y hay lenguajes populares que generalmente no se consideran funcionales, sino que admiten funciones anónimas desde el principio, o al menos desde el principio, siendo el ejemplo brillante JavaScript.
Al igual que con todas las tendencias, tratar de buscar un solo evento que los haya provocado es probablemente una pérdida de tiempo, generalmente es una combinación de factores, la mayoría de los cuales no son cuantificables. Practical Common Lisp , publicado en 2005, puede haber jugado un papel importante al atraer nueva atención a Lisp como un lenguaje práctico , ya que durante bastante tiempo Lisp fue principalmente un idioma que conocerías en un entorno académico o en nichos de mercado muy específicos. La popularidad de JavaScript también puede haber jugado un papel importante en atraer nueva atención a las funciones anónimas, como explica munificent en su respuesta .
Además de la adopción de conceptos funcionales de lenguajes multipropósito, también hay un cambio notable hacia lenguajes funcionales (o mayormente funcionales). Idiomas como Erlang (1986), Haskell (1990), OCaml (1996), Scala (2003), F # (2005), Clojure (2007) e incluso los idiomas específicos de dominio como R (1993) parecen haber ganado un fuerte seguimiento. después de que fueron presentados. La tendencia general ha atraído nueva atención a los lenguajes funcionales más antiguos, como Scheme (1975) y, obviamente, Common Lisp.
Creo que el evento más importante es la adopción de programación funcional en la industria. No tengo ni idea de por qué no solía ser así, pero me parece que en algún momento durante la programación funcional de principios y mediados de los 90 comenzó a encontrar su lugar en la industria, comenzando (tal vez) con la proliferación de Erlang en telecomunicaciones y la adopción de Haskell en el diseño aeroespacial y de hardware .
Joel Spolsky ha escrito una publicación de blog muy interesante, The Perils of JavaSchools , donde argumenta en contra de la (entonces) tendencia de las universidades de favorecer Java sobre otros idiomas, quizás más difíciles de aprender. Aunque la publicación del blog tiene poco que ver con la programación funcional, identifica un problema clave:
Todavía recuerdo cuánto odiaba a Lisp, cuando la conocí durante mis años universitarios. Definitivamente es una amante dura, y no es un lenguaje en el que puedas ser productivo de inmediato (bueno, al menos no pude). En comparación con Lisp, Haskell (por ejemplo) es mucho más amigable, puede ser productivo sin tanto esfuerzo y sin sentirse como un completo idiota, y eso también podría ser un factor importante en el cambio hacia la programación funcional.
En general, esto es algo bueno. Varios lenguajes multipropósito están adoptando conceptos de paradigma que podrían haber parecido arcanos a la mayoría de sus usuarios antes, y la brecha entre los paradigmas principales se está reduciendo.
Preguntas relacionadas:
Otras lecturas:
fuente
Creo que es interesante hasta qué punto la popularidad de la programación funcional ha sido paralela al crecimiento y la proliferación de Javascript. Javascript tiene muchas características radicales a lo largo del espectro de programación funcional que en el momento de su creación (1995) no eran muy populares entre los lenguajes de programación convencionales (C ++ / Java). Fue inyectado repentinamente en la corriente principal como el único lenguaje de programación web del lado del cliente. De repente, muchos programadores simplemente tenían que saber Javascript y, por lo tanto, tenían que saber algo sobre las características funcionales del lenguaje de programación.
Me pregunto qué tan populares serían las funciones / lenguajes funcionales si no hubiera sido por el repentino aumento de Javascript.
fuente
JavaScript y DOM controladores de eventos significado que millones de programadores tenían que aprender al menos un poco sobre las funciones de primera clase con el fin de hacer ninguna interactividad en la web.
A partir de ahí, es un paso relativamente corto para las funciones anónimas . Debido a que JavaScript no se cierra
this
, también lo alienta a aprender sobre los cierres. Y luego eres dorado: entiendes funciones anónimas de primera clase que se cierran sobre los ámbitos léxicos.Una vez que se sienta cómodo con él, lo querrá en todos los idiomas que use.
fuente
Ciertamente no es el único factor, pero señalaré la popularidad de Ruby. No digo que esto sea más importante que cualquiera de las seis respuestas que ya están en la pizarra, pero creo que sucedieron muchas cosas a la vez y que es útil enumerarlas todas.
Ruby no es un lenguaje funcional y sus lambdas, productos y bloques parecen torpes cuando has usado algo como ML, pero el hecho es que popularizó la noción de mapeo y reducción a una generación de jóvenes programadores que huyen de Java y PHP por hipper pastizales Las lambdas en varios idiomas parecen ser movimientos defensivos más que cualquier otra cosa ("¡Quédate! ¡Nosotros también los tenemos!)
Pero la sintaxis de bloque y la forma en que se integraba con .each, .map, .reduce, etc., popularizaron la idea de una función anónima, incluso si realmente es una construcción sintáctica que se comporta como una rutina. Y la fácil conversión a un proceso a través de & lo convierte en un fármaco de entrada para la programación funcional.
Sostengo que los programadores de Ruby on Rails que escribían JavaScript ya estaban activados para hacer las cosas con un estilo funcional ligero. Combine eso con los blogs de programadores, la invención de Reddit, Hacker News y Stack Overflow al mismo tiempo, y las ideas se difundieron más rápido en Internet que en los días de grupos de noticias.
TL; DR: Ruby, Rails, JavaScript, blogging y Reddit / Hacker News / Stack Overflow impulsaron las ideas funcionales a un mercado masivo, por lo que todos las querían en los idiomas existentes para evitar nuevas deserciones.
fuente
Como señaló Yannis , hay una serie de factores que han influido en la adopción de funciones de alto orden en idiomas que anteriormente no tenían. Uno de los elementos importantes que solo tocó a la ligera es la proliferación de procesadores multinúcleo y, con eso, el deseo de un procesamiento más paralelo y concurrente.
El estilo de programación funcional de mapa / filtro / reducción es muy amigable con la paralelización, lo que permite al programador utilizar fácilmente múltiples núcleos, sin escribir ningún código de subproceso explícito.
Como señala Giorgio, la programación funcional tiene más que funciones de alto orden. Las funciones, más un patrón de programación de mapa / filtro / reducción, y la inmutabilidad son el núcleo de la programación funcional. En conjunto, estas cosas hacen poderosas herramientas de programación paralela y concurrente. Afortunadamente, muchos lenguajes ya admiten alguna noción de inmutabilidad e, incluso si no lo hacen, los programadores pueden tratar las cosas como inmutables permitiendo que las bibliotecas y el compilador creen y administren operaciones asincrónicas o paralelas.
Agregar funciones de alto orden a un lenguaje es un paso importante para simplificar la programación concurrente.
Actualizar
Agregaré un par de ejemplos más detallados para abordar las preocupaciones que señaló Loki.
Considere el siguiente código C # que atraviesa una colección de widgets, creando una nueva lista de precios de widgets.
Para una gran colección de widgets, o un método CalculateWidgetPrice (Widget) computacionalmente intensivo, este bucle no haría un buen uso de los núcleos disponibles. Hacer los cálculos de precios en diferentes núcleos requeriría que el programador cree y administre explícitamente hilos, pasando el trabajo y recopilando los resultados juntos.
Considere una solución una vez que se hayan agregado funciones de orden superior a C #:
El bucle foreach se ha movido al método Seleccionar, ocultando los detalles de su implementación. Todo lo que le queda al programador es decirle a Select qué función aplicar a cada elemento. Esto permitiría que la implementación de Select ejecute los cálculos en paralelo, manejando todas las preocupaciones de sincronización y gestión de subprocesos sin la participación del programador.
Pero, por supuesto, Select no hace su trabajo en paralelo. Ahí es donde entra en juego la inmutabilidad. La implementación de Select no sabe que la función proporcionada (CalculateWidgets arriba) no tiene efectos secundarios. La función podría cambiar el estado del programa fuera de la vista de Select y su sincronización, rompiendo todo. Por ejemplo, en este caso, el valor de salesTax podría modificarse por error. Los lenguajes funcionales puros proporcionan inmutabilidad, por lo que la función Seleccionar (mapa) puede saber con certeza que ningún estado está cambiando.
C # aborda esto proporcionando PLINQ como una alternativa a Linq. Eso se vería así:
Lo que hace un uso completo de todos los núcleos de su sistema sin una gestión explícita de esos núcleos.
fuente
cause
y aperceived affect
sin explicar elcorrelation
. La última línea de la OMI es de qué se trata la pregunta; pero no lo respondiste ¿Por qué simplifica la programación concurrente?Estoy de acuerdo con muchas de las respuestas aquí, pero lo interesante es que cuando aprendí sobre lambdas y salté sobre ellas, no fue por ninguna de las razones que otros han mencionado.
En muchos casos, las funciones lambda simplemente mejoran la legibilidad de su código. Antes de lambdas, cuando llama a un método que acepta un puntero de función (o función, o delega), tenía que definir el cuerpo de esa función en otro lugar, por lo que cuando tenía una construcción "foreach", su lector tendría que saltar a un punto diferente parte del código para ver exactamente qué planeaba hacer con cada elemento.
Si el cuerpo de la función que procesa elementos tiene solo unas pocas líneas, usaría una función anónima porque ahora cuando está leyendo el código, la funcionalidad permanece sin cambios, pero el lector no tiene que saltar de un lado a otro, toda la implementación es Justo delante de él.
Muchas de las técnicas de programación funcional y la paralelización podrían lograrse sin funciones anónimas; simplemente declare uno normal y pase una referencia a eso siempre que lo necesite. Pero con la facilidad de escritura del código lambdas y la facilidad de lectura del código se mejora enormemente.
fuente
Después de haber estado involucrado en la historia reciente aquí un poco, creo que un factor fue la adición de genéricos a Java y .NET. Eso naturalmente conduce a Func < , > y otras abstracciones computacionales fuertemente tipadas (Tarea < >, Async < > etc.)
En el mundo .NET, agregamos estas características precisamente para admitir FP. Eso desencadenó un conjunto de trabajos de lenguaje en cascada relacionados con la programación funcional, especialmente C # 3.0, LINQ, Rx y F #. Esa progresión también influyó en otros ecosistemas y aún continúa en C #, F # y TypeScript.
También ayuda tener a Haskell trabajando en MSR, por supuesto :)
Por supuesto, también hubo muchas otras influencias (JS ciertamente) y estos pasos fueron a su vez influenciados por muchas otras cosas, pero agregar genéricos a estos idiomas ayudó a romper la rígida ortodoxia OO de finales de los 90 en gran parte del mundo del software y ayudó a abrir la puerta para FP.
Don Syme
ps F # era 2003, no 2005, aunque diríamos que no alcanzó 1.0 hasta 2005. También hicimos un prototipo de Haskell.NET en 2001-02.
fuente
Esto no pretende ser una respuesta seria, pero la pregunta me recordó una publicación humorística genial de James Iry: una historia breve, incompleta y en su mayoría incorrecta de lenguajes de programación que incluye la siguiente frase:
"Las lambdas quedan relegadas a una relativa oscuridad hasta que Java las haga populares al no tenerlas".
fuente
Por lo que veo, la mayoría de las respuestas se concentran en explicar por qué la programación funcional en general regresó y llegó a la corriente principal. Sin embargo, sentí que esto realmente no responde la pregunta sobre las funciones anónimas en particular, y por qué de repente se hicieron tan populares.
Lo que realmente ha ganado popularidad son los cierres . Dado que en la mayoría de los casos los cierres son funciones descartables que pasan variables, obviamente tiene sentido usar la sintaxis de funciones anónimas para estos. Y, de hecho, en algunos idiomas es la única forma de crear un cierre.
¿Por qué los cierres han ganado popularidad? Porque son útiles en la programación controlada por eventos, al crear funciones de devolución de llamada . Actualmente es la forma de escribir código de cliente JavaScript (de hecho, es la forma de escribir cualquier código GUI). Actualmente también es la forma de escribir código de fondo altamente eficiente, así como el código del sistema, ya que el código escrito en el paradigma controlado por eventos suele ser asíncrono y sin bloqueo . Para el back-end, esto se hizo popular como solución al problema C10K .
fuente
Creo que la razón es la creciente prevalencia de la programación concurrente y distribuida, donde el núcleo de la programación orientada a objetos (encapsulando el estado cambiante con objetos) ya no se aplica. En el caso de un sistema distribuido, porque no es ningún estado compartido (y abstracciones de software de este concepto son fugas) y en el caso de un sistema concurrente, porque correctamente la sincronización de acceso a estado compartido ha demostrado engorroso y propenso a errores. Es decir, uno de los beneficios clave de la programación orientada a objetos ya no se aplica a muchos programas, lo que hace que el paradigma orientado a objetos sea mucho menos útil de lo que solía ser.
En contraste, el paradigma funcional no usa el estado mutable. Cualquier experiencia que obtengamos con paradigmas y patrones funcionales es, por lo tanto, transferible de manera más inmediata a la computación concurrente y distribuida. Y en lugar de reinventar la rueda, la industria ahora toma prestados esos patrones y características del lenguaje para satisfacer sus necesidades.
fuente
Si puedo agregar mis € 0.02, aunque estaría de acuerdo con la importancia de que JavaScript introduzca el concepto, creo que más que la programación concurrente culparía a la moda actual de la programación asincrónica. Cuando se realizan llamadas asíncronas (necesarias con las páginas web), las funciones anónimas simples son tan evidentemente útiles que cada programador web (es decir, cada programador) tuvo que familiarizarse con el concepto.
fuente
Otro ejemplo muy antiguo de algo parecido a funciones anónimas / lambdas es la llamada por nombre en Algol 60. Sin embargo, tenga en cuenta que la llamada por nombre está más cerca de pasar macros como parámetros que pasar funciones verdaderas, y es más frágil / difícil de identificar. entender como resultado.
fuente
Aquí la ascendencia a lo mejor de mi conocimiento.
fuente
Las funciones anónimas son buenas porque nombrar cosas es difícil, y si solo usa una función de una vez, no necesita un nombre.
Las funciones de Lambda se han convertido en una corriente principal recientemente porque hasta hace poco, la mayoría de los idiomas no admitían cierres.
Sugeriría que Javascript ha impulsado esta corriente principal. Es un lenguaje universal que no tiene forma de expresar paralelismo, y las funciones anónimas facilitan el uso de modelos de devolución de llamada. Además, han contribuido idiomas populares como Ruby y Haskell.
fuente