sábado, febrero 28, 2009

Ajedrez

Hyperbolic surfaces, ambient occlusion, diffuse reflections, area lights, focal blur

Etiquetas:

miércoles, febrero 18, 2009

Otra seta, perdón, libro sobre LINQ

Cuando comparo este libro con una seta (y la seta sale ganando al final) no es sólo por la capacidad de propagación por esporas de los libros sobre LINQ sino, principalmente, por su potencial alucinógeno. Leo un par de páginas y alucino. Luego viene la resaca. Pasa un par de horas y siento el mono de volver a comprobar cuán malo es el libro. Y así, entre risas y risas, me lo he leído. Pero las setas tienen fibra y son buenas para el colon.
Vale, me he pasado. Si a usted le cuesta leer en pantalla, este libro puede sustituir a la ayuda en línea, siempre que renuncie al índice, a las búsquedas y a ese estilo seco de los documentos de Microsoft que tanto gusta a los amantes del dolor. De todos modos, supongo que tengo que justificarme. Ahí va eso...
El principal problema de (joder, qué pedazo de título)... bueno, de este título, es la bisoñez del autor, acompañada de un exceso de entusiasmo, buenrrollismo y grandes dosis de soberbia injustificada. Este es un libro de apenas 380 páginas, con los típicos listados sin abreviar y con un tipo de letra más grande que la del texto. A pesar de todo esto, el autor se permite escribir un ditirambo de cuarenta páginas en honor a los ORM... demostrando de paso que no tiene ni pajolera idea del asunto. Ahí va un sabroso "tip" (página 5):
Note: Writing stored procedures does not equate to bad design. On the contrary, stored procedures if managed correctly are an excellent alternative to dynamic SQL. If you use a code generator to create your CRUD stored procedures, or if you develop them by hand and have good oversight, in many cases you will be able to use them in conjunction with an ORM tool.
¡Gracias, gran Vijay (así se llama el pavo), por perdonar nuestras viles existencias! Pero siento interrumpir: usted, ¿ha entendido algo? ¿Son buenos o no los procedimientos almacenados? ¿Sólo si se escriben a mano, o si los crea una herramienta? Ah, ¿en ambos casos? Entonces, ¿para qué mencionar que mañana lloverá o no? El problema está en que el Gran Vijay "sabe" que los procedimientos almacenados sólo son buenos si sirven a los propósitos del Gran ORM. Pero soltar tal chorrada es peligroso. Te puede enemistar con esa bestia cejijunta del DBA (en cristiano, el Administrador de Bases de Datos). Los DBA's, cuando se enfadan, te dejan sin índices secundarios y te echan sal en el café.
"Claro, Marteens, cabronazo, pero nos has ocultado el contexto de la parida". Bien aquí tiene el contexto: el señor Mehta nos explica que la compañía X ha escrito software para un fabricante de comida para perros. ¿Y sabe qué?, pues que usaron procedimientos almacenados. La cosa se les escapa de las manos (claro, porque son unos pecadores relacionales). Y de repente, llega la gran oportunidad: pueden vender la aplicación a un fabricante de comida para gatos. Aquí viene la Segunda Parida:
With the increased development costs, Company X won't be making enough money to justify this deal [...] so they hire a consulting company to help reduce their overhead.
Vijay, hermanito, que nunca se te ocurra montar una empresa con los ahorros de papá, que te los vas a fundir. La consultoría, evidentemente, les endiña un ORM a la Compañía X, y según palabrita del niño Vijay, (they) optimize and normalize the company database. Lamento señalar que:
  • Lo de "optimizar y normalizar" suele resultar bastante costoso. Es como empezar de cero. Para que resulte viable, es obligatorio que la consultoría subcontrate programadores en algún país remoto del Tercer Mundo. Quizás los tiros vayan por ahí.
  • Ah, de repente la base de datos carece de "normalización". ¿Esa es una consecuencia de utilizar procedimientos almacenados?
Pero la diversión continúa:
Company X is able to eliminate the thousands of stored procedures (que ya funcionaban) and instead use "automagic" mapping features to generate its SQL. Development costs go down, profits go up and everyone is happy [...]
... especialmente el niño Vijay, que ya tiene el ejemplo canónico que siempre se escribe en los primeros capítulos. Menos mal que enseguida nos aclara que "this is an oversimplified example". Menos mal. Porque cuando la magia entra por la puerta, la inteligencia salta corriendo por la ventana.
Vale, reconozco que los escritores solemos rellenar los primeros capítulos con la misma alegría con la que los chinos echaban melamina en la leche para bebés. El problema es que los problemas continúan a lo largo del libro... excepto en las páginas que contienen información irrelevante, o en las que se limitan a tirar de la ayuda. Para no hacer esta masacre demasiado larga, aquí tiene un botón de muestra, sumamente divertido y revelador. El hermanito Vijay intenta explicarnos por qué prefiere no usar var para declarar variables locales con tipo implícito (p. 149):
The reason to be explicit with the types is the same reason that you strongly type variables in the world of software development: to make it easier to understand and gain a negligible performance increase.
Heeeelp meeee!Las negritas son mías, por supuesto: resulta que este señor no comprende un sencillísimo truco del lenguaje en que programa, y sin embargo, le recomienda pasar a un sistema que exige un conocimiento en profundidad de ese mismo lenguaje y de su modelo de objetos.
Claro, que si usted se siente molesto conmigo puede dar ejemplo de solidaridad y comprarse el engendro. Pero recuerde que, cada vez que alguien compra un libro malo, el Diablo aparece y mata un cachorrito.

La verdad es que me extrañó un poco el ejemplo de la comida para perros. Pero luego recordé que son los musulmanes quienes consideran que el perro es un animal impuro. Los hindúes, en realidad... bueno, no recuerdo si los consideran seres divinos o si se los comen.
Hat tip to Alfredo Novoa: se casan con ellos.

Etiquetas: , , , ,

lunes, febrero 09, 2009

Garantías

Dorothy se enfrenta a la familia política del León CobardeLa Programación no es trabajo ni para cobardes ni para desmemoriados. La máquina es una bestia salvaje atada por una cadena: si queremos ganarnos la vida con ella, tenemos que despojarnos del miedo irracional. Pero también tenemos que recordar, en todo momento, cuál es la longitud de la puñetera cadena.
Cuando hablo de "cadena", usted debe entender "contrato": cada pieza de software debe dejar bien claro qué es lo que hace por nosotros, siempre que le proporcionemos lo que especifica el contrato. Math.Sqrt nos calculará la raíz cuadrada de un número, siempre que le proporcionemos un número no negativo. El indexador de la clase genérica Dictionary nos devolverá el valor asociado a una clave, aunque sólo cuando la clave se haya almacenado antes. Ignorar la existencia de estos contratos nos puede llevar a uno de dos problemas:
  1. Pedimos la raíz cuadrada de menos uno, y el león nos come una oreja.
  2. El problema anterior suele pasar una sola vez. El aprendiz de domador suele quedar aterrorizado, y de ahí en adelante no se acerca al león, excepto cuando su abogado está presente (para que se coma al león en caso necesario). El abogado, por desgracia, termina siendo un problema mayor que el propio león.
A continuación, voy a presentarle varias joyas que muestran los problemas que se pueden presentar cuando el programador decide ignorar, por excesivo terror, las garantías que le ofrecen los contratos. El problema con estas "joyas" es que, cuando uno se las encuentra, pierde un tiempo valioso intentando decidir si:
  1. Se trata de una genialidad.
  2. O se trata de una simple cagada.
El 99% de los casos corresponde a la segunda opción, pero uno se niega a perder tan pronto la fe en el género humano.
La primera perla negra tiene que ver, como no podía ser de otro modo, con la ya mencionada clase Dictionary. Estoy harto de ver este fragmento de código en el software de cierta empresa imaginaria:
if (dict.ContainsKey(key))
dict[key] = value;
else
dict.Add(key, value);
Esto podría pasar como buena programación en Ankh-Morpork, pero aquí solemos ser un poco más exigentes. Un buen día, uno de los leones, el método Add, se adelantó al gato y le comió la lengua al programador, cuando éste intentó insertar una clave duplicada en un diccionario. De ese día en adelante, el programador pincha primero el diccionario con una vara larga, para ver si ya existe la clave. En caso afirmativo, actualiza el valor asociado a la clave; de lo contrario, inserta el nuevo par clave/valor. El problema es que el mismo resultado se obtiene con menos charla:
dict[key] = value;
En efecto, cuando se realiza una asignación sobre el indizador un diccionario, si exista la clave, se actualiza el valor; de lo contrario, se inserta. Hay dos posibilidades: o el programador lo sabía, y se le olvidó... o nunca se tomó la molestia de leerse la documentación. Prefiero pensar lo primero. Al fin y al cabo, la memoria viene genéticamente determinada, y uno no debe reñirle al prójimo por sus genes.
Por cierto, y si me permite el paréntesis, ¿sabía usted que podemos inicializar un diccionario genérico como si se tratase de una vulgar colección? ¡Es que un diccionario genérico es una colección! El quid está en saber qué tipo de colección: sus elementos son instancias de la clase genérica KeyValuePar. Esta es la técnica:
private Dictionary<int, string> d =
new Dictionary<int, string>()
{
{1, "Uno"},
{2, "Dos"},
{3, "Tres"},
};
Preste atención a la coma, aparentemente superflua, que sigue al tercer par de valores. Se trata de un sencillo, pero poco conocido, truco sintáctico que permite añadir o quitar pares, en cualquier momento, sin preocuparnos por el carácter especial del último par. Este mismo truco se aplica a los inicializadores de objetos, e incluso a la declaración de tipos enumerativos. Si le parece una barbaridad y una concesión (hay gente para todo), recuerde que en C# el punto y coma es un terminador, no un separador. ¿Qué hay de malo en querer usar la coma también como terminador, en estos casos?
Vamos ahora a otra garantía que solemos olvidar... o simplemente desconocer. Esta vez, la garantía tiene que ver con el propio lenguaje:
double d = 0.0;
if (cond)
d = 1.0;
else
d = 2.0;
Naturalmente, la inicialización de la variable local sobra:
double d;
if (cond)
d = 1.0;
else
d = 2.0;
No pasa nada porque se deje d sin inicializar... aparentemente. La instrucción que sigue lo garantiza, sin importar cuál de sus ramas se ejecute. Por supuesto, en este caso sencillo, habría sido más fácil escribir todo el código de esta manera:
double d = cond ? 1.0 : 2.0;
El programador que escribió el primer fragmento ha olvidado la sección de la especificación de C# que explica el concepto de asignación definida. La ha olvidado, o nunca se ha tomado la molestia de leerla.
Otro disparate frecuente debido al temor a lo desconocido:
private double f()
{
double d = 0.0;
OpenXXX();
try {
if (cond)
d = f1();
else
d = f2();
}
finally {
CloseXXX();
}
return d;
}
En realidad, el código anterior combina varios disparates debido al olvido o desconocimiento de las garantías de los contratos. Para empezar, vuelve a sobrarnos la inicialización de la variable local: la rama try del bloque try/finally nos garantiza la asignación definida de la variable, y la rama finally es irrelevante, si no utilizamos d en su interior. Tenga en cuenta que, si se ejecuta el finally, nunca llegaremos a la instrucción return.
Pero la zarpa que realmente aterra al programador del ejemplo es la posibilidad de ejecutar un return dentro de la rama try, por desconocimiento (u olvido) del funcionamiento de esta instrucción. Podemos ejecutar ese return sin problemas, y el código se simplifica entonces enormemente:
private double f()
{
OpenXXX();
try {
return cond ? f1() : f2();
}
finally {
CloseXXX();
}
}
El código simplificado es más corto, más comprensible y, probablemente, algo más eficiente, y ya no hablemos de la elegancia. ¿Moraleja? Conozca sus deberes y derechos, tal y como los especifican los contratos de su software, y así será mejor programador. O, mejor aún: RTFM (again).

Etiquetas: , ,