¿El uso de LINQ y Lambda Expressions conduce a un código menos legible? [cerrado]

43

Estoy teniendo una discusión con un compañero de trabajo en Linq, copiaré aquí:

Compañero de trabajo: seamos honestos aquí. La sintaxis de Linq apesta. Es confuso y no intuitivo.

Yo: oh, vamos, ¿más confuso que T-SQL?

Compañero de trabajo: uh, sí.

Yo: tiene las mismas partes básicas, seleccionar, dónde y de

Compañero de trabajo: Linq, para mí, es una bastarización de relacional + OO. Compañero de trabajo: No me malinterpreten, es increíblemente poderoso, pero reutilizaron SQL para usar colecciones de objetos.

Soy de la opinión de que usar Linq + Lamda's es muy poderoso (él está de acuerdo), y también hace que el código sea más fácil de leer (no está de acuerdo en ese punto):

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

o

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

o (código VB aquí)

   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 mí esto es limpio y fácil de leer (al menos más fácil que las alternativas), ¿cuáles son sus opiniones al respecto?

BlackICE
fuente
11
Los humanos, en general, odian el cambio. Un gran porcentaje lo odia tanto que realmente lo temen.
Tony
99
Seamos realistas ... linq es solo una sintaxis de programación funcional tonta agregada a C # y VB.Net. Bashing expresiones linq y lambda básicamente dice "FP apesta". Eso es lo que dice tu compañero de trabajo. Creo que ese debate se ha generado en otros lugares.
Scott Whitlock, el
48
¿Le molesta a alguien que las personas tienden a usar la palabra "intuitivo" cuando realmente quieren decir "familiar"?
Larry Coleman
77
De todos modos, el equipo de C # (incluido Eric Lippert) ha hecho todo lo posible para explicar que Linq no era un portado de SQL, fue diseñado desde cero como la mayoría de las otras características. Debo decir que tu compañero de trabajo es un ludita.
Aaronaught
16
Para lo que vale: acabo de pedirle a mi esposa (administrador de la oficina - experiencia práctica de programación casi nula) que mire los 3 ejemplos de aaronaught y pude descifrar la intención de los ejemplos linq y lambda mucho más fácilmente que el ejemplo imperativo tradicional.
Steven Evers

Respuestas:

73

Ya no puedo encontrar la publicación correcta, pero Eric Lippert (y posiblemente varios otros softwares) han opinado en varias ocasiones sobre cómo Linq es declarativo , lo que, para varias clases de problemas, es mucho más intuitivo que la sintaxis imperativa .

Linq le permite escribir código que expresa la intención , no el mecanismo .

Me dices cuál es más fácil de leer. Esta:

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

¿O esto?

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

O incluso esto?

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

El primer ejemplo es solo un montón de repeticiones sin sentido para obtener los resultados más simples. Cualquiera que piense que es más legible que las versiones de Linq necesita que le examinen la cabeza. No solo eso, sino que el primero desperdicia memoria. Ni siquiera puedes escribirlo usando yield returnla clasificación.

Tu compañero de trabajo puede decir lo que quiera; personalmente, creo que Linq ha mejorado la legibilidad de mi código de manera inconmensurable.

Tampoco hay nada "relacional" sobre Linq. Puede tener algunas similitudes superficiales con SQL, pero no intenta de ninguna forma implementar el cálculo relacional. Es solo un conjunto de extensiones que hacen que sea más fácil consultar y proyectar secuencias. "Consulta" no significa "relacional", y de hecho hay varias bases de datos no relacionales que usan una sintaxis similar a SQL. Linq está puramente orientado a objetos, simplemente funciona con bases de datos relacionales a través de marcos como Linq to SQL debido a un vudú de árbol de expresión y un diseño inteligente del equipo de C #, lo que hace que las funciones anónimas se conviertan implícitamente en árboles de expresión.

Aaronaught
fuente
66
+1 Si no te gusta LINQ probablemente sea porque "no lo estás haciendo bien" :)
Darknight
1
Linq is purely object-oriented+1 por esto. Esta es también la razón por la cual nuestra guía de estilo de equipo impone el uso de la sintaxis fluida sobre la sintaxis de la consulta. Me parece que hace que la naturaleza OO del enlace sea más obvia para alguien acostumbrado a objetar la notación en lenguajes tipo C.
SBI
1
Sé que la publicación tiene 7 años. Pero aquí está la pregunta: ¿usó lenguajes funcionales como herramientas principales antes de conocer a Linq?
Sergey.quixoticaxis.Ivanov
44
Francamente, prefiero el bucle foor, porque entonces veo de inmediato dónde podemos y tendremos excepciones de referencia NULL, cómo se maneja nulo, y no es la ejecución diferida lo que dificulta la depuración, y no crea n listas también, por lo que el ciclo for también es más eficiente.
Dilema
1
@Aaronaught, si elimina la ordenación y reformatea el código de procedimiento en estilo Horstmann , diré que es más legible que la versión LINQ . Y de acuerdo con el principio de responsabilidad única, la clasificación en realidad no pertenece GetVipCustomers(), lo que, como su propio nombre sugiere, solo devolverá una colección de clientes VIP, en un orden arbitrario. Para los casos excepcionales en los que el orden es importante, como la salida a la pantalla, deje que la persona que llama clasifique la colección.
Ant_222
69

Compañero de trabajo: seamos honestos aquí. La sintaxis de Linq apesta. Es confuso y no intuitivo.

No se puede discutir con esa crítica. Para su compañero de trabajo, es una mierda . No pudimos diseñar una sintaxis que, para ellos, fuera clara e intuitiva. Ese es nuestro error, y puedes transmitir mis disculpas a tu compañero de trabajo. Me complace tomar sugerencias sobre cómo mejorarlo; ¿Qué encuentra específicamente su compañero de trabajo confuso o poco intuitivo?

Sin embargo, no puedes complacer a todos. Mi opinión personal, y la opinión de la mayoría de las personas con las que he hablado sobre el tema, es que la sintaxis de comprensión de consultas es mucho más clara que la sintaxis imperativa equivalente. Claramente, no todos están de acuerdo, pero afortunadamente no requerimos el consenso de todos los millones de nuestros clientes cuando hacemos el diseño del lenguaje.

Sin embargo, sobre el tema de lo que es "intuitivo", recuerdo la historia del lingüista inglés que estudió muchos idiomas diferentes y finalmente concluyó que el inglés era el mejor de todos los idiomas porque en inglés, las palabras vienen en el mismo orden en que usted Piensa en ellos . A diferencia del francés, donde constantemente dicen cosas como "el perro blanco come la carne roja". ¡Qué difícil debe ser para los franceses pensar las palabras en el orden correcto y luego tener que decirlas en el orden francés ! ¡El francés es tan poco intuitivo! Es sorprendente que los franceses logren hablarlo. ¿Y alemán? donde piensan que "el perro se come la carne" pero luego tienen que decir "¡el perro se come la carne"!

A menudo, lo que es "intuitivo" es simplemente una cuestión de familiaridad. Me llevó meses trabajar con LINQ antes de dejar de comenzar mis consultas con la cláusula "select". Ahora es una segunda naturaleza, y el orden SQL parece extraño.

¡Cual es! Las reglas de alcance están desordenadas en SQL. Algo que quizás desee señalar a su compañero de trabajo es que LINQ fue cuidadosamente diseñado para que (1) la introducción de variables y ámbitos ocurra de izquierda a derecha (*), y (2) el orden en que aparece la consulta en la página es El orden en que se ejecuta. Es decir, cuando dices

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

la c aparece en el alcance a la izquierda y permanece en el alcance a la derecha. Y el orden en que suceden las cosas es: primero se evalúa a los "clientes". Luego se evalúa el "dónde" para filtrar la secuencia. Luego, la secuencia filtrada se proyecta mediante la "selección".

SQL no tiene esta propiedad. Si usted dice

SELECT Name FROM Customers WHERE City = 'London'

entonces "Nombre" se pone en alcance por algo a su derecha, no a su izquierda, y la consulta se ejecuta en un orden completamente desordenado; la cláusula intermedia se evalúa primero, luego la última cláusula y luego la primera cláusula. Eso ahora me parece loco y poco intuitivo, ya que he trabajado únicamente con LINQ durante tanto tiempo.


(*) Las reglas de alcance son un poco extrañas en LINQ con cláusulas de unión. Pero aparte de eso, los ámbitos anidan muy bien.

Eric Lippert
fuente
55
Nitpick: En alemán, "Der Hund frisst das Fleisch" es en realidad el mismo orden que en inglés, "El perro come la carne" :) Aparte de eso, encontré que la sintaxis de LINQ Query es muy legible una vez que me di cuenta de que no era SQL .
Michael Stum
18
@gbjbaanb: No estoy justificando la sintaxis de LINQ sobre la base completamente subjetiva de que sea más comprensible . Más bien, lo estoy justificando sobre la base objetiva de que es mucho más fácil diseñar herramientas que ayuden al usuario en la construcción de consultas LINQ porque la sintaxis LINQ se diseñó teniendo en cuenta las herramientas, y que es más fácil comprender mentalmente orden en que los eventos suceden en una consulta porque se presentan en el mismo orden léxico.
Eric Lippert
2
Eric, desde una perspectiva de programación del estilo declarativo, entiendo que Linq es apropiado (es decir, legible) que SQL. ¿Pero es más legible para los humanos que SQL? De ninguna manera. Si 'el perro come carne roja' es difícil, ¿cuánto más fácil es 'desde la cocina donde el color es rojo obtener un cuchillo'? De hecho, todos hablamos y pensamos como 'consigue ese cuchillo que está encima de la mesa de la cocina'. Y ahí es donde SQL está más cerca de la vida real que LINQ.
nawfal
66
Quiero una taza de café de Starbucks, donde la tienda está abierta hasta la medianoche. ¿Te suena familiar? Está exactamente en el mismo orden que una consulta SQL. Sí, LINQ puede ser más legible que el código innecesario que debe generar para ejecutar una consulta SQL estándar, pero LINQ no es más legible que la consulta SQL en sí. Entonces sí; puede ser ventajoso para los desarrolladores de LINQ tener un alcance de izquierda a derecha, pero como usuario, ¿por qué me importa? Solo me importa la usabilidad.
KyleM
8
@KyleM: Por supuesto; la usabilidad fue un aspecto muy importante del diseño LINQ. En particular, ser clave para las herramientas de productividad fue clave. Debido a que el alcance fluye desde la izquierda para escribir en una consulta LINQ, en el momento en que escribe c.el IDE ya conoce el tipo cy puede darle IntelliSense. En LINQ usted dice "de c en clientes donde c". y boom, obtienes IntelliSense ayudándote al enumerar los miembros de Customer. En SQL, cuando escribe "SELECCIONAR nombre DE clientes", no puede obtener ninguna ayuda de IDE para decirle que "nombre" es una buena opción porque escribió name antes customers .
Eric Lippert
22

Como cualquier otra cosa en el mundo de la programación, debes acostumbrarte a la sintaxis, y luego es (potencialmente) más fácil de leer.

Como cualquier otra cosa en el mundo de la programación, existe el potencial para el código de espagueti u otros abusos.

Como cualquier otra cosa en el mundo de la programación, puedes hacerlo de esta manera o de otra.

Como cualquier otra cosa en el mundo de la programación, su kilometraje puede variar.

Wonko el cuerdo
fuente
6

Vi un comentario / comentario donde decía algo, en relación con LINQ / lambda, en la línea de: "Escribe código legible para los humanos, en lugar de legible para tu computadora".

Creo que esta declaración tiene mucho mérito, sin embargo, considere al desarrollador (como yo) que ha pasado por toda la gama de lenguajes de desarrollo desde Assembly, procesales, OO, gestionados, aprovechando soluciones paralelas de tareas de alto rendimiento .

Me he enorgullecido de hacer que mi código sea lo más legible y reutilizable posible y de adoptar muchos de los principios del patrón de diseño de GOF para ofrecer sistemas y servicios de calidad de producción en una gran cantidad de sectores comerciales dispares.

La primera vez que me encontré con la expresión lambda pensé: "¡¿Qué demonios es eso ?!" Fue inmediatamente contra-intuitivo a mi sintaxis declarativa explícita familiar (y por lo tanto cómoda). ¡Los chicos menores de 5 años en el trabajo, sin embargo, lo lamieron como si fuera maná del cielo!

Esto se debe a que, durante años, pensar como una computadora (en sentido sintáctico) se tradujo muy fácilmente en sintaxis de codificación directa (independientemente del lenguaje). Cuando haya tenido esa mentalidad computacional durante alrededor de 20 años (más de 30 en mi caso), debe apreciar que el shock sintáctico inicial de la expresión lambda puede traducirse fácilmente en miedo y desconfianza.

¿Quizás el compañero de trabajo en el OP provenía de un entorno similar al mío (es decir, había estado en la cuadra varias veces) y era contra-intuitivo para ellos en ese momento? Mi pregunta es: ¿qué hiciste al respecto? ¿Intentó reeducar a su compañero para que comprenda los beneficios de la sintaxis en línea, o los ridiculizó / excluyó por no "estar con el programa"? El primero probablemente habría visto a su compañero de trabajo acercarse a su línea de pensamiento, el último probablemente los haría desconfiar aún más de la sintaxis de LINQ / lambda y exacerbar así la opinión negativa.

Por mi parte, tuve que reeducar mi propia forma de pensar (como Eric infiere anteriormente, no es un cambio mental insignificante, y tuve que programar en Miranda en los años 80, así que he tenido mi parte de experiencia en programación funcional) pero una vez que pasé por ese dolor, los beneficios fueron obvios pero, lo que es más importante, donde su uso se usó en exceso (es decir, por el simple hecho de usarlo), más complejo y repetitivo (considerando el principio DRY en ese caso).

Como alguien que no solo todavía escribe mucho código, sino que también tiene que revisar técnicamente mucho código, era imperativo que entendiera estos principios para poder revisar los artículos de manera imparcial, aconsejar dónde el uso de una expresión lambda puede ser más eficiente / legible, y también para que los desarrolladores consideren la legibilidad de expresiones lambda en línea altamente complejas (donde una llamada a un método, en esos casos, haría que el código sea más legible, mantenible y extensible).

Entonces, cuando alguien dice que "no tienen lambda?" o la sintaxis LINQ, en lugar de calificarlos como un ludito, intente ayudarlos a comprender los principios subyacentes. Después de todo, pueden tener antecedentes de "vieja escuela" como yo.

CWC
fuente
Comentario interesante: tuve esto cuando cambié de lenguajes estáticos fuertemente tipados a lenguajes dinámicos. Me tomó un tiempo adaptarme (miedo y desconfianza) y ahora me cuesta volver, sinceramente.
lunchmeat317
4

Lambda Expressions conduce a un código menos legible si las consultas son demasiado largas. Sin embargo, es mucho mejor que demasiados bucles anidados .

Es mejor con una mezcla de los dos .

Escríbalo en Lambda si es más rápido (necesita que sea rápido) o más fácil de leer.

Amir Rezaei
fuente
Sí, pero ¿qué haría que linq / lamda no fuera más fácil de leer?
BlackICE
lo siento, significaba que no era más fácil de leer que la alternativa.
BlackICE
1

Creo que depende en la mayoría de los casos (excepto cuando se hace algo muy extraño) si quieres decir "legible" como si alguien tuviera la idea de lo que está sucediendo o si puede encontrar fácilmente todos los pequeños detalles.

Creo que el enlace ayuda con el primero, pero a menudo (especialmente cuando se depura) perjudica al segundo.

En mi humilde opinión, cuando estoy mirando el código que no estoy familiarizado con el primero es mucho más importante que el segundo, por lo que me parece mucho más legible.

Cuenta
fuente
Buen punto. Este último generalmente está cubierto por una buena prueba de unidad.
Scott Whitlock, el
¿Entonces cree que los aspectos internos de la evaluación de linq hacen que sea difícil encontrar todos los detalles? ¿Puedes dar un ejemplo de cuándo puede ser?
BlackICE
3
@David: las expresiones de Linq no solo se evalúan de forma perezosa, son expresiones . El código de recepción de una expresión linq en realidad puede modificar la expresión para que haga algo completamente diferente, como una macro lisp que trabaja en expresiones s. Por ejemplo, cuando se trabaja con linq to SQL, en realidad toma la expresión where e.FirstName == "John"y la traduce en una consulta SQL. Lo hace al observar la expresión no compilada (pero analizada), al ver que es una comparación de una propiedad invocada FirstNameen una entidad persistente, y se compara con una cadena, etc. Están sucediendo muchas cosas.
Scott Whitlock, el
@david generalmente no encuentro los detalles difíciles de encontrar hasta que empiezas a usar linq contra sí mismo. Un ejemplo "simple" de esto que me llevó mucho tiempo entender es este: bugsquash.blogspot.com/2008/07/y-combinator-and-linq.html, pero hay muchos más ejemplos necesariamente en algunos de las cosas que las personas están haciendo con expresiones en las áreas dinámica, DynamicObject e IDynamicMetaObjectProvider. Donde se dibuja la línea de lo que es linq es discutible, pero como señala Scott, todo eso está disponible para / en linq porque linq usa los mismos árboles de expresión.
Bill
@Bill, de acuerdo, te daré eso es difícil, pero las funciones de combinador y y de alto orden van a dañar la mayoría de nuestras cabezas, es como una recursión, lo entiendes o no. ¿La implementación recursiva de eso es más fácil de leer (si no conocía ninguno de los dos)?
BlackICE
1

Encuentro que la sintaxis de LINQ es intuitiva y fácil de leer, especialmente porque pusieron el FROM al comienzo donde pertenece en lugar de en el medio, como en SQL. Pero las lambdas de la OMI son confusas y hacen que el código sea más difícil de leer.

Mason Wheeler
fuente
¿Por qué crees que las lambdas son confusas? ¿Es la inferencia de tipo?
ChaosPandion
1
@ChaosPandion: Primero, la inferencia de tipos, y segundo, el símbolo. Poner un = y un> juntos parece un> = que alguien se equivocó y escribió al revés, y tiende a dar vueltas a mi cerebro.
Mason Wheeler
@Mason - ¿Te gusta la sintaxis de Haskell ( \x -> x * x) o F # ( fun x -> x * x)?
ChaosPandion
@ChaosPandion: No, no particularmente. Las Lambdas pueden ser rápidas de escribir, pero me resulta difícil analizarlas, especialmente cuando se vuelven no triviales en complejidad. Sus ejemplos no son tan malos, pero tienden a empeorar mucho.
Mason Wheeler
66
@Mason: Parece que estás objetando el abuso de lamdas, en lugar de objetar la idea de lamdas en primer lugar.
Anon
1

Estoy de acuerdo con usted en que la sintaxis de Linq no es significativamente diferente de T-SQL. Creo que su compañero de trabajo realmente podría estar objetando que las cosas relacionales se mezclen en su código OO agradable y brillante. Por otro lado, la programación funcional requiere un poco de tiempo para acostumbrarse y la voluntad de acostumbrarse.

Larry Coleman
fuente
0

Depende. Obviamente, T-SQL proporciona de forma exclusiva algunas soluciones relacionales de base de datos. Obviamente, LINQ ofrece de manera única algunas soluciones OO.

Sin embargo; "más confuso que T-SQL?" - se discute / pregunta en la pregunta inicial. Esto implica claramente algunas características que ninguna de las respuestas existentes aborda, en su lugar, acusa al crítico (obviamente familiarizado con SQL) de estar atrapado en el pasado.

Entonces, aunque aprecio LINQ por ciertas cualidades, y no estoy muy en desacuerdo con las respuestas existentes aquí, siento que el contrapunto merece representación:

Años después de familiarizarme con LINQ, realizar ciertos tipos de operaciones grupales, uniones externas y no equijoins, trabajar con teclas compuestas y otras operaciones en LINQ todavía me da escalofríos. (Especialmente cuando se dirige a un back-end relacional con preguntas sensibles al rendimiento).

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

Si crees que es intuitivo, más poder para ti. ;)

Shannon
fuente
-4

Probablemente haya una razón por la cual Linq apesta, solo trate de hacer ejemplos del mundo real, en lugar de estos ejemplos de libros de texto.

intente mostrar esta función en linqlamdathings y verá que toda la belleza se ha ido, mientras que la forma clásica sigue siendo legible. Sin mencionar los problemas de ejecución diferida y el establecimiento de puntos de interrupción.

La verdad es que LinqLambdaThings son muy útiles en algunos casos y no se cree que reemplacen toda la ingeniosa orientación a objetos que ustedes nunca entendieron

    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;
    }
bagazo
fuente
66
Creo que es source.Where(c => c.IsVip && c.FirstName == "Aaron" || c.SecondName == "Aaron").Take(maxCount).OrderBy(d => d.LastName);pero es difícil leer el tuyo, así que puede que haya cometido un error;)
jk.
1
¿De qué manera consideró que estos son ejemplos de libros de texto? Ese es en realidad el código de uso de producción. Sería útil si proporcionara tanto su código clásico "legible" como la versión linq / lamda para comparar en lugar de esperar que todos lo conviertan.
BlackICE
3
¿Te registraste específicamente para quejarte sobre LINQ?
KChaloux
1
@KChaloux Creo que solo estaban trolleando para ser honesto
jk.