L'utilisation de LINQ et des expressions lambda entraîne-t-elle un code moins lisible? [fermé]

43

Je suis en train de discuter avec un collègue sur Linq, je vais copier ici:

Co-travailleur: soyons honnêtes ici. La syntaxe Linq est nulle. C'est déroutant et non intuitif.

Moi: oh allez, plus déroutant que T-SQL?

Co-travailleur: euh, oui.

Moi: il a les mêmes parties de base, sélectionnez, où et à partir de

Co-travailleur: Linq, pour moi, est une bâtarde de relationnel + OO. Co-Worker: Ne vous méprenez pas, c'est incroyablement puissant, mais ils ont réutilisé le code SQL pour utiliser des collections d'objets identiques.

Je suis d'avis que l'utilisation de Linq + Lamda est très puissante (il est d'accord) et facilite également la lecture du code (il n'est pas d'accord sur ce point):

pickFiles = from f in pickFolder.GetFiles("*.txt")
where ValidAuditFileName.IsMatch(f.Name)
select f;

ou

var existing = from s in ActiveRecordLinq.AsQueryable<ScannedEntity>()
where s.FileName == f.FullName && s.DocumentType != "Unknown"
select s;

ou (code VB ici)

   Dim notVerified = From image In images.AsParallel
     Group Join verifyFile In verifyFolder.GetFiles("*.vfy").AsParallel.Where(
      Function(v) v.Length > 0
      ).AsParallel
   On image.Name.Replace(image.Extension, ".vfy") Equals verifyFile.Name
     Into verifyList = Group
    From verify In verifyList.DefaultIfEmpty
    Where verify Is Nothing
    Select verify

Pour moi, cela est clair et facile (au moins plus facile que les alternatives) à lire, quelles sont vos opinions à ce sujet?

Glace noir
la source
11
Les humains, en général, détestent le changement. Un pourcentage élevé le déteste tellement qu'ils en ont vraiment peur.
Tony
9
Regardons les choses en face ... linq est juste une syntaxe de programmation fonctionnelle insensée ajoutée à C # et VB.Net. Bashing linq et les expressions lambda signifie en gros "FP sucks". C'est ce que dit votre collègue. Je pense que ce débat a été divisé ailleurs.
Scott Whitlock
48
Est-ce que ça dérange personne que les gens aient tendance à utiliser le mot "intuitif" quand ils veulent vraiment dire "familier"?
Larry Coleman
7
Quoi qu'il en soit, l'équipe C # (y compris Eric Lippert) a tout fait pour expliquer que Linq n'était pas un portage de code SQL, il a été conçu à la base, comme la plupart des autres fonctionnalités. Je dois dire que votre collègue est un Luddite.
Aaronaught
16
Pour ce que ça vaut: je viens juste de demander à ma femme (administratrice de bureau - pratiquement aucune expérience en programmation) de regarder les 3 exemples d’aronaught et de déchiffrer l’intention des exemples linq et lambda bien plus facilement que l’exemple traditionnel impératif.
Steven Evers

Réponses:

73

Je ne trouve plus le bon post, mais Eric Lippert (et peut-être plusieurs autres logiciels) a déjà exprimé son opinion sur le caractère déclaratif de Linq , qui, pour plusieurs classes de problèmes, est bien plus intuitif que la syntaxe impérative .

Linq vous permet d'écrire du code qui exprime l' intention , pas le mécanisme .

Vous me dites ce qui est le plus facile à lire. Cette:

IEnumerable<Customer> GetVipCustomers(IEnumerable<Customer> source)
{
    List<Customer> results = new List<Customer>();
    foreach (Customer c in source)
    {
        if (c.FirstName == "Aaron")
        {
            results.Add(c);
        }
    }
    results.Sort(new LastNameComparer());
    return results;
}

class LastNameComparer : IComparer<Customer>
{
    public int Compare(Customer a, Customer b)
    {
        return x.LastName.CompareTo(b.LastName);
    }
}

Ou ca?

IEnumerable<Customer> GetVipCustomers(IEnumerable<Customer> source)
{
    return from c in source
           where c.FirstName == "Aaron"
           orderby c.LastName
           select c;
}

Ou même ça?

IEnumerable<Customer> GetVipCustomers(IEnumerable<Customer> source)
{
    return source.Where(c => c.FirstName == "Aaron").OrderBy(c => c.LastName);
}

Le premier exemple est juste un tas de passe-partout inutiles pour obtenir les résultats les plus simples. Quiconque pense qu'il est plus lisible que les versions de Linq doit faire examiner sa tête. Non seulement cela, mais le premier gaspille de la mémoire. Vous ne pouvez même pas l'écrire en utilisant à yield returncause du tri.

Votre collègue peut dire ce qu'il veut. Personnellement, je pense que Linq a considérablement amélioré la lisibilité de mon code.

Il n'y a rien de "relationnel" à propos de Linq non plus. Il peut avoir des similitudes superficielles avec SQL mais il ne tente en aucun cas de mettre en œuvre un calcul relationnel. C'est juste un tas d'extensions qui facilitent les requêtes et les séquences de projet. "Requête" ne signifie pas "relationnel", et plusieurs bases de données non relationnelles utilisent une syntaxe semblable à celle de SQL. Linq est purement orienté objet, il arrive juste de travailler avec des bases de données relationnelles à travers des frameworks tels que Linq to SQL en raison de l'arborescence des expressions voodoo et de la conception intelligente de l'équipe C #, rendant les fonctions anonymes convertibles implicitement en arborescences d'expression.

Aaronaught
la source
6
+1 Si vous n'aimez pas LINQ, c'est probablement parce que "vous ne le faites pas correctement" :)
Darknight
1
Linq is purely object-oriented+1 pour cela. C'est également la raison pour laquelle notre guide de style d'équipe impose l'utilisation de la syntaxe fluide par rapport à la syntaxe de requête. Je trouve que cela rend la nature OO du lien plus évidente pour quelqu'un habitué à objecter la notation dans les langages C-like.
SBI
1
Je sais que le poste a 7 ans. Mais voici la question: avez-vous utilisé les langages fonctionnels comme outils principaux avant de rencontrer Linq?
Sergey.quixoticaxis.Ivanov 12/12/2016
4
Franchement, je préfère la boucle foor, car je vois immédiatement où nous pouvons et aurons des exceptions de référence NULL, comment null est gérée, et ce n'est pas une exécution différée qui rend le débogage difficile, et cela ne crée pas n listes ainsi, la boucle for est également plus efficace.
Quandary
1
@Aaronaught, si vous supprimez le tri et reformatez le code de procédure dans le style Horstmann , je dirai qu'il est plus lisible que la version LINQ . Et selon le principe de la responsabilité unique, le tri n’appartient pas à la réalité GetVipCustomers(), ce qui, comme son nom l'indique, ne doit renvoyer qu'une collection de clients VIP, dans un ordre arbitraire. Dans les rares cas où l'ordre est important, tel que l'affichage à l'écran, laissez l'appelant trier la collection.
Ant_222
69

Co-travailleur: soyons honnêtes ici. La syntaxe Linq est nulle. C'est déroutant et non intuitif.

Vous ne pouvez pas discuter avec cette critique. Pour votre collègue, ça craint . Nous n'avons pas réussi à concevoir une syntaxe qui, pour eux, était claire et intuitive. C'est notre échec et vous pouvez transmettre mes excuses à votre collègue. Je suis heureux de prendre des suggestions sur la façon de l'améliorer. qu'est-ce que votre collègue trouve spécifiquement déroutant ou peu intuitif?

Cependant, vous ne pouvez pas plaire à tout le monde. Mon opinion personnelle, et l'opinion de la plupart des personnes à qui j'ai parlé sur le sujet, est que la syntaxe de compréhension de requête est beaucoup plus claire que la syntaxe impérative équivalente. Il est clair que tout le monde n’est pas d’accord, mais heureusement, nous n’exigeons pas l’unanimité des millions de nos clients lorsque nous concevons le langage.

Au sujet de ce qui est "intuitif" cependant, je me souviens de l'histoire du linguiste anglais qui a étudié de nombreuses langues et qui a finalement conclu que l'anglais était la meilleure de toutes les langues, car en anglais, les mots viennent dans le même ordre pense les . Contrairement au français, où ils disent constamment des choses comme "le chien blanc mange la viande rouge". Comme il doit être difficile pour les Français de penser les mots dans le bon ordre et de les dire ensuite dans l’ ordre français ! Le français est tellement peu intuitif! C'est incroyable que les Français réussissent à le parler. Et allemand? où ils pensent "le chien mange de la viande" mais il faut ensuite dire "le chien que la viande mange"!?!

Souvent, ce qui est "intuitif" est simplement une question de familiarité. Il m'a fallu des mois de travail avec LINQ avant d'arrêter de commencer mes requêtes avec la clause "select". Maintenant, c'est une seconde nature et l'ordre SQL semble bizarre.

Ce que c'est! Les règles de portée sont toutes confondues dans SQL. Vous voudrez peut-être signaler à votre collègue que LINQ a été soigneusement conçu pour que (1) l'introduction des variables et des étendues se déroule de gauche à droite (*) et (2) l'ordre d'affichage de la requête sur la page. l'ordre dans lequel il est exécuté. C'est-à-dire quand vous dites

from c in customers where c.City == "London" select c.Name

le c apparaît dans la portée à gauche et reste dans la droite. Et l'ordre dans lequel les choses se passent est: le premier "client" est évalué. Ensuite, le "où" est évalué pour filtrer la séquence. Ensuite, la séquence filtrée est projetée par le "select".

SQL n'a pas cette propriété. Si tu le dis

SELECT Name FROM Customers WHERE City = 'London'

alors "Nom" est amené dans le champ d'application par quelque chose à sa droite, pas à sa gauche, et la requête est exécutée dans un ordre complètement foiré; la clause du milieu est évaluée en premier, puis la dernière, puis la première. Cela me semble fou et peu intuitif, ayant travaillé uniquement avec LINQ depuis si longtemps.


(*) Les règles de cadrage sont un peu bizarres dans LINQ avec des clauses de jointure. Mais à part cela, les scopes nichent bien.

Eric Lippert
la source
5
Nitpick: En allemand, "Der Hund frisst das Fleisch" correspond en réalité au même ordre qu'en anglais, "Le chien mange la viande" :) En plus de cela, j'ai trouvé la syntaxe de LINQ Query très lisible une fois que j'ai compris qu'il ne s'agissait pas de SQL .
Michael Stum
18
@gbjbaanb: Je ne justifie pas la syntaxe LINQ sur la base entièrement subjective de sa plus grande compréhension . Je justifie plutôt sur une base tout à fait objective qu'il est beaucoup plus facile de concevoir des outils qui aident l'utilisateur à construire des requêtes LINQ, car la syntaxe LINQ a été conçue avec des outils en tête, et qu'il est plus facile de comprendre mentalement les ordre dans lequel les événements se produisent dans une requête parce qu'ils viennent dans le même ordre lexicalement.
Eric Lippert
2
Eric, Du point de vue de la programmation du style déclaratif, je comprends que Linq est plus approprié que le SQL (je dis bien lisible). Mais est-il plus lisible que SQL? En aucune façon. Si «le chien mange de la viande en rouge» est difficile, à quel point est-il plus facile «de la cuisine où la couleur est rouge, obtenez un couteau»? En fait, nous parlons tous et pensons comme "récupère ce couteau qui est sur la table de la cuisine". Et c’est là que SQL est plus proche de la vie réelle que LINQ.
Nawfal
6
Je veux une tasse de café Starbucks où le magasin est ouvert jusqu'à minuit. Cela vous semble familier? C'est dans le même ordre qu'une requête SQL. Oui, LINQ peut être plus lisible que le code inutile que vous devez générer pour exécuter une requête SQL standard, mais LINQ n'est pas plus lisible que la requête SQL elle-même. Donc oui; il peut être avantageux pour les développeurs LINQ d’étendre la portée de gauche à droite, mais en tant qu’utilisateur, pourquoi est-ce que cela m’intéresse? Je me soucie seulement de la facilité d'utilisation.
KyleM
8
@KyleM: bien sûr; la convivialité était un aspect très important de la conception de LINQ. En particulier, être réceptif aux outils de productivité était la clé. Étant donné que la portée va de la gauche à l'écriture dans une requête LINQ, au moment où vous tapez, c.l'EDI connaît déjà le type de cet peut vous donner IntelliSense. Dans LINQ, vous dites "de c dans les clients où c." et boum, IntelliSense vous aide en listant les membres de Customer. En SQL, lorsque vous tapez "nom SELECT parmi les clients", vous ne pouvez obtenir aucune aide de l'EDI pour vous dire que "nom" est un bon choix, car vous avez déjà saisi name auparavant customers .
Eric Lippert
22

Comme n'importe quoi d'autre dans le monde de la programmation, il faut s'habituer à la syntaxe, et ensuite, elle est (potentiellement) plus facile à lire.

Comme n'importe quoi d'autre dans le monde de la programmation, il existe un risque de code spaghetti ou d'autres abus.

Comme n'importe quoi d'autre dans le monde de la programmation, vous pouvez le faire de cette manière ou d'une autre manière.

Comme toute autre chose dans le monde de la programmation, votre kilométrage peut varier.

Wonko le sain d'esprit
la source
6

J'ai vu un commentaire / une remarque où il est dit quelque chose - par rapport à LINQ / lambda - dans le sens de: "Écrivez un code lisible par les humains, plutôt que par votre ordinateur".

Je pense que cette déclaration a beaucoup de mérite, cependant, considérons le développeur (comme moi-même) qui a traversé toute la gamme des langages de développement depuis Assembly, en passant par la procédure, en passant par OO, en passant par la gestion, en exploitant des solutions parallèles de tâches à haut débit. .

Je suis fier de rendre mon code aussi lisible et réutilisable que possible et d'adopter de nombreux principes de modèle de conception GOF afin de fournir des systèmes et des services de qualité de la production dans un grand nombre de secteurs d'activité différents.

La première fois que j'ai rencontré l'expression lambda, j'ai pensé: "Qu'est-ce que c'est que ça!?!" C'était immédiatement contre-intuitif par rapport à ma syntaxe déclarative explicite bien connue (et donc confortable). Les plus jeunes <5 ans dans le boulot, cependant, ont tout gâché comme si c'était de la manne du paradis!

En effet, pendant des années, penser comme un ordinateur (au sens syntaxique) se traduit très facilement en syntaxe de codage direct (quelle que soit la langue). Lorsque vous avez cet état d'esprit informatique depuis environ 20 ans (30 ans et plus dans mon cas), vous devez comprendre que le choc syntaxique initial de l'expression lambda peut facilement se traduire par la peur et la méfiance.

Peut-être que le collègue du PO était issu d'un milieu similaire au mien (c.-à-d. Passé autour du bloc à quelques reprises) et que c'était contre-intuitif pour eux à cette époque? Ma question est: qu'avez-vous fait à ce sujet? Avez-vous essayé de rééduquer votre pair pour qu'il comprenne les avantages de la syntaxe en ligne, ou est-ce que vous le pilori / ostracisez-le pour ne pas "être avec le programme"? Les premiers auraient probablement vu votre collègue se rallier à votre pensée, les seconds leur auraient probablement fait se méfier encore plus de la syntaxe LINQ / lambda et exacerber ainsi l'opinion négative.

Pour moi, je devais rééduquer ma propre façon de penser (comme le dit Eric ci-dessus, ce n'est pas un changement de mental insignifiant, et je devais programmer à Miranda dans les années 80, alors j'ai eu mon lot d'expérience en programmation fonctionnelle). mais une fois que j’ai vécu cette douleur, les avantages étaient évidents, mais plus important encore, là où son utilisation était trop utilisée (c’est-à-dire pour l’utiliser), complexe et répétitive (compte tenu du principe DRY dans ce cas).

En tant que personne qui non seulement écrit encore beaucoup de code mais qui doit également réviser techniquement beaucoup de code, il était impératif que je comprenne ces principes afin de pouvoir examiner les éléments de manière impartiale et d'indiquer les endroits où l'utilisation d'une expression lambda peut être plus efficace / lisible, et aussi pour amener les développeurs à considérer la lisibilité d'expressions lambda en ligne très complexes (lorsqu'un appel de méthode rendrait, dans ces cas, le code plus lisible, maintenable et extensible).

Donc, quand quelqu'un dit qu'il "ne reçoit pas de lambda?" ou la syntaxe LINQ, plutôt que de les qualifier de luddites, essayez de les aider à comprendre les principes sous-jacents. Après tout, ils peuvent avoir un passé "old school" comme moi.

CWC
la source
Commentaire intéressant - J'avais ceci lorsque je suis passé de langages statiques fortement typés à des langages dynamiques. Il m'a fallu un certain temps pour m'ajuster (peur et méfiance) et maintenant, j'ai du mal à revenir en arrière, honnêtement.
lunchmeat317
4

Les expressions lambda conduisent à un code moins lisible si les requêtes sont trop longues. Cependant, c'est beaucoup mieux que trop de boucles imbriquées .

C'est mieux avec un mélange des deux .

Écrivez-le en lambda s'il est plus rapide (vous avez besoin d'être rapide) ou plus facile à lire.

Amir Rezaei
la source
Oui, mais qu'est-ce qui le rendrait linq / lamda pas plus facile à lire?
BlackICE
désolé, signifiait pas plus facile à lire que l'alternative.
BlackICE
1

Je pense que cela dépend dans la plupart des cas (sauf lorsque vous faites quelque chose de très bizarre) si vous voulez dire "lisible" en tant que personne ayant l’idée de ce qui se passe ou s’ils peuvent facilement trouver tous les petits détails.

Je pense que le lien aide avec le premier, mais souvent (surtout lors du débogage) fait du mal au second.

IMHO quand je regarde le code, je ne suis pas familier avec le premier est énormément plus important que le dernier, donc je le trouve beaucoup plus lisible.

Facture
la source
Bon point. Ce dernier est généralement couvert par un bon test unitaire.
Scott Whitlock
Vous pensez donc que les éléments internes de linq evaluation rendent difficile la recherche de tous les détails? Pouvez-vous donner un exemple de quand cela peut être?
BlackICE
3
@ David: Les expressions Linq ne sont pas simplement évaluées paresseuses, elles sont aussi des expressions . Le code de réception d'une expression linq peut en réalité modifier l'expression, de sorte qu'il effectue quelque chose de complètement différent, comme une macro lisp travaillant sur des expressions s. Par exemple, lorsque vous travaillez avec linq en SQL, il prend l’expression where e.FirstName == "John"et la traduit en requête SQL! Pour ce faire, il examine l'expression non compilée (mais analysée), car il s'agit d'une comparaison d'une propriété appelée FirstNamesur une entité persistante, d'une comparaison avec une chaîne, etc. Il se passe beaucoup de choses.
Scott Whitlock
@ David généralement je ne trouve pas les détails difficiles à trouver jusqu'à ce que vous commencez à utiliser linq contre lui-même. Un exemple « simple » de ce qui m'a pris beaucoup de temps pour envelopper ma tête est autour de ceci: bugsquash.blogspot.com/2008/07/y-combinator-and-linq.html mais il y a beaucoup plus nécessairement des exemples dans certains des choses que les gens font avec des expressions dans les zones dynamiques, DynamicObject et IDynamicMetaObjectProvider. Où vous tracez une ligne sur ce qui est linq est discutable, mais comme le souligne scott, tout ce matériel est disponible pour / dans linq car linq utilise les mêmes arbres d’expression.
Bill
@ Bill, d'accord, je vais vous dire que c'est difficile, mais le combinateur y et les fonctions d'ordre élevé vont faire mal à la tête, c'est comme une récursion, que vous obteniez ou non. L’application récursive de cela est-elle plus facile à lire (si vous ne connaissiez l’un ou l’autre)?
BlackICE
1

Je trouve la syntaxe LINQ intuitive et facile à lire, d’autant plus qu’elles placent le FROM au début, comme il se doit, plutôt qu’au milieu, comme en SQL. Mais les lambdas de l’OMI sont source de confusion et rendent le code plus difficile à lire.

Maçon Wheeler
la source
Pourquoi pensez-vous que les lambdas sont déroutants? Est-ce l'inférence de type?
ChaosPandion
1
@ChaosPandion: D'abord l'inférence de type, et ensuite, le symbole. Assembler un = et un> ressemble à un> = que quelqu'un a foiré et écrit à l'envers, et cela a tendance à me faire perdre la tête.
Mason Wheeler
@Mason - Aimez-vous la syntaxe de Haskell ( \x -> x * x) ou F # ( fun x -> x * x)?
ChaosPandion
@ChaosPandion: Non, pas particulièrement. Lambda peut être prompte à écrire, mais je trouve qu'il est difficile de les analyser, surtout quand leur complexité devient non triviale. Vos exemples ne sont pas si mauvais, mais ils ont tendance à s’aggraver.
Mason Wheeler
6
@Mason: On dirait que vous vous opposez à l'abus de lamdas, plutôt qu'à l'idée de lamdas.
Anon.
1

Je conviens avec vous que la syntaxe Linq n'est pas très différente de celle de T-SQL. Je pense que votre collègue pourrait vraiment s'opposer à ce que des éléments relationnels soient mélangés, dans lesquels son code OO agréable et brillant. Par ailleurs, la programmation fonctionnelle demande un peu d’habitude et une volonté de s’y habituer.

Larry Coleman
la source
0

Ça dépend. De toute évidence, T-SQL fournit uniquement des solutions relationnelles à la base de données. De toute évidence, LINQ fournit uniquement des solutions d’OO.

Toutefois; "Plus déroutant que T-SQL?" - est discuté / demandé dans la question initiale. Cela implique clairement certaines caractéristiques qu’aucune des réponses existantes n’a abordées, accusant au contraire le critique (visiblement familier avec SQL) d’être bloqué dans le passé.

Donc, bien que j'apprécie LINQ pour certaines qualités et que je ne désapprouve pas beaucoup les réponses existantes, je pense que le contrepoint mérite d'être représenté:

Des années après la familiarisation avec LINQ, l'exécution de certains types d'opérations de groupe, les jointures externes et les non-équijointures, l'utilisation de clés composites et d'autres opérations dans LINQ me font encore grincer des dents. (Surtout lorsque vous ciblez un back-end relationnel avec des questions sensibles à la performance.)

from c in categories
join p in products on c equals p.Category into ps
from p in ps.DefaultIfEmpty()

Si vous pensez que c'est intuitif, vous aurez plus de pouvoir. ;)

Shannon
la source
-4

Il y a probablement une raison pour laquelle Linq craint, essayez juste de faire des exemples du monde réel, au lieu de ces exemples classiques.

essayez de montrer cette fonction dans linqlamdathings et vous verrez que toute la beauté a disparu, alors que la manière classique reste lisible. Sans parler des problèmes d'exécution différée et de la définition de points d'arrêt.

La vérité est que LinqLambdaThings est très utile dans peu de cas et on ne pense pas qu'il remplace toute l'orientation astucieuse des objets que vous n'avez jamais comprise

    public IList<Customer> GetVipCustomers(IList<Customer> source, int maxCount)
    {
        var results = new SortedList<string,Customer>();

        foreach (Customer c in source)
        {
            if (maxCount == results.Count)
                break;

            if (c.IsVip && c.FirstName== "Aaron" || c.SecondName== "Aaron")
                results.Add(c.LastName, c);
        }

        return results.Values;
    }
Marc
la source
6
Je pense que source.Where(c => c.IsVip && c.FirstName == "Aaron" || c.SecondName == "Aaron").Take(maxCount).OrderBy(d => d.LastName);c’est difficile à lire mais j’ai peut-être commis une erreur;)
jk.
1
De quelle manière avez-vous considéré ces exemples comme des exemples? C'est en fait un code d'utilisation de production. Cela aiderait si vous fournissiez à la fois votre code "lisible" classique et la version de linq / lamda à des fins de comparaison au lieu d’attendre que tout le monde le convertisse.
BlackICE
3
Vous êtes-vous inscrit spécifiquement pour vous plaindre de LINQ?
KChaloux
1
@KChaloux Je pense qu'ils étaient juste en train de troll pour être honnête
jk.