miércoles, julio 30, 2008

Acerca de Linq

"Vos si te llenas de mocos por bobadas"... Eso me dijo alguien cuando le dije que necesitaba responder al comentario de un amigo en Twitter. Y si... lleno de mocos y todo empiezo. :P

Pues bien. Es básicamente lo siguiente:

febuiles http://xrl.us/ok5v2 "The idea that you can express your intent in terms of higher-level query expressions is really new" - Nope, sorry.

diegoeche @febuiles porque no es nueva?

febuiles @diegoeche: Twitter no es el medio: Tumblr

Y el post es básicamente esto:

var products = from p in db.Products
where p.Category.CategoryName == "Beverages"
select p;

products = Products.find_by_category_name("Beverages")

Los queries que abstraen condiciones de un nivel inferior (en este caso SQL) no son una idea nueva. Los dos ejemplos anteriores hacen lo mismo y solo difieren en sintaxis.


Decir que Linq es ultra-novedoso es no saber medir el adjetivo "ultra-novedoso". Linq es 90% Marketing y 10% buenas ideas... el que no se haya dado cuenta de eso es un MVP cerebri-lavado.

No comparto la idea de Eric Meijer, sin embargo creo que el ejemplo está muy mal escogido.

En primer lugar no consideraría llamar una función con un nombre bonito un DSL. De hecho me parece sobre simplificar el termino llamar DSL a cualquier conjunto de identificadores bonitos dentro de una sintaxis externa para una tarea específica. De la misma manera me parece erróneo llamar a "está función con nombre bonito" algo equivalente "higher-level query expressions".

La sentencia de Linq después de de-sugaring es algo como:

db.Products.Where(p => p.Category.CategoryName == "Beverages").Select(p => p);
(Probablemente por usar la proyección de identidad se omita el último Select)


Eso no es nuevo... reemplaza uno filter por Where y map por Select y es algo tan viejo como Lisp (de pronto anterior?) y me parece muy de "alto nivel". Lo que creo que se refiere Meijer como "nuevo" es precisamente el de-sugaring (que por lo que tengo entendido va pasando la la variable "p" por medio de una monada).

Así que cuando dice "Los dos ejemplos anteriores hacen lo mismo y solo difieren en sintaxis." Resulta un comentario vacuo.

Uno puede pasar funciones en Java usando clases anónimas de un solo método y puede decir uno "Java hace lo mismo que haskell... solo difiere la sintaxis"

O acaso un foreach no es nuevo comparado con un for??
Ambos hacen lo mismo... difieren en la sintáxis... y si encapsulo el for en una función que lo haga ver bonito? la inclusión del foreach en un lenguaje de programación como elemento sintáctico deja de ser nueva? no.

Pero es precisamente ésto a lo que se refiere Eric Meijer. Incluir sintaxis adicional en el lenguaje con el fin de operar con colecciones es "nuevo". Que tan nuevo? tan nuevo como Linq? la verdad no sé.

Y novedoso? Pues uno usando la monada de lista de haskell puede hacer lo que hacen las Query Expressions.

Ahora bien... se que es estúpido escribir en vez de una refutación un "el argumento que usaste es muy malo" pero hace rato no escribía.

3 comentarios:

Federico Builes dijo...

>> En primer lugar no consideraría llamar una función con un nombre bonito un
DSL. De hecho me parece sobre simplificar el termino llamar DSL a cualquier
conjunto de identificadores bonitos dentro de una sintaxis externa para una
tarea específica. De la misma manera me parece erróneo llamar a "está función
con nombre bonito" algo equivalente "higher-level query expressions".

La función que dí como ejemplo en el caso de Rails no existe, por medio de
metaprogramación + method_missing se crea el query, así que no es un simple
"llamado a una función con un nombre bonito".

Por otro lado, en este ejemplo:

25.percent.of 16 # => 16 * 0.25

Eso sí es un encadenamiento de funciones para definir un DSL, ¿ no considerás
eso un lenguaje? Si no es así, ¿en qué pensas a la hora hacer lenguajes de
dominio?, ¿crear una gramatica desde cero, un compilador o interprete y
guardarlo aparte para cargarlo como una cadena? Si es así te estás entonces
limitando a DSLs externos, se usan solamente cuando el lenguaje no te permite
hacer algo suficientemente legible dentro de el.

>> Lo que creo que se refiere Meijer como "nuevo" es precisamente el
de-sugaring.

Esta parte que creo que es la más importante de tu artículo no la
entiendo. Si estás diciendo que lo nuevo es el resultado final (en lo que se
transforma, lo que pasa abajo), volvemos al mismo cuento, estás llamando tres
funciones con 3 selectores (delegados, lambdas, etc) que no es nada del otro
mundo, y mucho menos nuevo:

[:p | p Category CategoryName == "Beverages"] # Smalltalk

Por cierto, la versión de lo que escribí en Ruby hace lo siguiente (omitiendo
el catch a la llamada de un método inexistente por longitud):

Products.find_all { |p| p.category_name == "Beverages }

Omitiendo obviamente la identidad.

Entonces:

Products.find_all { |p| p.category_name == "Beverages }
db.Products.Where(p => p.Category.CategoryName == "Beverages").

¿Son tan diferentes después de todo? Metele la teoría que querás detrás y
decí que el comentario es vacío, pero sigo viendolos iguales. Azucar para que
se vean lindos es la diferencia, lo que está pasando atrás es igual.

>> Uno puede pasar funciones en Java usando clases anónimas de un solo método y
puede decir uno "Java hace lo mismo que haskell... solo difiere la sintaxis"

No, al pasar un objeto con un solo método **el resultado final y las
posibilidades son diferentes**. Ambos lo sabemos pero con mucho gusto
escribiré el código si es necesario para mostrar porque uno no puede emular
totalmente el otro.

>> O acaso un foreach no es nuevo comparado con un for?? Ambos hacen lo
mismo.

No, no hacen lo mismo. Uno itera sobre una colección tomada cada elemento de
la colección, dandole un nombre local y luego aplicandole una operacion
mientras el otro ejecuta una operacion n veces. Tienen un nombre parecido y
en ciertos lenguajes también una sintaxis parecida, pero de nuevo, tienen
objetivos diferentes.

>> Pero es precisamente ésto a lo que se refiere Eric Meijer. Incluir
sintaxis adicional en el lenguaje con el fin de operar con colecciones es
"nuevo".

Esto _creo_ que no lo dijo Meijer sino el otro tipo de la entrevista. Esta
sintaxis para operar sobre colecciones como te dije se conoce como DSLs para
el resto del mundo y existía en Lisp, Smalltalk, Ruby y seguramente en
otros. En Common Lisp:

(select 'products :where [= [slot-value 'product 'category-name]
"Beverages"])

No opera solamente en listas (ya que para estas hay operaciones más cortas)
sino que si instalas CLSQL pasa a servir para SQL también, y estoy seguro que
debe funcionar sobre otras cosas.


Para finalizar: La gente que impulsa lenguajes es realmente contenta hablando
sobre lo nuevo del suyo, al principio a Java lo vendían como el primer
lenguaje realmente orientado a objetos (lawlz). Yo personalmente sigo creyendo que
Meijer (y no el otro tipo) es de las pocas personas de bien que queda en MS y
el mismo sabe que no están introduciendo nada nuevo.

diegoeche dijo...

>>Por otro lado, en este ejemplo:
>>25.percent.of 16 # => 16 * 0.25

Un juego con una sintaxis establecida y los nombres de los identificadores. No me importa lo que diga toda la comunidad de Ruby o Smalltalk juntas. Creo que un verdadero DSL está en estos usage patterns... pero no quiero caer en una discusión de "el verdadero irlandes"

http://en.wikipedia.org/wiki/Domain_Specific_Language#Usage_patterns

>> Lo que creo que se refiere Meijer como "nuevo" es precisamente el
de-sugaring.

Me equivoque totalmente :'( creo que lo nuevo es la syntactic-sugar para hacer el query, no su de-sugaring. Creo que el resto del post se malentiende por esto.

Mi idea era establecer que el hecho de que el de-sugaring de un feature resulte en algo ya existente no quiere decir que no sea "nuevo" y en eso iban encaminados mis ejemplos... Si no me hice entender otro ejemplo que creo es mas claro puede ser con python.

Las comprensiones de listas al final son traducidas en un for con un append. Decir que las comprensiones de listas en python no son nuevas porque es "es lo mismo pero con diferente sintaxis" me parece equivocado... cae uno en el absurdo de hacer una paulatina descomposición de cualquier elemento gramátical de un lenguaje de programación hasta assembler y decir "no veo la diferencia... al final hacen lo mismo"

Que pena por el malentendido

Federico Builes dijo...

>> Un juego con una sintaxis establecida y los nombres de los identificadores. No me importa lo que diga toda la comunidad de Ruby o Smalltalk juntas. Creo que un verdadero DSL está en estos usage patterns... pero no quiero caer en una discusión de "el verdadero irlandes"

Ok, entonces hablamos de lo que puse como "External DSLs". Es curioso que esa
entrada de Wikipedia hable de como están separados del lenguaje de
programación pero luego ponen como ejemplo los scripts de Bash, pero dicen:

"Even though UNIX scripting languages are Turing-complete, they differ from
General Purpose Languages". Sin un por qué me parece que está mal, pero
bueno, es Wikipedia.

>> Me equivoque totalmente :'( creo que lo nuevo es la syntactic-sugar para hacer el query, no su de-sugaring. Creo que el resto del post se malentiende por esto.

Mi argumento se mantiene, los dos ejemplos:

var products = from p in db.Products
where p.Category.CategoryName == "Beverages"
select p;

products = Products.find_by_category_name("Beverages")

Siguen pareciendome iguales, pueden parecer distintos si los comparamos
caracter por carater pero el objetivo es el mismo, expresado de maneras
diferentes. Entiendo lo que decís de descomponer los ejemplos a lo que pasa
por debajo pero en este caso la comparación es entre los dos elementos de
nivel superior que por ser parte de lenguajes diferentes obviamente van a ser
diferentes, pero la ideal sigue siendo la misma (queries de manera
amigable).

Ahora, para discutir si uno es más "innovador" que el otro, o por simple
gusto, ¿con cuál de las dos alternativas te quedás? Esto obviamente es
personal e irrelevante a la discusión.


>> Mi idea era establecer que el hecho de que el de-sugaring de un feature
resulte en algo ya existente no quiere decir que no sea "nuevo" y en eso
iban encaminados mis ejemplos...

Si la azucar sintáctica de este caso fuera nueva no discutiría nada, pero es
que mi punto es que tampoco lo es: el ejemplo que puse de Lisp en el
comentario anterior o Ambition en Ruby son prueba de eso.

Finalmente,

>> Decir que las comprensiones de listas en python no son nuevas porque es
"es lo mismo pero con diferente sintaxis" me parece equivocado

En este caso tenemos la diferencia de que sí _creo_ que la comprensión de
listas (?) sea algo nuevo. No se si sea originalmente de Erlang, Python o de Haskell
ya que no se por tiempo cual lo haya tenido primero, pero lo único que he visto
parecido en Lisp es:

(collect (list) ((* x x))
(in (x) '(1 2 3 4 5 6 7 8)))

Y está basado en Erlang.

De nuevo, mi punto no es que agregar azucar sintáctico sea algo malo o que no
se pueda considerar como algo nuevo (programo Ruby, vivo para esa mierda
:). Mi punto es que en el caso especial de Linq es algo que ya existía antes
de que C# tuviera un repositorio de SourceSafe o lo que sea que usen en
Microsoft, y ellos intentan venderlo como algo nuevo. Los programdores de C#
y VB, siendo programadores de C# y VB obviamente hasta se comen el cuento.