Pregunta

Recientemente he estado aprendiendo sobre programación funcional (específicamente Haskell, pero también he revisado tutoriales sobre Lisp y Erlang). Si bien encontré los conceptos muy esclarecedores, todavía no veo el lado práctico de & "; Sin efectos secundarios &"; concepto. ¿Cuáles son las ventajas prácticas de esto? Estoy tratando de pensar en la mentalidad funcional, pero hay algunas situaciones que parecen demasiado complejas sin la capacidad de guardar el estado de una manera fácil (no considero que las mónadas de Haskell sean "fáciles").

¿Vale la pena continuar aprendiendo Haskell (u otro lenguaje puramente funcional) en profundidad? ¿Es la programación funcional o sin estado realmente más productiva que procesal? ¿Es probable que continúe usando Haskell u otro lenguaje funcional más tarde, o debería aprenderlo solo para entenderlo?

Me importa menos el rendimiento que la productividad. Así que principalmente pregunto si seré más productivo en un lenguaje funcional que en un procedimiento / orientado a objetos / lo que sea.

¿Fue útil?

Solución

Lea Programación funcional en pocas palabras .

La programación sin estado tiene muchas ventajas, entre las cuales se encuentra dramáticamente el código multiproceso y concurrente. Para decirlo sin rodeos, el estado mutable es enemigo del código multiproceso. Si los valores son inmutables por defecto, los programadores no tienen que preocuparse de que un subproceso mute el valor del estado compartido entre dos subprocesos, por lo que elimina toda una clase de errores de subprocesos múltiples relacionados con las condiciones de carrera. Dado que no hay condiciones de carrera, tampoco hay razón para usar bloqueos, por lo que la inmutabilidad elimina otra clase completa de errores relacionados con los puntos muertos también.

Esa es la gran razón por la cual la programación funcional es importante, y probablemente la mejor para saltar al tren de programación funcional. También hay muchos otros beneficios, incluida la depuración simplificada (es decir, las funciones son puras y no mutan el estado en otras partes de una aplicación), un código más conciso y expresivo, menos código repetitivo en comparación con los idiomas que dependen en gran medida de los patrones de diseño, y el compilador puede optimizar más agresivamente su código.

Otros consejos

Cuantas más partes de su programa no tengan estado, más formas hay de juntar piezas sin que nada se rompa . El poder del paradigma sin estado no reside en la apatridia (o la pureza) per se , sino en la capacidad que le brinda para escribir funciones poderosas, reutilizables y combinarlas.

Puede encontrar un buen tutorial con muchos ejemplos en el artículo de John Hughes Por qué es importante la programación funcional (PDF).

Serás gobs más productivo, especialmente si eliges un lenguaje funcional que también tenga tipos de datos algebraicos y coincidencia de patrones (Caml, SML, Haskell).

Muchas de las otras respuestas se han centrado en el lado del rendimiento (paralelismo) de la programación funcional, lo que creo que es muy importante. Sin embargo, usted preguntó específicamente sobre la productividad, ya que, en, ¿puede programar lo mismo más rápido en un paradigma funcional que en un paradigma imperativo?

De hecho, encuentro (por experiencia personal) que la programación en F # coincide con la forma en que pienso mejor, por lo que es más fácil. Creo que esa es la mayor diferencia. He programado tanto en F # como en C #, y hay mucho menos & "; Luchando contra el lenguaje &"; en F #, que me encanta. No tiene que pensar en los detalles en F #. Aquí hay algunos ejemplos de lo que he encontrado que realmente disfruto.

Por ejemplo, aunque F # esté tipado estáticamente (todos los tipos se resuelven en tiempo de compilación), la inferencia de tipos determina qué tipos tiene, por lo que no tiene que decirlo. Y si no puede resolverlo, automáticamente convierte su función / clase / lo que sea genérico. Así que nunca tienes que escribir ningún genérico, todo es automático. Creo que eso significa que paso más tiempo pensando en el problema y menos en cómo implementarlo. De hecho, cada vez que vuelvo a C #, encuentro que realmente extraño esta inferencia de tipo, nunca te das cuenta de lo molesto que es hasta que ya no necesitas hacerlo.

También en F #, en lugar de escribir bucles, se llaman funciones. Es un cambio sutil, pero significativo, porque ya no tienes que pensar en la construcción del bucle. Por ejemplo, aquí hay un código que pasaría y coincidiría con algo (no recuerdo qué, es de un rompecabezas de Euler del proyecto):

let matchingFactors =
    factors
    |> Seq.filter (fun x -> largestPalindrome % x = 0)
    |> Seq.map (fun x -> (x, largestPalindrome / x))

Me doy cuenta de que hacer un filtro y luego un mapa (que es una conversión de cada elemento) en C # sería bastante simple, pero hay que pensar en un nivel inferior. En particular, tendrías que escribir el ciclo en sí y tener tu propia declaración if explícita, y ese tipo de cosas. Desde que aprendí F #, me di cuenta de que me resulta más fácil codificar de manera funcional, donde si quieres filtrar, escribes & Quot; filter & Quot ;, y si quieres mapear, escriba " map " ;, en lugar de implementar cada uno de los detalles.

También me encanta el | > operador, que creo que separa F # de ocaml, y posiblemente otros lenguajes funcionales. Es el operador de tubería, te permite & "; Tubería &"; La salida de una expresión en la entrada de otra expresión. Hace que el código siga cómo pienso más. Al igual que en el fragmento de código anterior, es decir, & Quot; tome la secuencia de factores, filtre y luego asigne. & Quot; Es un nivel de pensamiento muy alto, que no se obtiene en un lenguaje de programación imperativo porque estás muy ocupado escribiendo el bucle y las declaraciones if. Es lo único que más extraño cada vez que voy a otro idioma.

Entonces, en general, aunque puedo programar tanto en C # como en F #, me resulta más fácil usar F # porque puedes pensar en un nivel superior. Yo diría que debido a que los detalles más pequeños se eliminan de la programación funcional (en F # al menos), soy más productivo.

Editar : vi en uno de los comentarios que solicitó un ejemplo de " state " en un lenguaje de programación funcional. F # se puede escribir imperativamente, así que aquí hay un ejemplo directo de cómo puede tener un estado mutable en F #:

let mutable x = 5
for i in 1..10 do
    x <- x + i

Considere todos los errores difíciles que ha pasado mucho tiempo depurando.

Ahora, ¿cuántos de esos errores se debieron a " interacciones no intencionadas " entre dos componentes separados de un programa? (Casi todos los errores de subprocesos tienen esta forma: razas que implican escribir datos compartidos, puntos muertos, ... Además, es común encontrar bibliotecas que tienen algún efecto inesperado en el estado global, o leer / escribir el registro / entorno, etc.) < em> I postularía que al menos 1 de cada 3 'errores duros' entran en esta categoría.

Ahora, si cambias a programación sin estado / inmutable / pura, todos esos errores desaparecerán. En su lugar, se le presentan algunos desafíos nuevos (por ejemplo, cuando do desea que diferentes módulos interactúen con el entorno), pero en un lenguaje como Haskell, esas interacciones se reifican explícitamente en el sistema de tipos, lo que significa que solo puede mirar el tipo de función y razonar sobre el tipo de interacciones que puede tener con el resto del programa.

Esa es la gran victoria de la 'inmutabilidad' IMO. En un mundo ideal, todos diseñaríamos API fabulosas e incluso cuando las cosas fueran mutables, los efectos serían locales y bien documentados y las interacciones 'inesperadas' se reducirían al mínimo. En el mundo real, hay muchas API que interactúan con el estado global de innumerables maneras, y estas son la fuente de los errores más perniciosos. Aspirar a la apatridia es aspirar a deshacerse de las interacciones no intencionadas / implícitas / detrás de escena entre los componentes.

Una ventaja de las funciones sin estado es que permiten el cálculo previo o el almacenamiento en caché de los valores de retorno de la función. Incluso algunos compiladores de C le permiten marcar explícitamente funciones como apátridas para mejorar su optimizabilidad. Como muchos otros han notado, las funciones sin estado son mucho más fáciles de paralelizar.

Pero la eficiencia no es la única preocupación. Una función pura es más fácil de probar y depurar ya que cualquier cosa que la afecte se indica explícitamente. Y cuando se programa en un lenguaje funcional, uno tiene la costumbre de hacer tan pocas funciones & Quot; dirty & Quot; (con E / S, etc.) como sea posible. Separar las cosas con estado de esta manera es una buena manera de diseñar programas, incluso en lenguajes no tan funcionales.

Los lenguajes funcionales pueden tardar un tiempo en " get " ;, y es difícil de explicar a alguien que no ha pasado por ese proceso. Pero la mayoría de las personas que persisten el tiempo suficiente finalmente se dan cuenta de que el alboroto vale la pena, incluso si no terminan usando mucho los lenguajes funcionales.

Sin estado, es muy fácil paralelizar automáticamente su código (ya que las CPU están hechas con más y más núcleos, esto es muy importante).

Escribí una publicación sobre este tema hace un tiempo: Sobre la importancia de la pureza .

Las aplicaciones web sin estado son esenciales cuando comienza a tener un mayor tráfico.

Podría haber muchos datos de usuario que no desea almacenar en el lado del cliente por razones de seguridad, por ejemplo. En este caso, debe almacenarlo en el lado del servidor. Puede usar la sesión predeterminada de las aplicaciones web, pero si tiene más de una instancia de la aplicación, deberá asegurarse de que cada usuario esté siempre dirigido a la misma instancia.

Los equilibradores de carga a menudo tienen la capacidad de tener 'sesiones fijas' donde el equilibrador de carga sabe de algún modo a qué servidor enviar la solicitud de los usuarios. Sin embargo, esto no es ideal, por ejemplo, significa que cada vez que reinicie su aplicación web, todos los usuarios conectados perderán su sesión.

Un mejor enfoque es almacenar la sesión detrás de los servidores web en algún tipo de almacén de datos, en estos días hay un montón de excelentes productos nosql disponibles para esto (redis, mongo, elasticsearch, memcached). De esta forma, los servidores web no tienen estado, pero aún tiene un estado del lado del servidor y la disponibilidad de este estado se puede administrar eligiendo la configuración correcta del almacén de datos. Estos almacenes de datos suelen tener una gran redundancia, por lo que casi siempre debería ser posible realizar cambios en su aplicación web e incluso en el almacén de datos sin afectar a los usuarios.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top