Est-ce que de petites quantités de programmation fonctionnelle sont compréhensibles par les non-PF? [fermé]

43

Cas : je travaille dans une entreprise, écrivant une application en Python qui traite beaucoup de données dans des tableaux. Je suis le seul développeur de ce programme pour le moment, mais il sera probablement utilisé / modifié / étendu dans le futur (1-3 ans) par un autre programmeur, pour le moment inconnu de moi. Je ne serai probablement pas là directement pour aider alors, mais peut-être apporterais-je de l'aide par courrier électronique si j'en ai le temps.

Donc, en tant que développeur qui a appris la programmation fonctionnelle (Haskell), j’ai tendance à résoudre, par exemple, le filtrage de la manière suivante:

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

Le reste du code est OO, ce ne sont que quelques cas où je veux le résoudre comme ceci, car il est beaucoup plus simple et plus beau selon moi.

Question : Est-ce que ça va aujourd'hui d'écrire du code comme ça?

  • Comment un développeur qui n'a pas écrit / appris la PF réagit-il à un code comme celui-ci?
  • Est-ce lisible?
  • Modifiable?
  • Devrais-je écrire une documentation comme expliquer à un enfant ce que la ligne fait?

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

J'ai demandé à mon patron, et il a juste dit "La PF est une magie noire, mais si cela fonctionne et est la solution la plus efficace, alors c'est OK de l'utiliser."

Quelle est votre opinion à ce sujet? En tant que programmeur non-PF, comment réagissez-vous au code? Le code est-il "googable" afin que vous puissiez comprendre ce qu'il fait? J'aimerais des commentaires à ce sujet.

kd35a
la source
14
À mon avis, de nombreux langages modernes permettent une certaine programmation fonctionnelle et son utilisation devient de plus en plus courante. Personnellement, je dirais que ça va (au moins pour les tâches de filtrage / mappage relativement simples).
Joachim Sauer
Je ne peux pas vous donner une réponse non affectée, parce que j'aime la PF. Ce serait une bonne idée d'ajouter un commentaire, si c'est au-delà de la simple.
Bruno Schäpper
3
Votre commentaire pourrait être plus clair; Le filtre inclut des éléments pour lesquels le test est True, votre commentaire pourrait donc lire # Select the item's from measures for which included(item.time, dur) == True:, éviter un double négatif améliore toujours la compréhension.
Martijn Pieters
6
Avez-vous envisagé d'utiliser des listes de compréhension? Ils sont souvent considérés comme plus "pythoniques" que map / filter.
phant0m
2
@MatjazMuhic Eh bien, c'est magnifique ...;)
kd35a

Réponses:

50

Est-ce lisible?

Pour moi: Oui, mais j’en suis venu à comprendre que la communauté Python semble souvent considérer que la compréhension de liste est une solution plus propre que d’utiliser map()/ filter().

En fait, GvR a même envisagé de supprimer complètement ces fonctions.

Considère ceci:

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

De plus, ceci a l'avantage qu'une compréhension de liste retournera toujours une liste. map()et filter()d'autre part retournera un itérateur dans Python 3.

Remarque: si vous souhaitez plutôt un itérateur, il vous suffit de le remplacer []par ():

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

Pour être honnête, je ne vois pratiquement aucune raison d'utiliser map()ou filter()en Python.

Est-ce modifiable?

Oui, certes, cependant, il y a une chose qui facilite cela: en faire une fonction, pas un 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 votre état devient plus complexe, l’échelle sera beaucoup plus simple et vous permettra également de réutiliser votre chèque. (Notez que vous pouvez créer des fonctions dans d'autres fonctions, cela peut le garder plus près de l'endroit où il est utilisé.)

Comment un développeur qui n'a pas écrit / appris la PF réagit-il sur un code comme celui-ci?

Il recherche la documentation Python dans Google et sait comment cela fonctionne cinq minutes plus tard. Sinon, il ne devrait pas programmer en Python.

map()et filter()sont extrêmement simples. Ce n'est pas comme si vous leur demandiez de comprendre les monades. C'est pourquoi je ne pense pas que vous ayez besoin d'écrire de tels commentaires. Utilisez de bons noms de variables et de fonctions, le code est alors presque explicite. Vous ne pouvez pas anticiper sur les fonctionnalités qu'un développeur ne connaît pas. Pour autant que vous sachiez, le prochain développeur pourrait ne pas savoir ce qu'est un dictionnaire.

Ce que nous ne comprenons pas est généralement illisible pour nous. Ainsi, vous pourriez affirmer que ce n'est pas moins lisible qu'une compréhension de liste si vous ne les avez jamais vues auparavant. Mais comme Joshua l'a mentionné dans son commentaire, je pense moi aussi qu'il est important d'être cohérent avec ce que les autres développeurs utilisent - du moins si cette alternative ne fournit aucun avantage substantiel.

phantom
la source
5
Il peut être utile de mentionner aussi les expressions du générateur , qui fonctionnent de la même liste que compréhensions mais générateurs de retour au lieu de listes, comme mapet filterfaire en python 3.
James
@ James: Excellente idée, je les ai ajoutés à mon post. Merci!
Phant0m
2
En général, je donne reducecomme contre-exemple que vous pouvez toujours utiliser un argument de compréhension de liste , car il est relativement difficile de le remplacer reducepar un générateur en ligne ou une compréhension de liste.
Kojiro
4
+1 pour noter l'idiome préféré de la langue en question. La cohérence de l'IMHO a une valeur considérable et utiliser un langage de la manière utilisée par une partie importante des "locuteurs" peut offrir une facilité de maintenance supérieure à celle de commentaires.
Joshua Drake
4
+1 pour "Il recherche la documentation Python dans Google et sait comment cela fonctionne cinq minutes plus tard. Sinon, il ne devrait pas programmer en Python."
Bjarke Freund-Hansen
25

Étant donné que la communauté des développeurs regagne de l’intérêt pour la programmation fonctionnelle, il n’est pas rare de voir une programmation fonctionnelle dans des langages qui étaient à l’origine entièrement orientés objet. Un bon exemple est le C #, où les types anonymes et les expressions lambda permettent d’être beaucoup plus courts et expressifs grâce à la programmation fonctionnelle.

Cela étant dit, la programmation fonctionnelle est étrange pour les débutants. Par exemple, lors d’une formation, j’ai expliqué aux débutants comment améliorer leur code grâce à la programmation fonctionnelle en C #, certains d’entre eux n’étaient pas convaincus et d’autres ont dit que le code original leur était plus facile à comprendre. L'exemple que je leur ai donné était le suivant:

Code avant refactoring:

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);
}

Même code après refactoring en utilisant 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))
    );

Cela me fait penser que:

  • ne vous inquiétez pas si vous savez que le prochain développeur qui gérera votre code aura une expérience globale suffisante et une certaine connaissance de la programmation fonctionnelle, mais:

  • sinon, évitez la programmation fonctionnelle ou mettez un commentaire explicite expliquant en même temps la syntaxe, les avantages et les inconvénients de votre approche par rapport à une programmation non fonctionnelle.

Arseni Mourzenko
la source
8
+1 si FP ne rend votre code plus lisible par personne , vous le faites mal.
György Andrasek
7
Je peux voir comment, pris isolément, quelqu'un qui n'a jamais vu FP pourrait considérer le premier extrait plus lisible que le dernier. Mais si on multiplie cela par 100? La lecture de la courte 100 succinctes presque anglais comme des phrases décrivant le ce contre des milliers de lignes de plomberie comment le code. Le volume de code, si familier soit-il, devrait être tellement écrasant que la familiarité n’est plus une victoire énorme. À un moment donné, il doit être plus facile pour quiconque de se familiariser avec les éléments de la PF, puis de lire quelques lignes plutôt que de lire des milliers de lignes de code familier.
Esailija
7
Ce qui me dérange le plus, c'est que nous sommes contents de croire qu'il est acceptable pour les développeurs de ne pas apprendre le style fonctionnel. Les avantages ont été maintes fois prouvés, le paradigme est soutenu par les langages traditionnels. Si les gens n'ont pas la capacité de comprendre les techniques fonctionnelles, ils devraient apprendre ou apprendre eux-mêmes, même s'ils n'ont pas l'intention de les utiliser. Je déteste XSLT avec une passion brûlante, mais cela ne m'a pas empêché de l'apprendre, en tant que professionnel.
Sprague
2
@MainMa Votre point est tout à fait valide, j'aurais dû être plus spécifique. Ce que je veux dire, c'est qu'un développeur dans une langue donnée doit pouvoir utiliser efficacement les fonctionnalités de ces langues. Par exemple, utiliser 'style fonctionnel' en C # (utiliser la programmation par style de filtre / carte / réduction ou les fonctions de passage / retour) n’a rien de nouveau, et donc je pense que tout développeur C # qui n’est pas capable de lire de telles déclarations devrait mettre à jour ses fonctions. compétences ... probablement très rapidement. Je pense certainement que chaque développeur devrait apprendre un peu Haskell, mais uniquement pour des raisons académiques. Ceci est une chose différente.
Sprague
2
@Sprague: Je comprends votre point de vue et suis d'accord avec cela. C'est juste que nous pouvons difficilement attendre, par exemple, que les programmeurs C # commencent à utiliser des paradigmes de FP, alors qu'un trop grand nombre de ces programmeurs n'utilise même pas de génériques. Jusqu'à ce qu'il y ait autant de programmeurs non qualifiés, le niveau de notre profession sera très réduit, d'autant plus que la plupart des personnes sans formation technique ne comprennent pas du tout ce qu'est le développement.
Arseni Mourzenko
20

Je suis un programmeur non-PF et récemment, je devais modifier le code de mon collègue en JavaScript. Il y avait une demande Http avec un rappel qui ressemblait beaucoup à la déclaration que vous avez incluse. Je dois dire que cela m'a pris un certain temps (environ une demi-heure) pour tout comprendre (le code dans l'ensemble n'était pas très volumineux).

Il n'y avait pas de commentaires, et je pense qu'il n'y en avait aucune nécessité. Je n'ai même pas demandé à mon collègue de m'aider à comprendre son code.

Étant donné que je travaille depuis environ un an et demi, je pense que la plupart des programmeurs seront en mesure de comprendre et de modifier ce code, comme je l’ai fait.

En outre, comme Joachim Sauer l'a dit dans son commentaire, il existe souvent des morceaux de PF dans de nombreuses langues, comme C # (indexOf, par exemple). Tant de programmeurs non-PF traitent cela assez souvent, et l'extrait de code que vous avez inclus n'est pas quelque chose de terrible ou d'incompréhensible.

SuperM
la source
1
Merci pour votre commentaire! Cela m'a donné plus d'informations sur l'autre perspective. Il est facile de devenir aveugle à la maison et vous ne savez pas à quoi ça ressemble quand vous ne connaissez pas FP :)
kd35a
1
@ kd35a, de rien. Heureusement, je peux être objectif ici))
superM
1
+1 pour signaler que de nombreuses langues contiennent maintenant des éléments de PF.
Joshua Drake
LINQ est le principal exemple de FP en C #
Konrad Morawski
3

Je dirais certainement oui!

Il existe de nombreux aspects de la programmation fonctionnelle et l'utilisation de fonctions d'ordre supérieur, comme dans votre exemple, n'en est qu'un.

Par exemple, j’estime que l’écriture de fonctions pures est extrêmement importante pour tout logiciel écrit dans n’importe quelle langue (par «pur», j’entends pas d'effets secondaires), car:

  • ils sont plus faciles à tester
  • ils sont beaucoup plus composables que les fonctions secondaires
  • ils sont plus faciles à déboguer

J'évite aussi souvent de muter les valeurs et les variables - un autre concept emprunté à la PF.

Ces deux techniques fonctionnent bien en Python et dans d'autres langages qui ne sont généralement pas classés comme fonctionnels. Ils sont souvent même pris en charge par le langage lui-même (c'est- finalà- dire les variables en Java). Ainsi, les futurs programmeurs de maintenance ne seront pas confrontés à une énorme barrière pour comprendre le code.


la source
2

Nous avons eu la même discussion sur une entreprise pour laquelle j'ai travaillé l'année dernière.

La discussion a porté sur le "code magique" et si cela devait être encouragé ou non. En y regardant un peu plus, il semblait que les gens avaient des points de vue très différents sur ce qui était en réalité un "code magique". Ceux qui ont abordé la discussion ont semblé vouloir dire principalement que les expressions (en PHP) qui utilisaient le style fonctionnel étaient du "code magique", tandis que les développeurs originaires d'autres langues utilisant davantage le style de la PF dans leur code semblent penser que le code magique est plutôt lorsque vous faites l'inclusion dynamique de fichiers à travers les noms de fichiers et ainsi de suite.

Nous ne sommes jamais parvenus à une bonne conclusion à ce sujet. Mieux encore, les gens pensent que le code qui semble inconnu est "magique" ou difficile à lire. Alors, est-ce une bonne idée d'éviter un code qui semble inconnu des autres utilisateurs? Je pense que cela dépend de l'endroit où il est utilisé. Je m'abstiendrais d'utiliser des expressions de style fp (inclusion de fichier dynamique, etc.) dans une méthode principale (ou dans des parties centrales importantes d'applications) dans laquelle les données devraient être tunnelisées de manière claire, intuitive et facile à lire. D'autre part, je ne pense pas que l'on devrait avoir peur de repousser les limites, les autres développeurs apprendront probablement la PF rapidement s'ils sont confrontés au code de la PF et auront peut-être une bonne ressource interne à consulter sur ces questions.

TL; DR: évitez en haut la partie centrale des applications (qui doivent être lues pour avoir une vue d'ensemble des fonctionnalités de l'application). Sinon utilisez-le.

Christopher Käck
la source
Bon point à éviter de l'utiliser dans un code de niveau supérieur!
kd35a
J'ai toujours pensé qu'il y avait un manque de formalisme, et beaucoup d'opportunités, dans la définition de techniques de programmation dans différents types de blocs (tels que les méthodes statiques, les méthodes publiques, les constructeurs, etc.) Bonne réponse ... ça me fait revisiter certains de ces pensées.
Sprague
2

La communauté C ++ a récemment eu le lambda aussi, et je crois qu’elle a à peu près la même question. La réponse peut ne pas être la même, cependant. L'équivalent C ++ serait:

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

Ce std::copyn’est pas nouveau, et les _ifvariantes ne le sont pas non plus, mais le lambda l’est. Pourtant, il est défini assez clairement dans son contexte: il durest capturé et donc constant, Item ivarie dans la boucle et la returndéclaration unique effectue tout le travail.

Cela semble acceptable pour de nombreux développeurs C ++. Je n'ai cependant pas échantillonné d'opinions sur les lambda d'ordre supérieur et je m'attendrais à beaucoup moins d'acceptation.

MSalters
la source
Point intéressant, il peut y avoir différentes réponses en fonction de la langue dans laquelle il se trouve. Probablement lié au post de @Christopher Käck sur la façon dont les codeurs PHP ont eu plus de problèmes avec ce genre de choses que les codeurs Python.
kd35a
0

Envoyez un extrait de code à un autre développeur qui ne parle pas aussi bien le python, et demandez-lui s’il peut passer 5 minutes à vérifier le code pour voir s’il le comprend.

Si oui, vous êtes probablement prêt à partir. Sinon, vous devriez essayer de le clarifier.

Votre collègue pourrait-il être stupide et ne pas comprendre quelque chose qui devrait être évident? Oui, mais vous devriez toujours programmer conformément à KISS.

Peut-être que votre code est plus efficace / beau / élégant qu'une approche plus simple et plus sûre des imbéciles? Ensuite, vous devez vous demander: dois-je faire cela? Encore une fois, si la réponse est non, alors ne le faites pas!

Si, après tout cela, vous pensez toujours que vous avez besoin et que vous voulez le faire de la manière de la PF, alors, certainement, faites-le. Faites confiance à votre instinct, ils sont mieux adaptés à vos besoins que la plupart des gens sur n'importe quel forum :)

Arnab Datta
la source
ne hésitez pas à expliquer la raison de ce vote
Arnab Datta