O uso das expressões LINQ e Lambda leva a um código menos legível? [fechadas]

43

Estou tendo uma discussão com um colega de trabalho no Linq, vou copiar aqui:

Colega de trabalho: Vamos ser honestos aqui. A sintaxe do Linq é uma porcaria. É confuso e não intuitivo.

Eu: ah, vamos lá, mais confuso que o T-SQL?

Colega de trabalho: sim.

Eu: possui as mesmas partes básicas, selecione, onde e de

Colega de trabalho: Linq, para mim, é uma bastardização do relacional + OO. Colega de trabalho: Não me interpretem mal - é incrivelmente poderoso, mas eles redirecionaram o SQL para usar novamente coleções de objetos.

Sou da opinião de que o uso do Linq + Lamda é muito poderoso (ele concorda) e também facilita a leitura do código (ele discorda nesse ponto):

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 (código VB aqui)

   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

Para mim, isso é limpo e fácil (pelo menos mais fácil do que as alternativas) de ler, quais são suas opiniões sobre isso?

Gelo preto
fonte
11
Os seres humanos, em geral, odeiam mudanças. Uma grande porcentagem odeia tanto que eles realmente temem.
Tony
9
Vamos ser sinceros ... linq é apenas uma sintaxe de programação funcional simplificada, adicionada ao C # e ao VB.Net. Bater expressões linq e lambda está basicamente dizendo "FP sucks". É isso que seu colega de trabalho está dizendo. Eu acho que esse debate foi resolvido em outro lugar.
Scott Whitlock
48
Incomoda alguém que as pessoas tendem a usar a palavra "intuitivo" quando realmente querem dizer "familiar"?
Larry Coleman
7
De qualquer forma, a equipe de C # (incluindo Eric Lippert) se esforçou para explicar que o Linq não era uma portabilidade do SQL, ele foi projetado desde o início como a maioria dos outros recursos. Eu teria que dizer que seu colega de trabalho é um ludita.
Aaronaught
16
Pelo que vale a pena: Acabei de pedir à minha esposa (administrador do escritório - quase zero de experiência prática em programação) que olhasse para os 3 exemplos de aaronaught e consegui decifrar a intenção dos exemplos linq e lambda com muito mais facilidade do que o exemplo imperativo tradicional.
Steven Evers

Respostas:

73

Não consigo mais encontrar o post certo, mas Eric Lippert (e possivelmente vários outros softies) opinaram em várias ocasiões sobre como o Linq é declarativo , o que, para várias classes de problemas, é muito mais intuitivo do que a sintaxe imperativa .

O Linq permite escrever código que expressa a intenção , não o mecanismo .

Você me diz o que é mais fácil de ler. Este:

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 isto?

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

Ou até isso?

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

O primeiro exemplo é apenas um monte de clichê inútil para obter os resultados mais simples. Qualquer um que pense que é mais legível do que as versões do Linq precisa ser examinado. Não apenas isso, mas o primeiro desperdiça memória. Você não pode nem escrever usando yield returnpor causa da classificação.

Seu colega de trabalho pode dizer o que ele quer; pessoalmente, acho que o Linq melhorou imensamente minha legibilidade de código.

Também não há nada "relacional" no Linq. Pode ter algumas semelhanças superficiais com o SQL, mas não tenta, de forma alguma, implementar o cálculo relacional. São apenas várias extensões que facilitam a consulta e o projeto de seqüências. "Consulta" não significa "relacional" e, de fato, existem vários bancos de dados não relacionais que usam sintaxe semelhante ao SQL. O Linq é puramente orientado a objetos, por acaso trabalha com bancos de dados relacionais por meio de estruturas como Linq para SQL por causa de algum vodu da árvore de expressão e design inteligente da equipe C #, tornando funções anônimas implicitamente conversíveis em árvores de expressão.

Aaronaught
fonte
6
+1 Se você não gosta do LINQ, é provavelmente porque "você não está fazendo o que é certo" :)
Darknight
1
Linq is purely object-oriented+1 para isso. É também por isso que nosso guia de estilo de equipe impõe o uso da sintaxe fluente sobre a sintaxe da consulta. Acho que isso torna a natureza OO do link mais óbvia para alguém acostumado a notação de objetos em linguagens do tipo C.
SBI
1
Eu sei que o post tem 7 anos. Mas aqui está a pergunta: você usou as linguagens funcionais como suas principais ferramentas antes de conhecer o Linq?
Sergey.quixoticaxis.Ivanov
4
Francamente, prefiro o loop foor, porque então vejo imediatamente onde podemos e teremos exceções de referência NULL, como o nulo é tratado e a execução adiada que dificulta a depuração e não cria n listas também, portanto, o loop for também é mais eficiente.
Quandary
1
@Aaronaught, se você remover a classificação e reformatar o código processual no estilo Horstmann , direi que é mais legível que a versão LINQ . E de acordo com o princípio da responsabilidade única, a triagem realmente não pertence GetVipCustomers(), a qual, como o próprio nome sugere, deve devolver apenas uma coleção de clientes VIP, em ordem arbitrária. Nos casos raros em que a ordem é importante, como a saída na tela, deixe o chamador classificar a coleção.
Ant_222
69

Colega de trabalho: Vamos ser honestos aqui. A sintaxe do Linq é uma porcaria. É confuso e não intuitivo.

Você não pode argumentar com essa crítica. Para o seu colega de trabalho, é uma merda . Falhamos em projetar uma sintaxe que, para eles, era clara e intuitiva. Essa é a nossa falha, e você pode passar minhas desculpas ao seu colega de trabalho. Fico feliz em receber sugestões sobre como torná-lo melhor; o que especificamente seu colega de trabalho considera confuso ou pouco intuitivo?

No entanto, você não pode agradar a todos. Minha opinião pessoal, e a opinião da maioria das pessoas com quem conversei sobre o assunto, é que a sintaxe de compreensão da consulta é muito mais clara que a sintaxe imperativa equivalente. Claramente, nem todos concordam, mas felizmente não exigimos consenso de todos os milhões de nossos clientes quando fazemos o design de idiomas.

No entanto, sobre o que é "intuitivo", lembro-me da história do lingüista inglês que estudou muitas línguas diferentes e finalmente concluí que o inglês era o melhor de todos os idiomas porque, em inglês, as palavras vêm na mesma ordem em que você pense neles . Ao contrário do francês, onde eles estão constantemente dizendo coisas como "o cachorro branco come a carne vermelha". Quão difícil deve ser para os franceses pensarem as palavras na ordem correta e depois as dizerem na ordem francesa ! Francês é tão pouco intuitivo! É incrível que os franceses consigam falar. E alemão? onde eles pensam "o cachorro come a carne", mas depois tem que dizer "o cachorro a carne come"!?!

Frequentemente, o que é "intuitivo" é apenas uma questão de familiaridade. Levei meses trabalhando com o LINQ antes de eu parar de iniciar minhas consultas com a cláusula "select". Agora é uma segunda natureza, e a ordem SQL parece bizarra.

Qual é! As regras de escopo estão todas bagunçadas no SQL. Algo que você pode apontar para seu colega de trabalho é que o LINQ foi cuidadosamente projetado para que (1) a introdução de variáveis ​​e escopos aconteça da esquerda para a direita (*) e (2) a ordem em que a consulta aparece na página é a ordem em que é executada. Ou seja, quando você diz

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

o c aparece no escopo à esquerda e permanece no escopo à direita. E a ordem na qual as coisas acontecem são: os primeiros "clientes" são avaliados. Em seguida, o "onde" é avaliado para filtrar a sequência. Em seguida, a sequência filtrada é projetada pelo "select".

SQL não tem essa propriedade. Se você diz

SELECT Name FROM Customers WHERE City = 'London'

então "Nome" é trazido ao escopo por algo à sua direita, não à sua esquerda, e a consulta é executada em uma ordem completamente confusa; a cláusula do meio é avaliada primeiro, depois a última e depois a primeira. Isso agora parece louco e pouco intuitivo para mim, tendo trabalhado apenas com o LINQ por tanto tempo.


(*) As regras de escopo são um pouco estranhas no LINQ com cláusulas de junção. Mas, além disso, os escopos se encaixam bem.

Eric Lippert
fonte
5
Nitpick: Em alemão, "Der Hund frisst das Fleisch" é na verdade a mesma ordem que em inglês, "O cachorro come carne" :) Além disso, achei a sintaxe do LINQ Query muito legível quando percebi que não era SQL .
Michael Stum
18
@gbjbaanb: Não estou justificando a sintaxe LINQ com base inteiramente subjetiva, pois é mais compreensível . Em vez disso, justifico com base inteiramente objetiva que é muito mais fácil projetar ferramentas que auxiliam o usuário na construção de consultas LINQ porque a sintaxe do LINQ foi projetada com as ferramentas em mente e que é mais fácil entender mentalmente o ordem na qual os eventos ocorrem em uma consulta porque eles vêm na mesma ordem lexicamente.
Eric Lippert
2
Eric, De uma perspectiva de programação do estilo declarativo, eu entendo que o Linq é apropriado (eu digo legível) do que o SQL. Mas é mais legível por humanos que SQL? De jeito nenhum. Se 'cachorro come carne vermelha' é difícil, quão mais fácil é 'da cozinha, onde a cor é vermelha, fica com faca'? De fato, todos conversamos e pensamos como 'pegue a faca que está em cima da mesa da cozinha'. E é aí que o SQL está mais próximo da vida real do que o LINQ.
Nawfal,
6
Quero uma xícara de café da Starbucks, onde a loja fica aberta até meia-noite. Isso lhe parece familiar? Está exatamente na mesma ordem que uma consulta SQL. Sim, o LINQ pode ser mais legível do que o código desnecessário que você precisa gerar para executar uma consulta SQL padrão, mas o LINQ não é mais legível do que a própria consulta SQL. Então sim; pode ser vantajoso para os desenvolvedores do LINQ o escopo da esquerda para a direita, mas como usuário, por que me importo? Eu só me importo com a usabilidade.
KyleM
8
@KyleM: Claro; a usabilidade era um aspecto muito importante do design do LINQ. Em particular, ser passível de ferramentas de produtividade era fundamental. Como o escopo flui da esquerda para gravação em uma consulta LINQ, no momento em que você digita, c.o IDE já sabe o tipo ce pode fornecer o IntelliSense. No LINQ, você diz "de c em clientes em que c". e boom, você recebe o IntelliSense, listando os membros de Customer. No SQL, quando você digita "SELECT name FROM customers", não pode obter ajuda do IDE para dizer que "name" é uma boa opção porque você digitou name antes customers .
precisa
22

Como qualquer outra coisa no mundo da programação, você precisa se acostumar com a sintaxe e, então, é (potencialmente) mais fácil de ler.

Como qualquer outra coisa no mundo da programação, existe o potencial para código de espaguete ou outros abusos.

Como qualquer outra coisa no mundo da programação, você pode fazer dessa maneira ou de outra maneira.

Como qualquer outra coisa no mundo da programação, sua milhagem pode variar.

Wonko, o são
fonte
6

Vi um comentário / observação em que afirmava algo - em relação ao LINQ / lambda - ao longo das linhas de: "Escreva código legível para humanos, em vez de legível para seu computador".

Eu acho que essa afirmação tem muito mérito, no entanto, considere o desenvolvedor (como eu) que passou por toda a gama de linguagens de desenvolvimento do Assembly, do procedural, do OO, do gerenciado, do aproveitamento de soluções paralelas de tarefas de alto rendimento .

Orgulho-me de tornar meu código o mais legível e reutilizável possível e a adoção de muitos dos princípios de padrão de design do GOF, a fim de fornecer sistemas e serviços de qualidade de produção em um amplo número de setores de negócios diferentes.

A primeira vez que encontrei a expressão lambda, pensei: "Que diabos é isso!?!" Foi imediatamente contra-intuitivo para minha sintaxe declarativa explícita familiar (e, portanto, confortável). Os mais jovens <5anos do trabalho, no entanto, fizeram isso como se fosse um maná do céu!

Isso ocorre porque, durante anos, pensando como um computador (no sentido sintático) traduzido com muita facilidade em sintaxe de codificação direta (independentemente do idioma). Quando você tem essa mentalidade computacional há mais de 20 anos (mais de 30 anos no meu caso), é preciso compreender que o choque sintático inicial da expressão lambda pode se traduzir facilmente em medo e desconfiança.

Talvez o colega de trabalho no OP tivesse um histórico semelhante ao meu (isto é, já andei no quarteirão algumas vezes) e tenha sido contra-intuitivo para eles naquele momento? Minha pergunta é: o que você fez sobre isso? Você tentou reeducar seus colegas para entender os benefícios da sintaxe em linha ou os criticou / ostracizou por não "estar no programa"? O primeiro provavelmente teria visto seu colega de trabalho se aproximar de sua linha de pensamento, o último provavelmente os faria desconfiar ainda mais da sintaxe LINQ / lambda, exacerbando a opinião negativa.

Para mim, eu tive que reeducar minha própria maneira de pensar (como Eric deduz acima, não é uma mudança de mente insignificante, e eu tive que programar em Miranda nos anos 80, para ter minha parcela de experiência em programação funcional) mas depois que passei por essa dor, os benefícios foram óbvios, mas - mais importante -, onde seu uso foi superutilizado (isto é, usado para fins de uso), complexo e repetitivo (considerando o princípio DRY nesse caso).

Como alguém que não apenas ainda escreve muito código, mas também precisa revisar tecnicamente muitos códigos, era essencial que eu entendesse esses princípios para poder revisar itens com imparcialidade, aconselhar onde o uso de uma expressão lambda pode ser mais eficiente / legível e também para que os desenvolvedores considerem a legibilidade de expressões lambda inline altamente complexas (onde uma chamada de método tornaria - nesses casos - tornar o código mais legível, sustentável e extensível).

Então, quando alguém diz que "não recebe lambda?" ou sintaxe LINQ, em vez de dar a eles uma tentativa luddita de ajudá-los a entender os princípios subjacentes. Afinal, eles podem ter um histórico da "velha escola" como eu.

CWC
fonte
Comentário interessante - eu tive isso quando mudei de linguagens estáticas fortemente tipadas para linguagens dinâmicas. Levei um tempo para me ajustar (medo e desconfiança) e agora acho difícil voltar, honestamente.
lunchmeat317
4

As expressões Lambda levam a um código menos legível se as consultas forem muito longas. No entanto, é muito melhor do que muitos loops aninhados .

É melhor com uma mistura dos dois .

Escreva no Lambda se for mais rápido (você precisa ser rápido) ou mais fácil de ler.

Amir Rezaei
fonte
Sim, mas o que tornaria linq / lamda não mais fácil de ler?
BlackICE
desculpe, não significava mais fácil de ler do que a alternativa.
BlackICE
1

Eu acho que depende, na maioria dos casos (exceto quando se faz algo muito bizarro), se você quer dizer "legível" como alguém tendo a idéia do que está acontecendo ou se pode encontrar facilmente todos os pequenos detalhes.

Acho que o link ajuda com o primeiro, mas muitas vezes (principalmente na depuração) prejudica o segundo.

IMHO, quando eu estou olhando para o código, eu não estou familiarizado com o primeiro, é imensamente mais importante que o último, então eu o acho muito mais legível.

Conta
fonte
Bom ponto. Este último é geralmente coberto por um bom teste de unidade.
Scott Whitlock
Então você acha que os aspectos internos da avaliação do linq dificultam a localização de todos os detalhes? Você pode fornecer um exemplo de quando isso pode ser?
BlackICE
3
@ David: expressões Linq não são apenas preguiçosos avaliados, eles são expressões . O código de recebimento de uma expressão linq pode realmente modificá-la, fazendo algo completamente diferente, como uma macro lisp trabalhando em expressões s. Por exemplo, ao trabalhar com linq para SQL, ele realmente pega a expressão where e.FirstName == "John"e a converte em uma consulta SQL! Ele faz isso observando a expressão não compactada (mas analisada), vendo que é uma comparação de uma propriedade chamada FirstNameem uma entidade persistente, e está comparada a uma string, etc. Há muita coisa acontecendo.
Scott Whitlock
@ David geralmente não acho os detalhes difíceis de encontrar até que você comece a usar o linq contra si. Um exemplo "simples" disso que me levou muito tempo para entender é: bugsquash.blogspot.com/2008/07/y-combinator-and-linq.html, mas existem exemplos muito mais necessariamente em alguns das coisas que as pessoas estão fazendo com expressões nas áreas dynamic, DynamicObject e IDynamicMetaObjectProvider. Onde você desenha a linha do que é linq é discutível, mas como scott aponta, todo esse material está disponível para / no linq porque o linq usa as mesmas árvores de expressão.
Bill
@ Bill, tudo bem, eu darei a você que é difícil, mas as funções de combinador y e ordem alta vão doer a maior parte de nossas cabeças, é como recursão, você entende ou não. A implementação recursiva disso é mais fácil de ler (se você não conhecia nenhum deles)?
BlackICE
1

Acho a sintaxe do LINQ intuitiva e fácil de ler, principalmente porque eles colocam o FROM no início, onde ele pertence, em vez de no meio, como no SQL. Mas as lambdas da OMI são confusas e dificultam a leitura do código.

Mason Wheeler
fonte
Por que você acha que as lambdas são confusas? É a inferência de tipo?
ChaosPandion
1
@ChaosPandion: primeiro a inferência de tipo e depois o símbolo. Juntar um = e um> parece um> = que alguém estragou tudo e escreveu de trás para frente, e isso tende a fazer meu cérebro dar uma volta.
Mason Wheeler
@Mason - Você gosta da sintaxe de Haskell ( \x -> x * x) ou F # ( fun x -> x * x)?
precisa saber é o seguinte
@ChaosPandion: Não, não particularmente. A escrita de Lambdas pode ser rápida, mas acho difícil analisá-las, especialmente quando elas se tornam não triviais em complexidade. Seus exemplos não são tão ruins, mas tendem a ficar muito piores.
Mason Wheeler
6
@Mason: Parece que você está fazendo objeções ao abuso de lamdas, em vez de se opor à idéia de lamdas em primeiro lugar.
Anon.
1

Concordo com você que a sintaxe do Linq não é significativamente diferente do T-SQL. Eu acho que seu colega de trabalho pode realmente estar se opondo a coisas relacionais se misturando em seu código OO agradável e brilhante. Por outro lado, a programação funcional leva um pouco de tempo para se acostumar e uma disposição para se acostumar.

Larry Coleman
fonte
0

Depende. Obviamente, o T-SQL fornece exclusivamente algumas soluções relacionais de banco de dados. Obviamente, o LINQ fornece exclusivamente algumas soluções OO.

Contudo; "mais confuso que T-SQL?" - é discutido / solicitado na pergunta inicial. Isso implica claramente alguns recursos que nenhuma das respostas existentes aborda, acusando o crítico (obviamente familiarizado com o SQL) de ficar preso no passado.

Portanto, embora eu aprecie o LINQ por certas qualidades e não discorde muito das respostas existentes aqui, sinto que o contraponto merece representação:

Anos depois de me familiarizar com o LINQ, executar certos tipos de operações de grupo, junções externas e não equivalentes, trabalhar com chaves compostas e outras operações no LINQ ainda me fazem estremecer. (Especialmente ao direcionar um back-end relacional com perguntas sensíveis ao desempenho.)

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

Se você acha que é intuitivo, mais poder para você. ;)

Shannon
fonte
-4

Provavelmente existe uma razão pela qual o Linq é péssimo, apenas tente fazer exemplos do mundo real, em vez desses exemplos de livros didáticos.

tente mostrar essa função em linqlamdathings e verá que toda a beleza se foi, enquanto a maneira clássica permanece legível. Sem mencionar problemas de execução adiados e configuração de pontos de interrupção.

A verdade é que LinqLambda é muito útil em alguns casos e não se pensa que substitua toda a orientação bacana que você nunca entendeu

    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
fonte
6
Eu acho que é source.Where(c => c.IsVip && c.FirstName == "Aaron" || c.SecondName == "Aaron").Take(maxCount).OrderBy(d => d.LastName);difícil ler o seu, então eu posso ter cometido um erro;)
jk.
1
De que maneira você considera esses exemplos de livros didáticos? Na verdade, esse é o código de uso da produção. Seria útil se você fornecesse o código "legível" clássico e a versão linq / lamda para comparação, em vez de esperar que todos o convertessem.
BlackICE 14/03
3
Você se registrou especificamente para reclamar do LINQ?
KChaloux
1
@KChaloux Eu acho que eles estavam apenas tentando ser honestos
jk.