¿Son comprensibles las pequeñas cantidades de programación funcional para personas que no son FP? [cerrado]

43

Caso : estoy trabajando en una empresa, escribiendo una aplicación en Python que maneja una gran cantidad de datos en matrices. Soy el único desarrollador de este programa en este momento, pero probablemente será utilizado / modificado / extendido en el futuro (1-3 años) por algún otro programador, en este momento desconocido para mí. Probablemente no estaré allí directamente para ayudarlo, pero tal vez brinde algún soporte por correo electrónico si tengo tiempo para hacerlo.

Entonces, como desarrollador que ha aprendido programación funcional (Haskell), tiendo a resolver, por ejemplo, filtrar así:

filtered = filter(lambda item: included(item.time, dur), measures)

El resto del código es OO, solo son algunos casos pequeños donde quiero resolverlo así, porque es mucho más simple y más hermoso según yo.

Pregunta : ¿Está bien hoy escribir código como este?

  • ¿Cómo reacciona un desarrollador que no ha escrito / aprendido FP al código como este?
  • ¿Es legible?
  • Modificable?
  • ¿Debo escribir documentación como explicarle a un niño lo que hace la línea?

     # Filter out the items from measures for which included(item.time, dur) != True

Le pregunté a mi jefe, y él simplemente dice "FP es magia negra, pero si funciona y es la solución más eficiente, entonces está bien usarla".

¿Qué opinas de esto? Como programador que no es FP, ¿cómo reaccionas al código? ¿Es el código "googable" para que pueda entender lo que hace? Me encantaría recibir comentarios sobre esto.

kd35a
fuente
14
En mi opinión, muchos lenguajes modernos permiten cierto grado de programación funcional y su uso se vuelve cada vez más común. Personalmente, diría que lo haga (al menos para las tareas de filtrado / mapeo relativamente simples).
Joachim Sauer
No puedo darle una respuesta no afectada, porque me gusta FP. Sería una buena idea agregar un comentario, si es más que simple.
Bruno Schäpper
3
Tu comentario podría ser más claro; El filtro incluye elementos para los cuales la prueba es Verdadera, por lo que su comentario podría leer: # Select the item's from measures for which included(item.time, dur) == Trueevitar un doble negativo siempre mejora la comprensión.
Martijn Pieters
66
¿Has considerado usar listas de comprensión en su lugar? A menudo se los considera más "pitónicos" que el mapa / filtro.
phant0m
2
@MatjazMuhic Bueno, eso es hermoso ...;)
kd35a

Respuestas:

50

¿Es legible?

Para mí: Sí, pero he llegado a comprender que la comunidad de Python a menudo parece considerar que las comprensiones de listas son una solución más limpia que usar map()/ filter().

De hecho, GvR incluso consideró abandonar esas funciones por completo.

Considera esto:

filtered = [item for item in measures if included(item.time, dur)]

Además, esto tiene el beneficio de que una comprensión de la lista siempre devolverá una lista. map()y filter()por otro lado devolverá un iterador en Python 3.

Nota: Si quieres tener un iterador, es tan simple como reemplazarlo []por ():

filtered = (item for item in measures if included(item.time, dur))

Para ser honesto, veo poca o ninguna razón para usar map()o filter()en Python.

¿Es modificable?

Sí, ciertamente, sin embargo, hay una cosa que lo hace más fácil: que sea una función, no una lambda.

def is_included(item):
    return included(item.time, dur)
filtered = filter(is_included, measures)
filtered = [item for item in measures if is_included(item)]

Si su condición se vuelve más compleja, esto se escalará mucho más fácilmente, además, le permite reutilizar su cheque. (Tenga en cuenta que puede crear funciones dentro de otras funciones, esto puede mantenerlo más cerca del lugar donde se usa).

¿Cómo reacciona un desarrollador que no ha escrito / aprendido FP en un código como este?

Busca en Google la documentación de Python y sabe cómo funciona cinco minutos después. De lo contrario, no debería estar programando en Python.

map()y filter()son extremadamente simples No es como si les estuvieras pidiendo que entiendan las mónadas. Es por eso que no creo que necesites escribir tales comentarios. Use buenos nombres de variables y funciones, entonces el código se explica por sí mismo. No puede anticipar qué características de idioma no conoce un desarrollador. Por lo que sabes, el próximo desarrollador podría no saber qué es un diccionario.

Lo que no entendemos generalmente no es legible para nosotros. Por lo tanto, podría argumentar que no es menos legible que una lista de comprensión si nunca antes ha visto ninguno de ellos. Pero como Joshua mencionó en su comentario, también creo que es importante ser coherente con lo que usan otros desarrolladores, al menos si la alternativa no ofrece una ventaja sustancial.

phant0m
fuente
55
También podría valer la pena mencionar las expresiones generadoras , que funcionan igual que las comprensiones de listas pero devuelven generadores en lugar de listas, como mapy filterhacen en python 3.
James
@ James: Gran idea, los he agregado a mi publicación. ¡Gracias!
phant0m
2
Por lo general, reducecomo un contraejemplo, siempre se puede usar un argumento de comprensión de la lista , ya que es relativamente difícil reemplazarlo reducecon un generador en línea o una comprensión de la lista.
kojiro
44
+1 por notar el idioma preferido del idioma en cuestión. La coherencia en mi humilde opinión tiene un gran valor y el uso de un lenguaje de la manera en que una parte importante de los "hablantes" puede proporcionar más capacidad de mantenimiento que incluso los comentarios a veces.
Joshua Drake
44
+1 para "Busca en Google la documentación de Python y sabe cómo funciona cinco minutos después. De lo contrario, no debería estar programando en Python".
Bjarke Freund-Hansen
25

Dado que la comunidad de desarrolladores está recuperando interés en la programación funcional, no es inusual ver algo de programación funcional en lenguajes que originalmente estaban completamente orientados a objetos. Un buen ejemplo es C #, donde los tipos anónimos y las expresiones lambda permiten ser mucho más cortos y expresivos a través de la programación funcional.

Dicho esto, la programación funcional es extraña para los principiantes. Por ejemplo, cuando durante un curso de capacitación, les expliqué a los principiantes cómo pueden mejorar su código a través de la programación funcional en C #, algunos de ellos no estaban convencidos, y algunos dijeron que el código original era más fácil de entender para ellos. El ejemplo que les di fue el siguiente:

Código antes de refactorizar:

var categorizedProducts = new Dictionary<string, List<Product>>();

// Get only enabled products, filtering the disabled ones, and group them by categories.
foreach (var product in this.Data.Products)
{
    if (product.IsEnabled)
    {
        if (!categorizedProducts.ContainsKey(product.Category))
        {
            // The category is missing. Create one.
            categorizedProducts.Add(product.Category, new List<Product>());
        }

        categorizedProducts[product.Category].Add(product);
    }
}

// Walk through the categories.
foreach (var productsInCategory in categorizedProducts)
{
    var minimumPrice = double.MaxValue;
    var maximumPrice = double.MinValue;

    // Walk through the products in a category to search for the maximum and minimum prices.
    foreach (var product in productsInCategory.Value)
    {
        if (product.Price < minimumPrice)
        {
            minimumPrice = product.Price;
        }

        if (product.Price > maximumPrice)
        {
            maximumPrice = product.Price;
        }
    }

    yield return new PricesPerCategory(category: productsInCategory.Key, minimum: minimumPrice, maximum: maximumPrice);
}

Mismo código después de refactorizar usando FP:

return this.Data.Products
    .Where(product => product.IsEnabled)
    .GroupBy(product => product.Category)
    .Select(productsInCategory => new PricesPerCategory(
              category: productsInCategory.Key, 
              minimum:  productsInCategory.Value.Min(product => product.Price), 
              maximum:  productsInCategory.Value.Max(product => product.Price))
    );

Esto me hace pensar que:

  • No debe preocuparse si sabe que el próximo desarrollador que mantendrá su código tendrá suficiente experiencia general y algunos conocimientos de programación funcional, pero:

  • de lo contrario, evite la programación funcional o escriba un comentario detallado que explique al mismo tiempo la sintaxis, las ventajas y las posibles advertencias de su enfoque frente a una programación no funcional.

Arseni Mourzenko
fuente
8
+1 si FP no hace que su código sea más legible para nadie , lo está haciendo mal.
György Andrasek
77
Puedo ver de forma aislada que alguien que nunca antes haya visto FP podría considerar que el fragmento anterior es más legible que el segundo. Pero, ¿y si multiplicamos esto por 100? Lectura de las 100 oraciones breves, breves, casi en inglés, que describen el qué frente a miles de líneas de plomería cómo codifican. El gran volumen de código, no importa cuán familiar, debe ser tan abrumador que la familiaridad ya no es una gran victoria. En algún momento, debe ser más fácil para cualquiera sentirse cómodo con los elementos de FP y luego leer un puñado de líneas en lugar de leer miles de líneas de código familiar.
Esailija
77
Lo que más me molesta es que estamos contentos de creer que es aceptable que los desarrolladores no aprendan el estilo funcional. Las ventajas se han demostrado muchas veces, el paradigma es compatible con los idiomas principales. Si las personas carecen de la capacidad de comprender las técnicas funcionales, se les debe enseñar o deben enseñarse a sí mismas, incluso si no tienen la intención de usarlas. Odio XSLT con una pasión ardiente, pero eso no me ha impedido aprenderlo como profesional.
Sprague
2
@MainMa Su punto es completamente válido, debería haber sido más específico. Lo que quiero decir es que se debe esperar que un desarrollador en cualquier idioma use las características de esos idiomas de manera efectiva. Por ejemplo, usar 'estilo funcional' en C # (usando programación de filtro / mapa / reducir estilo, o pasar / regresar funciones) no es nada nuevo, por lo que creo que cualquier desarrollador de C # que no sea capaz de leer tales declaraciones debería actualizar sus habilidades ... probablemente muy rápido. Ciertamente creo que todos los desarrolladores deberían aprender un poco de Haskell, pero solo por razones académicas. Esto es algo diferente.
Sprague
2
@Sprague: entiendo tu punto y estoy de acuerdo con eso. Es solo que difícilmente podemos esperar, por ejemplo, que los programadores de C # comiencen a usar paradigmas de FP, cuando muchos de esos programadores ni siquiera usan genéricos. Hasta que haya tantos programadores no calificados, la barra en nuestra profesión será baja, especialmente porque la mayoría de las personas sin antecedentes técnicos no entienden en absoluto de qué se trata el desarrollo.
Arseni Mourzenko
20

Soy un programador que no es FP y recientemente tuve que modificar el código de mi colega en JavaScript. Hubo una solicitud Http con una devolución de llamada que se parecía mucho a la declaración incluida por usted. Debo decir que me tomó algo de tiempo (como media hora) resolverlo (el código en general no era muy grande).

No hubo comentarios, y creo que no hubo necesidad de ninguno. Ni siquiera le pedí a mi colega que me ayudara a entender su código.

Teniendo en cuenta que estoy trabajando durante aproximadamente 1,5 años, creo que la mayoría de los programadores podrán entender y modificar dicho código, como lo hice.

Además, como dijo Joachim Sauer en su comentario, a menudo hay piezas de FP en muchos idiomas, como C # (indexOf, por ejemplo). Muchos programadores que no son FP se ocupan de esto con bastante frecuencia, y el fragmento de código que incluyó no es algo terrible o incomprensible.

superM
fuente
1
¡Gracias por tu comentario! Me dio más información sobre la otra perspectiva. Es fácil quedarse ciego en casa, y luego no sabes cómo era cuando no conocías FP :)
kd35a
1
@ kd35a, de nada. Afortunadamente, puedo ser objetivo aquí))
superM
1
+1 por señalar que muchos idiomas ahora contienen elementos de FP.
Joshua Drake
LINQ es el ejemplo principal de FP en C #
Konrad Morawski
3

Definitivamente diría que sí!

Hay muchos aspectos de la programación funcional, y el uso de funciones de orden superior, como en su ejemplo, es solo uno de ellos.

Por ejemplo, considero que escribir funciones puras es extremadamente importante para cualquier software escrito en cualquier idioma (donde "puro" no significa efectos secundarios), porque:

  • son más fáciles de prueba unitaria
  • son mucho más componibles que las funciones de efectos secundarios
  • son más fáciles de depurar

También evito a menudo mutar valores y variables, otro concepto tomado de FP.

Ambas técnicas funcionan bien en Python y otros lenguajes que no se clasifican comúnmente como funcionales. A menudo, incluso son compatibles con el lenguaje en sí (es decir, finalvariables en Java). Por lo tanto, los futuros programadores de mantenimiento no enfrentarán una enorme barrera para comprender el código.


fuente
2

Tuvimos esta misma discusión sobre una compañía para la que trabajé el año pasado.

La discusión se refería al "código mágico" y si debía alentarse o no. Al analizarlo un poco más, parecía que las personas tenían puntos de vista muy diferentes sobre lo que en realidad era "código mágico". Los que mencionaron la discusión parecían significar principalmente que las expresiones (en PHP) que usaban el estilo funcional eran "código mágico", mientras que los desarrolladores que se originaron en otros lenguajes que usaban más estilo FP en su código parecen pensar que el código mágico era más bien cuando hiciste una inclusión dinámica de archivos a través de nombres de archivos, etc.

Nunca llegamos a una buena conclusión sobre esto, más allá de que la mayoría de la gente piense que el código que parece desconocido es "mágico" o difícil de leer. Entonces, ¿es una buena idea evitar el código que parece desconocido para otros usuarios? Creo que depende de dónde se use. Me abstendría de usar expresiones de estilo fp (inclusión dinámica de archivos, etc.) en un método principal (o partes centrales importantes de las aplicaciones) donde los datos deben ser tunelizados de una manera clara, fácil de leer e intuitiva. Por otro lado, no creo que uno deba temer empujar el sobre, los otros desarrolladores probablemente aprenderán FP rápidamente si se enfrentan con un código FP y tal vez tengan algún buen recurso interno para consultar sobre los problemas.

TL; DR: Evite en la parte central de alto nivel de las aplicaciones (que deben leerse para obtener una descripción general de la funcionalidad de la aplicación). De lo contrario, úsalo.

Christopher Käck
fuente
Buen punto para evitar usarlo en un código de nivel superior.
kd35a
Siempre he pensado que hay falta de formalismo y muchas oportunidades para definir técnicas de programación en diferentes tipos de bloques (como métodos estáticos, métodos públicos, constructores, etc.) Buena respuesta ... me está haciendo revisar algunas de esos pensamientos
Sprague
2

La comunidad C ++ también obtuvo lambda recientemente, y creo que tienen aproximadamente la misma pregunta. Sin embargo, la respuesta puede no ser la misma. El equivalente de C ++ sería:

std::copy_if(measures.begin(), measures.end(), inserter(filter),
  [dur](Item i) { return included(i, dur) } );

Ahora std::copyno es nuevo, y las _ifvariantes tampoco lo son, pero la lambda sí. Sin embargo, se define con bastante claridad en el contexto: durse captura y, por lo tanto, Item ies constante, varía en el ciclo y la única returndeclaración hace todo el trabajo.

Esto parece aceptable para muchos desarrolladores de C ++. Sin embargo, no he muestreado opiniones sobre lambda de orden superior, y esperaría mucha menos aceptación.

MSalters
fuente
Punto interesante, que puede haber diferentes respuestas dependiendo del idioma en el que se encuentre. Probablemente relacionado con la publicación de @Christopher Käck sobre cómo los codificadores PHP tuvieron más problemas con este tipo de cosas que los codificadores Python.
kd35a
0

Publique un fragmento de código en un desarrollador que no sea tan fluido en Python y pregúntele si puede pasar 5 minutos revisando el código para ver si él / ella lo entiende.

En caso afirmativo, probablemente estés listo para ir. Si no es así, debe considerar aclararlo.

¿Podría su colega ser tonto y no entender algo que debería ser obvio? Sí, pero siempre debe programar de acuerdo con KISS.

¿Tal vez su código es más eficiente / atractivo / elegante que un enfoque más directo y a prueba de idiotas? Entonces debes preguntarte: ¿tengo que hacer esto? Nuevamente, si la respuesta es no, ¡no lo hagas!

Si después de todo esto, todavía cree que necesita y quiere hacerlo de la manera FP, entonces hágalo por todos los medios. Confíe en sus instintos, son más adecuados para sus necesidades que la mayoría de las personas en cualquier foro :)

Arnab Datta
fuente
siéntase libre de explicar la razón del voto negativo
Arnab Datta