¿Por qué Python no es muy bueno para la programación funcional? [cerrado]

324

Siempre he pensado que la programación funcional se puede hacer en Python. Por lo tanto, me sorprendió que Python no recibiera mucha mención en esta pregunta, y cuando se mencionó, normalmente no fue muy positivo. Sin embargo, no se dieron muchas razones para esto (se mencionó la falta de coincidencia de patrones y los tipos de datos algebraicos). Entonces mi pregunta es: ¿por qué Python no es muy bueno para la programación funcional? ¿Hay más razones que su falta de coincidencia de patrones y tipos de datos algebraicos? ¿O son estos conceptos tan importantes para la programación funcional que un lenguaje que no los admite solo puede clasificarse como un lenguaje de programación funcional de segunda categoría? (Tenga en cuenta que mi experiencia con la programación funcional es bastante limitada).

David Johnstone
fuente
2
2018 - Coconut (un lenguaje de programación funcional que compila a Python) mejora la programación funcional en Python. Consulte también esta serie de artículos de IBM página1 página2 página3
cssyphus

Respuestas:

393

La pregunta a la que hace referencia pregunta qué lenguajes promueven tanto la OO como la programación funcional. Python no promueve la programación funcional a pesar de que funciona bastante bien.

El mejor argumento en contra de la programación funcional en Python es que Guido considera cuidadosamente los casos de uso imperativo / OO, mientras que los casos de uso de programación funcional no lo son. Cuando escribo Python imperativo, es uno de los lenguajes más bonitos que conozco. Cuando escribo Python funcional, se vuelve tan feo y desagradable como su lenguaje promedio que no tiene un BDFL .

Lo que no quiere decir que sea malo, solo que tiene que trabajar más duro de lo que lo haría si cambiara a un lenguaje que promueva la programación funcional o cambie a escribir OO Python.

Estas son las cosas funcionales que extraño en Python:


  • Sin coincidencia de patrones y sin recursión de cola significa que sus algoritmos básicos deben escribirse imperativamente. La recursión es fea y lenta en Python.
  • Una pequeña biblioteca de listas y ningún diccionario funcional significa que tienes que escribir muchas cosas tú mismo.
  • Sin sintaxis para el currículum o la composición significa que el estilo sin puntos está tan lleno de puntuación como los argumentos que pasan explícitamente.
  • Los iteradores en lugar de listas perezosas significan que debe saber si desea eficiencia o persistencia, y dispersar las llamadas a list si desea persistencia. (Los iteradores son de uso único)
  • La sintaxis imperativa simple de Python, junto con su analizador LL1 simple, significa que una mejor sintaxis para expresiones if y expresiones lambda es básicamente imposible. A Guido le gusta de esta manera, y creo que tiene razón.
Nathan Shively-Sanders
fuente
55
+1 por falta de recursión de la cola, aunque las construcciones en bucle lo han reemplazado, todavía vale la pena perderse entre Python y Scheme.
nuevo123456
55
Excelente respuesta en cuanto a integridad y composición. Por desgracia, como con tantas respuestas con una sólida base funcional, tiene un uso abusivo de la terminología por parte de la OMI. Si bien entiendo que no se puede elaborar cada concepto en una respuesta, me pregunto si el OP (con antecedentes de FP limitados admitidos) está bien informado al leer términos como "coincidencia de patrones", "diccionarios funcionales", "currículum" o " listas perezosas ".
ThomasH
44
Buen punto; Creo que la solución es agregar enlaces. ¿Tienes suficiente representante para editar mi respuesta? Si es así, siéntase libre de agregar enlaces a los diversos conceptos. Comenzaré cuando tenga tiempo más tarde.
Nathan Shively-Sanders
55
Me doy cuenta de que esto tiene 5 años, pero ... parece que se trata más de las cosas que extrañas de Haskell , no de los lenguajes funcionales . Por ejemplo, la mayoría de los dialectos y descendientes de ML y Lisp no tienen currificación automática, hacen que el estilo sin puntos sea demasiado detallado, no tienen listas perezosas, etc. Entonces, si los iteradores en lugar de las listas perezosas hacen de Python un mal lenguaje funcional, tener ¿tampoco debe hacer de CaML un lenguaje funcional terrible ?
abarnert
44
@abarnert: Caml tiene todas las viñetas excepto las listas diferidas, que están disponibles como una biblioteca. Ocasionalmente usé Caml cuando escribí esta respuesta y actualmente uso F #. Ambos son lenguajes funcionales muy agradables.
Nathan Shively-Sanders
102

Guido tiene una buena explicación de esto aquí . Aquí está la parte más relevante:

Nunca he considerado que Python esté fuertemente influenciado por lenguajes funcionales, sin importar lo que la gente diga o piense. Estaba mucho más familiarizado con lenguajes imperativos como C y Algol 68 y, aunque había creado funciones de objetos de primera clase, no veía Python como un lenguaje de programación funcional. Sin embargo, anteriormente, estaba claro que los usuarios querían hacer mucho más con listas y funciones.

...

También vale la pena señalar que, aunque no imaginé Python como un lenguaje funcional, la introducción de cierres ha sido útil en el desarrollo de muchas otras características avanzadas de programación. Por ejemplo, ciertos aspectos de las nuevas clases de estilo, decoradores y otras características modernas dependen de esta capacidad.

Por último, a pesar de que se han introducido una serie de características de programación funcional a lo largo de los años, Python aún carece de ciertas características que se encuentran en lenguajes de programación funcional "reales". Por ejemplo, Python no realiza ciertos tipos de optimizaciones (p. Ej., Recursión de cola). En general, debido a la naturaleza extremadamente dinámica de Python, es imposible hacer el tipo de optimización en tiempo de compilación conocida de lenguajes funcionales como Haskell o ML. Y eso está bien.

Saco dos cosas de esto:

  1. El creador del lenguaje realmente no considera que Python sea un lenguaje funcional. Por lo tanto, es posible ver características "funcionales-esque", pero es poco probable que vea algo que sea definitivamente funcional.
  2. La naturaleza dinámica de Python inhibe algunas de las optimizaciones que ve en otros lenguajes funcionales. Por supuesto, Lisp es tan dinámico (si no más dinámico) como Python, por lo que esta es solo una explicación parcial.
Jason Baker
fuente
8
Puede hacer una optimización de llamadas de cola en Python muy bien. Guido no entiende / no entendió eso.
Jules
26
Parece reducirse a que a Guido van Rossum no le gusta el estilo funcional.
Svante
59
Creo que es más exacto decir que Guido van Rossum no entiende el estilo funcional y no entiende por qué Python los necesita. Debe comprender dos cosas: 1) los lenguajes de programación están en la parte inferior de una pila de tecnología y afectan todo lo que se construye sobre ellos y 2) al igual que cualquier otra pieza de software, es más fácil agregar funciones que eliminarlas. Así que creo que es una buena calidad que un diseñador de idiomas sea crítico con este tipo de solicitudes.
Jason Baker
8
"De acuerdo, Lisp es igual de dinámico" -> ¡e igual de imperativo!
pyon
66
@Jules, ¿te importaría compartir una guía para el uso de la optimización de llamadas de cola en Python? Un puntero a alguna fuente sería útil.
David sacudió el
52

El esquema no tiene tipos de datos algebraicos o coincidencia de patrones, pero sin duda es un lenguaje funcional. Cosas molestas sobre Python desde una perspectiva de programación funcional:

  1. Lambdas lisiadas. Dado que Lambdas solo puede contener una expresión, y no puede hacer todo tan fácilmente en un contexto de expresión, esto significa que las funciones que puede definir "sobre la marcha" son limitadas.

  2. Ifs son declaraciones, no expresiones. Esto significa, entre otras cosas, que no puedes tener una lambda con un If dentro. (Esto se soluciona mediante ternaries en Python 2.5, pero se ve feo).

  3. Guido amenaza con eliminar el mapa, filtrar y reducir de vez en cuando

Por otro lado, Python tiene cierres léxicos, Lambdas y listas de comprensión (que son realmente un concepto "funcional", lo admita o no Guido). Hago mucha programación de "estilo funcional" en Python, pero difícilmente diría que es ideal.

Jacob B
fuente
3
Mapear, filtrar y reducir realmente no son necesarios en Python. Todavía tengo que ver un fragmento de código que se simplificó mucho al usarlos. Además, las funciones de llamada en Python pueden ser costosas, por lo que generalmente es mejor usar una comprensión de lista / generador o un bucle for de todos modos.
Jason Baker
2
Esto es exactamente lo que Nathan Sanders dice a continuación: "Python no promueve la programación funcional a pesar de que funciona bastante bien". Si Guido quisiera que Python se convirtiera en un lenguaje funcional, haría que la implementación funcionara lo suficientemente bien como para usar funciones descartables, y eliminaría el título de Lambdas en la medida en que realmente pueda usar map / filter / reduce de maneras útiles. Por otro lado, las personas funcionales están comenzando a despertarse con la genialidad de las listas de comprensión. Esperemos que no tengamos que elegir uno u otro.
Jacob B
77
El mapa y el filtro se reemplazan trivialmente por una comprensión de la lista. reducir, casi siempre, es tan ineficiente que debería reemplazarse con una función de generador.
S.Lott
13
@ S.Lott, ¿cómo reemplazas reducir con un generador?
Antimonio
17
Las comprensiones de @JacobB List estaban disponibles en lenguajes funcionales aproximadamente 15 años antes de que se inventara Python y 25 años antes de que Python obtuviera una implementación de la función. La idea de que Python ha influido en su propagación, o que fp aprendió esto de Python, o incluso simplemente que su popularidad en el mundo de fp es posterior a la implementación de Python, es simplemente errónea. La implementación de Python fue tomada directamente de Haskell. Tal vez te he entendido mal y eso no es lo que querías decir, pero estoy perplejo por "las personas funcionales están empezando a despertar ante la genialidad de las listas de comprensión".
itsbruce
23

Nunca llamaría a Python "funcional", pero cada vez que programo en Python, el código termina siendo casi puramente funcional.

Es cierto que eso se debe principalmente a la comprensión de la lista extremadamente agradable. Por lo tanto, no necesariamente sugeriría Python como un lenguaje de programación funcional, pero sugeriría una programación funcional para cualquier persona que use Python.

Konrad Rudolph
fuente
17

Permítanme demostrar con un fragmento de código tomado de una respuesta a una pregunta de Python "funcional" sobre SO

Pitón:

def grandKids(generation, kidsFunc, val):
  layer = [val]
  for i in xrange(generation):
    layer = itertools.chain.from_iterable(itertools.imap(kidsFunc, layer))
  return layer

Haskell

grandKids generation kidsFunc val =
  iterate (concatMap kidsFunc) [val] !! generation

La diferencia principal aquí es que la biblioteca estándar de Haskell tiene funciones útiles para la programación funcional: en este caso iterate, concaty(!!)

yairchu
fuente
77
Aquí hay un reemplazo de una línea para grandKids()el cuerpo con el generador de expresiones: return reduce(lambda a, v: concat((x for x in kidsFunc(v)) for v in a), xrange(generation), [val]).
Lloeki el
66
Y aquí hay uno que tampoco necesita concat:return reduce(lambda a, v: (x for v in a for x in kidsFunc(v)), xrange(generation), [val])
Lloeki
99
@Lloeki: iterar simplificaría significativamente ese código, y (x para v en a para x en kidsFunc (v)) es mucho más claro como concatMap (kidsFunc). La falta de Python de bonitas construcciones de orden superior hace que el código equivalente sea críptico y detallado en comparación con Haskell.
Phob
2
concat puede ser reemplazado poritertools.chain.from_iterable
Antimony
@Antimonio: es bueno saberlo. thx
yairchu
14

Una cosa que es realmente importante para esta pregunta (y las respuestas) es la siguiente: qué demonios es la programación funcional y cuáles son sus propiedades más importantes. Trataré de dar mi punto de vista:

La programación funcional es muy parecida a escribir matemáticas en una pizarra. Cuando escribe ecuaciones en una pizarra, no piensa en una orden de ejecución. No hay (típicamente) ninguna mutación. No regresas al día siguiente y lo miras, y cuando haces los cálculos nuevamente, obtienes un resultado diferente (o puedes, si tomaste un café recién hecho :)). Básicamente, lo que está en la pizarra está ahí, y la respuesta ya estaba allí cuando comenzaste a escribir las cosas, simplemente no te has dado cuenta de lo que es todavía.

La programación funcional se parece mucho a eso; no cambia las cosas, simplemente evalúa la ecuación (o en este caso, "programa") y descubre cuál es la respuesta. El programa sigue ahí, sin modificaciones. Lo mismo con los datos.

Clasificaría lo siguiente como las características más importantes de la programación funcional: a) transparencia referencial: si evalúa la misma declaración en otro momento y lugar, pero con los mismos valores variables, seguirá significando lo mismo. b) sin efectos secundarios: no importa cuánto tiempo mires la pizarra, la ecuación que otro chico está mirando a otra pizarra no cambiará accidentalmente. c) las funciones también son valores. que se puede pasar y aplicar con, o a, otras variables. d) composición de funciones, puede hacer h = g · f y así definir una nueva función h (..) que es equivalente a llamar a g (f (..)).

Esta lista está en mi orden de prioridad, por lo que la transparencia referencial es la más importante, seguida de ningún efecto secundario.

Ahora, si revisa Python y verifica qué tan bien el lenguaje y las bibliotecas soportan y garantizan estos aspectos, entonces está en camino de responder su propia pregunta.

xeno
fuente
2
Las funciones son de primera clase en Python.
Carl Smith
@CarlSmith Ese, pero deja 3/4 que Python no tiene. : - \
arya
1
No creo que Python sea un buen lenguaje para la programación funcional. Ahora ni siquiera estoy seguro de lo que quise decir con ese comentario para ser honesto. No parece relevante para la respuesta. Lo eliminaría, pero luego tu comentario estaría fuera de contexto.
Carl Smith
1
La transparencia referencial y la inmutabilidad no son realmente características del lenguaje. Sí, algunos idiomas (Haskell) los enfatizan y dificultan no tenerlos, pero puedes hacer una función referencialmente transparente o un objeto inmutable en prácticamente cualquier idioma. Solo tiene que evitar la biblioteca estándar, que a menudo las violará.
Kevin
Además, Python tiene soporte para currículum y composición, pero no a nivel de lenguaje, sino que está en la biblioteca estándar.
Kevin
10

Python es casi un lenguaje funcional. Es "funcional lite".

Tiene características adicionales, por lo que no es lo suficientemente puro para algunos.

También carece de algunas características, por lo que no es lo suficientemente completo para algunos.

Las características que faltan son relativamente fáciles de escribir. Echa un vistazo a publicaciones como esta en FP en Python.

S.Lott
fuente
2
En su mayor parte, estoy de acuerdo con esta publicación. Pero no puedo estar de acuerdo con decir que Python es un lenguaje funcional. Alienta mucho la programación imperativa, y es difícil no hacerlo con las "características adicionales" que mencionas. Creo que es mejor referirse a Python como "lite funcional" como lo hizo en la otra publicación. :-)
Jason Baker
8
-1 Lo siento, no. Quiero decir, solo que no. Los lenguajes funcionales proporcionan construcciones que facilitan el razonamiento formal: inductivo (ML), equitativo (Haskell). Los cierres y las funciones anónimas son solo azúcar sintáctica para el patrón de estrategia.
pyon
8

Otra razón no mencionada anteriormente es que muchas funciones y métodos integrados de tipos incorporados modifican un objeto pero no devuelven el objeto modificado. Si se devolvieran esos objetos modificados, eso haría que el código funcional fuera más limpio y conciso. Por ejemplo, si some_list.append (some_object) devuelve some_list con some_object adjunto.

DVD Avins
fuente
4

Además de otras respuestas, una de las razones por las que Python y la mayoría de los otros lenguajes de paradigmas múltiples no son adecuados para una verdadera programación funcional es porque sus compiladores / máquinas virtuales / tiempos de ejecución no admiten la optimización funcional. El compilador logra este tipo de optimización entendiendo las reglas matemáticas. Por ejemplo, muchos lenguajes de programación admiten una mapfunción o método. Esta es una función bastante estándar que toma una función como un argumento y un iterable como el segundo argumento y luego aplica esa función a cada elemento en el iterable.

De todos modos resulta que map( foo() , x ) * map( foo(), y ) es lo mismo que map( foo(), x * y ). El último caso es en realidad más rápido que el primero porque el primero realiza dos copias donde el último realiza una.

Los mejores lenguajes funcionales reconocen estas relaciones basadas en matemáticas y realizan automáticamente la optimización. Los lenguajes que no están dedicados al paradigma funcional probablemente no se optimizarán.


fuente
map( foo() , x ) * map( foo(), y ) == map( foo(), x * y )No es cierto para todas las funciones. Por ejemplo, considere el caso cuando foocalcule una derivada.
Eli Korvigo
1
Creo que quiso decir en +lugar de *.
user1747134
¿Asume que foo () es lineal?
juan Isaza
esa propiedad NO es verdadera para foo (x) = x + 1. Como (x + 1) * (y + 1)! = X * y + 1.
juan Isaza