lunes, 16 de febrero de 2009

Bertrand Meyer en Argentina

La semana pasada tuvimos el honor de que nos visitara Bertran Meyer. Dio dos charlas en Buenos Aires, una en la UTN, organizado por los Seminarios Athena y otra en la FCEyN de la UBA organizada por Marcelo Frías. 
La primera (dada en la UTN) se trató sobre su punto de vista sobre como enseñar el curso de introducción a programación en estos días. En la misma comentó por qué los métodos más usados no eran de su agrado (programming in the small, teach API, just formal programming, teach a language, just functional programming) y cuales utiliza él. Uno de los temas que recuerdo como interesante era el hecho de hacer que los alumnos empiecen a programar con un framework ya creado para que aprendan a usar primero para luego aprendan a hacer. Me hizo recordar a lo que hacemos nosotros cuando les damos Smalltalk en POO, primero lo empiezan a usar y luego lo tienen que ver en más detalle y hasta modificar en DAO. De los 26 temas que comentaba como importantes para enseñar solo hay 16, que para aquellas personas que conocemos la bibliografía de Meyer no son nada novedosas, ej. Design by Contract, OO, etc. Me parece que lamentablemente el tema no fue el más apropiado para la audiencia porque la mayoría eran estudiantes y no profesores!, además mostró un ejemplo que usan en la primer clase de la materia donde crean la clase Turist que subclasificaba Preview (el framework que usan es para mostrar ciudades y rutas), en la cual el objeto París sabe responder show, Louvre sabe responder highlight, etc. responsabilidades que no le daría a esos objetos, o nombres de objetos que no usaría en todo caso.
La charla en la UBA se trato sobre testing automatizado. Básicamente lo que hacen es generar objetos random con una heurística determinada para algunos tipos de objetos y luego enviar mensajes a dichos objetos (siempre y cuando los mismos cumplan las pre-condiciones) y ver si hay post-condiciones o invariantes que no se cumplan. Si fuese así, se detectó un error. Mostró una estadística en la cual el testing manual encontró 14 errores y el automático 9. Había errores automáticos no encontrados manualmente y viceversa por supuesto. Le pregunté sobre la cobertura que ofrecía esa herramienta y se armó un gran revuelo puesto que dijo que la cobertura no indicaba nada, cosa que concuerdo a medias. Si hay un 20% no cubierto, uno sabe que debe testear ahí. Si tengo un 100% de cobertura por supuesto que no asegura que haya errores puesto depende de con que conjunto de "datos" se hizo la cobertura. En conclusión, no sacaron estadísticas de cobertura. Le pregunté si habían medido el tiempo que llevó encontrar lo errores manuales y los automáticos, dijo que no. Le pregunté si había pensado en utilizar mutation testing para modificar métodos y validar contratos y viceversa, modificar contratos y ver validar código y respondió que no "entendía mutation testing". Esta charla fue más interesante desde mi punto de vista que la anterior. Lástima que a veces hablaba mucho sobre ciertos temas en los que se perdía el núcleo de lo que quería decir.
Por supuesto, no dejó de publicitar su libro que está por salir "Touch of class ..." y Eiffel.

viernes, 13 de febrero de 2009

Igualdad de Objetos II

Algo que me olvidé de comentar en el post sobre Igualdad de Objetos, es que como el mensaje #= debe devolver true si los dos objetos representan el mismo ente su implementación siempre será verificar si aquellos colaboradores internos que representan la identidad del ente en el objetos receptor del mensaje es igual a los colaboradores internos que representan la identidad en el objeto pasado como parámetro. En pocas palabra, asegurar que ambos "identifican" al mismo ente.
En el caso de Date, esto será verificar que el año, mes y día sean iguales (si esa es la representación que se decidió usar para Date).
Una consecuencia interesante en identificar que colaboradores internos representan la identidad del ente es que esto se podría hacer automáticamente lo mismo que una implementación default del mensajes hash, como también asegurar que dichos colaboradores no pueden cambiar.

nil (pongo algo más para que no parezca un error :-)

Cuando escribí el título de este post era solo "nil", pero imaginé que alguien podría pensar que sucedió algún error y que por eso aparece solo "nil" en el título. ¿No les pasó eso miles de veces en los distintos sistemas que hicieron? Yo lo vi mucho, por eso modifiqué el título para no crear esa confusión :-) (por supuesto eso implicaría que Blogger está escrito en Smalltalk, cosa que sabemos que no, pero en fin, me pareció una caso curioso)
Ahora si, lo importante del blog. En las charlas que dio Meyer, comentó que en Eiffel pueden ahora asegurar que nunca se envía un mensaje inválido a nil de manera estática,  que él representó como que "x.f con x en null es detectado estáticamente" (1). Lo que me pareció curioso de ese comentario es que aún existan personas que estén pensando en ese problema cuando la solución es bien sencilla! y es simplemente no usar nil! pero claro, todos lo siguen haciendo por una cuestión histórica y porque la mayoría de los ejemplos que vemos cuando estamos aprendiendo lo hacen. 
Cuando le comenté a Bertrand que nosotros no tenemos el problema del nil (o null para él) puesto que no usamos nil para nada porque tenemos es una regla de programación que nos pide crear todos los objetos correctamente de entrada y representar la ausencia de algún objeto con un null object, me parece que lo sorprendió un poco. Inmediatamente me preguntó "cómo representábamos el fin de una lista encadenada por ejemplo", a lo que respondí que simplemente reificaríamos(2) el concepto de fin de lista y utilizaríamos un objeto especial para tal fin, pero no nil. Mi sensación es que se quedó pensando, por lo menos ahí terminó esta parte de la conversación.
Más allá de esta anécdota reciente con Meyer, les quería transmitir nuestra experiencia respecto de este tema y de que se puede vivir sin el objeto nil y conviene hacerlo por ser la fuente de innumerables dolores de cabeza. No se dan una idea cómo se simplifica la codificación creando objetos correctos de entrada, sincronizando con copias, evitando usar nil... Piensenlo por un minuto y traten de imaginar cuantos errores desaparecerían, cuantos "doesNotUnderstand" o "null pointer exception" dejarían de aparecer si nil o null no existieran... seguramente sientan que son muchos.
Les comento otro detalle anecdótico que me parece al mismo tiempo importante y en sintonía con este consejo. Hoy cuando leía unos blogs me encontré con esta charla que dará Hoare en QCon: "Null References: The Billion Dollar Mistake". Según lo que interpreté de su abstract y título, hablará justamente de que la existencia de null references fue un error gravísimo, y que sólo lo creó en ALGOL W por ser un feature fácil de implementar.... otro feature fácil de implementar que trae dolores de cabeza, y del cual coincido plenamente con Hoare, es realmente un "Billion Dollar Mistake".
(1) Después cuando nos comentó como lo hacían durante la cena, mucho no le entendí pero me pareció muy complejo.
(2) Digo reificaríamos y no reificamos porque nunca tuvimos que crear una lista encadenada en Smalltalk :-)

martes, 10 de febrero de 2009

Igualdad de Objetos

En el post anterior hable sobre el hecho de identificar correctamente la identidad de un objeto y sobre el verdadero significado del mensaje #==, que en definitiva verifica si dos objetos son idénticos, puesto que dos objetos pueden no ser idénticos pero estar representando el mismo ente.
Como bien comenta Gabriel (Gaboto) en un comentario, puede suceder que existan dos objetos representando el mismo ente (su ejemplo me confundió un poco) y debemos tener alguna manera de identificar dicha situación. Un ejemplo claro se da en Smalltalk con la clase Date, donde dos instancias distintas (o sea no idénticas) pueden representar el mismo día (por ejemplo hoy). 
El mensaje que utilizamos para saber si dos objetos están representando el mismo ente es el mensaje #=, más conocido como "igual". Su semántica es devolver verdadero si dos objetos representa el mismo ente, lo que en lenguaje vulgar o natural decimos "si son iguales"... claro está, acá empieza la confusión. Son iguales por estar representando al mismo ente (por ejemplo el día de hoy, que es idéntico a si mismo) pero no son el mismo objeto necesariamente y por no lo tanto no idénticos entre sí.
Por lo tanto el mensaje #= toma una nueva dimensión y para mí es más claro decir que este mensaje "devuelve true si dos objetos representan el mismo ente", puesto que definir qué significa "si son iguales" es bastante problemático como veremos más adelante. Sería menos confuso usar otro mensaje para esta semántica, por ejemplo #representsSameEntityAs:, pero bueno, ya estamos muy acostumbrado al otro.
El motivo por el cual dos objetos pueden representar el mismo ente se debe únicamente a problemas computacionales. En el caso de la clase Date esto se podría solucionar teniendo una cache de fechas creadas y siempre devolver el mismo objeto para la misma fecha, una implementación similar a la de Symbol, sin embargo hacer esto puede no tener sentido por poder ocupar más memoria o ser más lento que la implementación actual al tener que buscar el objeto en esa cache. En definitiva, si existe un solo objeto para representar un ente o si existen varios es una decisión implementativa y es por ello que siempre hay que utilizar el mensaje #= para saber si dos objetos representan el mismo ente (si son iguales) y sólo utilizar el mensaje #== para dominios computaciones, donde realmente nos interese saber si estamos hablando del "mismo objeto", del objeto que ocupa la misma zona de memoria.
Ahora bien, si se decide que puede haber más de un objeto para representar el mismo ente como el caso de Date, hay que asegurar que esos objetos sean inmutables porque sino estaremos en graves problemas puesto que podrían existir representaciones desincronizadas del mismo ente (creo que a esto apuntaba Gaby con su ejemplo). Imagínense dos objetos representando la misma cuenta corriente donde el saldo en uno es distinto al saldo en otro y donde claramente solo uno de ellos representa bien el ente en ese momento por tener el mismo saldo de la cuenta (por supuesto que también podría suceder que ninguno represente el ente por tener los dos el saldo incorrecto...). En definitiva, un grave problema.
Si no es factible que dicho objeto sea inmutable, hay que asegurar que exista una única instancia "master" para representar el ente y el resto sean solo "representaciones temporales" que tendrán un propósito determinado y esporádico. Un ejemplo de este caso es lo que en mi presentación de Smalltalks mostré como "sincronización" de objetos que comentaré en más detalle más adelante, pero que básicamente consiste en sincronizar la copia "master" con la "termporal" de una vez, con el envío de un solo mensaje. Por supuesto que acá nos metemos con problemas transaccionales, pero por ahora también los esquivaré.
Otro tema interesante que se desprende de este es definir exactamente que significa "que sean iguales". Por ejemplo, 0.5 y 1/2 ¿son iguales?. Ambos representan la misma cantidad, la mitad de algo, por lo tanto desde el punto de vista numérico nos interesa que sean iguales, pero también son distintos. Por ejemplo, desde el punto de vista implementativo uno será representado como un número de punto flotante y el otro como fracción. Mismos ejemplos se pueden dar para medidas equivalentes representadas por distintas unidades. Más interesante aún es pensar si 0 es igual a 0 metro, 0 litro, 0 pesos, etc. No voy a ahondar mucho en este tema ahora, pero la verdad es que sí, todos estos objetos representan lo mismo, o sea nada en cantidad y por lo tanto "son iguales". A muchos esta afirmación les parecerá muy loca pero la explicación matemática es clara. Una medida es la representación de la multiplicación de un número por una unidad, por lo tanto 0 metro es lo mismo a decir 0*metro, que algebraicamente es 0 de la misma manera que es 0 la expresión algebraica 0*A. Algunos denominan esta situación como "el cero polimórfico". Ahora bien, todas estas medidas están midiendo cosas distintas y sería interesante poder determinar esa situación, ¿cómo lo hacemos si devuelven true cuando se les envía el mensaje #=? (lo mismo para 1 metro y 1000 milimetros o 100 centímetros, etc). En Mercap estuvimos discutiendo mucho sobre esta situación, discusión en la que también participó Máximo Prieto y que fueron muy interesantes y acalorados (por qué ocultarlo). Llegamos a la conclusión que como en matemática, debería existir la posibilidad de definir distintas categorías de equivalencias, de la cual la igualdad es simplemente una de ellas. Si esto tiene sentido entonces el mensaje #= sería una implementación de una categoría de equivalencia determinada y podrían existir distintos mensajes para implementar las otras... suena interesante no? ¿Cómo se definiría una categoría o cuál sería su significado? Dependerá de lo que queramos representar, es una decisión totalmente arbitraria... aunque quizá suene mejor consensuada, consensuada con una comunidad o grupo...
En fin, si les interesa este tema avisen, da mucho para discutir y charlar.

viernes, 16 de enero de 2009

Tip 2: Clearly identify what represents the identity of an object

En el primer Tip (si ya se, lo escribí hace mucho...) comenté sobre lo interesante que es tener como objetivo lograr un isomorfismo entre los objetos de mi modelo y los entes del dominio del problema.
Además, como ya puse en otros posts, no hay que perder de vista que los objetos representarán la esencia de dichos entes mediante su comportamiento, no estructura, relaciones, etc.
Sin embargo hay otro punto del que siempre hablamos y es la identidad. La identidad es la que permite identificar únivocamente un ente. Todo ente posee una identidad y por lo tanto el objeto que lo representa en nuestro modelo también debe tener una. Sin embargo hay una gran confusión entre lo que es identidad, lo que algunos llaman "estado de un objeto" y lo que es la identidad del objeto. Voy a empezar por este último.
Un objeto, por ser a la vez un ente de la realidad (además de estar representado otro) también posee una identidad. Esa identidad en un ambiente de objetos está generalmente implementada por medio de la posición de memoria que ocupa el objeto. Es lo que hace que ese objeto sea único, no puede haber otro objeto en la misma zona de memoria que otro. 
En Smalltalk, se puede saber si dos objetos ocupan la misma zona de memoria usando el mensaje #== Este mensaje tiene por propósito verificar si dos "objetos" son identicos, o sea, si ocupan la misma zona de memoria pero no puede decir nada sobre la identidad del ente que representan dicho objetos. En rigor de verdad, si dos objetos son idénticos significa que están representando al mismo ente y por lo tanto representan la misma identidad del ente, pero si dos objetos no son identicos no se puede asegurar que estén representando a entes no idénticos. Es acá donde se ve claramente la diferencia entre identidad de objeto e identidad de ente. Un ejemplo lo va a aclarar.
En Smalltalk existe la clase Date, pero si evalúo dos veces la siguiente colaboración "Date today" obtengo dos objetos que no son idénticos pero que sí representan al mismo ente, el día de hoy. Por lo tanto no hay que confundir identidad de objeto que viene dado por el ambiente de objetos de identidad del ente que la tenemos que definir nosotros en nuestro modelo.
¿Cómo representamos la identidad del ente? Dependerá del ente que estemos modelando o la implementación que utilicemos. Por ejemplo, los SmallInteger en Smalltlk nos dan la sensación de ser únicos (o sea, de que hay uno y solo un objeto por cada uno de ellos). A nivel implementativo sabemos que no es así, pero es un claro ejemplo donde la identidad del ente esta asociada a la identidad del objeto. Otro ejemplo son los objetos instancia de Symbol, no puede haber más de un objeto para el mismo símbolo que representan de la realidad. Como vismo, no sucede así con las fechas y con otros objetos como aquellos instancias de Time, Point, etc.
¿Qué es lo que representa entonces la identidad de los entes para estos objetos? Siempre es un conjunto de colaboradores internos (variables de instancia) que cumplen ese propósito. En el caso de Date, dependiendo de la implementación serán el número de día, mes y año. En el caso de Point serán esos objetos que representan la coordenada x e y. 
Una característica interesante que tenemos que cumplir con estos colaboradores que representan la identidad de los objetos es que no pueden cambiar, puesto que si lo hacen el mismo objeto estaría en un instante de tiempo representando un ente y luego otro, sería un objeto con "problema de personalidad" no les parece?. Es por ello que es fundamental entender bien qué colaboradores internos representan la identidad de un objeto puesto que ellos no pueden cambiar. Hay otras implicancias interesantes de este hecho, la primera es que el mensaje #= del objeto devolverá true si los colaboradores internos que representan la identidad del ente son iguales. Esto significa que si queremos saber si dos objetos representan el mismo ente, debemos usar el mensaje #=, es este mensaje el que nos dirá si dos objetos representan a un ente idéntico o en otras palabras, a exactamente el mismo ente. Moraleja, no confundir identidad de objeto de identidad de ente y por lo tanto nunca, pero nunca usar el mensaje #== en un modelo a menos que ese modelo esté tratando con un dominio de problema computacional y donde realmente se tenga por intención saber si se está tratando a exactamente "el mismo objeto" sin importar si además si representan exactamente el mismo ente.
Esto que estoy comentando les debería disparar una nueva explicación de porque el hash debe estar relacionado con el #= y por qué cuando un objeto cambia los colaboradores que representan la identidad cambia su hash que termina produciendo un descalabro importante en aquellos objetos que dependen de la invariabilidad de esta característica.
También esto tiene que empezar a dispararnos la idea de que hay colaboradores internos que representan la identidad del ente que modela el objeto y otros que no. ¿qué características pueden tener estos otros? ¿pueden cambiar? ¿que representan?. Son muchas preguntas, no tengo una respuesta 100% segura, pero en la mayoría de los casos me parece que dichos colaboradores existirán por motivos implementativos como por ejemplo tener un cache, poder acceder más rápido a un objeto, etc. y no a cuestiones esenciales del dominio de problema en si, pero no lo puedo asegurar.
Otra pregunta interesante para hacerse es, ¿debería el lenguaje ofrecerme herramienta particulares para hacer uso de esta diferencia? Por ejemplo, que defina una clase de esta manera:
Object subclass: #Point
   identityDefinedBy: 'x y'
   instanceVariableNames: ''
   ... etc.
Al hacerlo, automáticamente el ambiente podría asegurar que la identidad no puede cambiar, y algo más interesante aún que la misma se defina en el momento de crear el objeto, algo que veremos en el próximo tip.... mientras tanto tiro la idea, yo aún no tengo un posición tomada.

Desarrollo ágil y vacaciones

Este fin de año terminé destruido, me dí cuenta que durante el 2008 hice muchas cosas e indudablemente sentí el impacto. Por suerte mañana empiezo mis vacaciones y esta vez me voy a tomar 3 semanas seguidas, siento que realmente las necesito. Espero poder desconectarme lo suficiente para volver con ganas para este año, aunque voy a limitar un poco lo que hago porque no quiero terminar otra vez así. Uno de los temas que creo que voy a limitar es la cantidad de tesis que estoy dirigiendo, aunque no lleve mucho tiempo hacerlo el problema es tener que estar pensando en todas ellas simultáneamente más todo el resto de cosas que tengo que hacer... 
Pero como en ciertas cosas no puedo con mi genio y me enteré de este seminario ágil, por ahí presente algo relacionado a la importancia que tiene para mi trabajar en un lenguaje dinámico si realmente se quiere se "ágil". Todo esto me vino a la mente después de haber visto ayer en Mercap Cinema (una vez por semana nos juntamos en el almuerzo a ver videos técnicos) la entrevista en la que estuvieron Dan Ingall y Allen Wirfs-Brook, en donde Dan hace hincapié y repite miles de veces la idea de "self sustainable system" o "meta circular system" y Allen hace una comparación entre Internet y la imagen de Smalltalk basada en la idea que no hay que bajar internet para cambiar una página de la misma manera que no hay que bajar la imagen de Smalltalk para cambiar un método. 
En fin, todo esto me hizo pensar lo poco que se entiende aún este espíritu dinámico que posee Smalltalk y que no tiene nada que ver con ser "dinámicamente tipado", sino con el hecho de tener un imagen y poder modificar un sistema mientras corre, donde ese sistema puede ser hasta sí mismo! (que es lo que hace incapíe Dan). Creo que la gente está empezando a ver la importancia de los lenguajes dinámicamente tipados y por lo tanto estaría bueno mostrarles un poco más allá y ver que opinan. 
En XTrade, donde utilizamos GemStone como server de objetos, es muy común que hagamos cambios sin bajar el sistema, simplemente modificando por medio de un script de topaz (un cliente de texto de GemStone) lo necesario para arreglar un error. Lamentablemente no tuvimos tiempo aún de implementar la modificación del cliente del sistema, que está escrito en VisualAge,  sin tener que cambiarlo completamente, o sea, bajando el cliente y volviéndolo a subir reemplazando toda la imagen, pero sabemos cómo podríamos hacerlo. En una aplicación Web esta característica es aún más interesante.
En fin, ¿qué les parece? ¿sería interesante una charla así en ese contexto? El título que pensé sería algo así: "No se puede ser ágil sin ser dinámico o que nos falta para ser técnicamente ágiles"

jueves, 11 de diciembre de 2008

Viernes 19/12 , 16:30 hrs: Defensa Tesis de Reingeniería de Jerarquías Polimórficas Utilizando Traits

Nuevamente estoy con poco tiempo para postear... tengo pendiente seguir con los tips que presente en Smalltalks 2008, pero por ahora copio esta invitación a la defensa de tesis de Claudio y Nicolás que además dieron una presentación muy buena en Smalltalks 2008 sobre este tema...
----------------
Invitamos a todos a la defensa de la siguiente tesis de licenciatura, que
se llevará a cabo el Viernes 19 de Diciembre a las 16:30 hrs, aula a
confirmar:

Título: "Reingeniería de Jerarquías Polimórficas Utilizando Traits"

Alumnos: Acciaresi, Claudio y Buttarelli, Nicolás Martín
Director: Hernán Wilkinson
Jurados: Máximo Prieto, Gabriela Arévalo
--------------------------------------------------------------------
Resumen
Los Traits son una nueva herramienta de modelado que permite al
programador compartir comportamiento entre objetos sin utilizar la
subclasificación. Este nuevo concepto fue llevado a Smalltalk en el año
2003 y desde entonces a la actualidad se realizaron varios trabajos
utilizando Traits. Sin embargo, la mayoría de estos aborda el tema desde
un punto de vista más bien práctico realizando refactorizaciones de
jerarquías ya implementadas. Este trabajo en cambio, estudia a los Traits
en un marco teórico donde se considera que programar no es otra cosa que
representar conocimiento.
Para cumplir con este objetivo se reimplementó la jerarquía de Collection
utilizando Traits.  Una vez generado el nuevo modelo se realizó un
análisis del mismo en comparación con la jerarquía implementada en el
ambiente de desarrollo elegido: Squeak. Dicho análisis se basó en la
definición e implementación de métricas de calidad y de performance
tomadas sobre ambos modelos.
Durante el desarrollo se encontraron un conjunto de ventajas y desventajas
en el uso de esta nueva herramienta, pero además, se redefinieron los
Traits como la reificación de características comunes entre objetos. De
esta manera, se separa el concepto de Traits con el de eliminar código
duplicado, y se analiza si el hecho de reificar características comunes
entre los objetos del dominio nos ayuda a crear mejores modelos
computacionales de la realidad.