Elucubrando

Enero 28, 2008

La inferencia de tipos es tu amiga

Archivado en: — rodrigo @ 6:18 pm

Supongan que quieren hacer una estructura de datos para guardar una asignación de roles a permisos. Como lo van a usar para visualizar y editar, no basta con asociar a cada rol un conjunto de permisos, sino que necesitan asignar un booleano a cada par (rol, permiso) (Así, el código de visualización no tiene más que dibujar la matriz).

Como no estamos escribiendo C, lo que vamos a hacer es usar un Map (o hash table, o arreglo asociativo, o como le llamen en su lenguaje favorito) anidado. Vamos a tener un arreglo rolPermiso indexado por roles. La entrada asociada a cada rol es a su vez otro arreglo. Este segundo está indexado por permisos, y cada entrada es un booleano que nos dice si el rol tiene este permiso. ¿Limpio, no?

Dicho en perl, es algo parecido a

 $rolPermiso{$rol}{$permiso}

Muy bonito. Pero propenso a errores, por que tengo que tener mucho cuidado que $rol y $permiso tengan siempre algo válido. Si no, perl alegremente creará nuevas entradas en el arreglo con lo que sea que tengan esas variables.

De ese problema quien nos salva es el tipado estático. Si le avisamos al compilador que nuestro arreglo debe estar indexado por Roles, que el contenido deben ser arreglos de Booleanos indexados por Permisos, y creamos un par de enumeraciones para los Roles y los Permisos, el compilador se encarga de avisarnos «¡Oye, “Adninistrador” no es un rol válido!»

Claro que, para eso, necesitamos un sistema de tipos capaz de expresar ese párrafo. Y que al mismo tiempo no nos obligue a tonterías como el sistema de tipos de Pascal, que consideraba tipos diferentes a los arreglos de 4 enteros y a los de 5, y nos obligaba a escribir una función para ordenar arreglos de 4 enteros y otra para los de 5.

La solución, por supuesto, es el uso de tipos polimórficos (paramétricos, les llaman algunos). En Java 5 las funciones que toman arreglos asociativos genericos usan el tipo Map<K, V> en dónde K y V son variables de tipo, que expresan que a dichas funciones no les importa qué hay en el arreglo, sino sólo que la estructura es de arreglo.

La declaración de la variable que nos importa es entonces

 Map<Rol, Map<Permiso, Boolean>>

Hasta aquí, todo bonito. El problema, por supuesto, es en cuanto queremos inicializar dicha variable, usando una implementación específica de la interfaz Map y un wrapper que inicialize los valores de forma automática en el primer acceso. Como es necesario en cada punto decirle al compilador los tipos de todas las variables en cuestión la inicialización termina siendo el siguiente mounstruo:

    public Map<Rol, Map<Permiso, Boolean>> rolPermiso =
            new DefaultValueMap<Rol, Map<Permiso, Boolean>>(
                    new HashMap<Rol, Map<Permiso, Boolean>>(),
                    new DefaultValueMap.DefaultCreator<Rol, Map<Permiso, Boolean>>() {
                        public Map<Permiso, Boolean> create(Rol rol) {
                            EnumMap<Permiso, Boolean> tmp =
                                new EnumMap<Permiso, Boolean>(Permiso.class);
                            for (Permiso p : Permiso.values())
                                tmp.put(p, rol.tienePermiso(p));
                            return tmp;
                        }
                    });

(Con el problema exacerbado por que Java no tiene funciones de primer orden, así que el inicializador default tiene que quedar envuelto en un objeto de una clase anónima cuyo único propósito es pasar a su único método de un lado a otro. Fuchi.)

Una vez que se les pase el dolor de cabeza de tratar de leer eso, fijense que más o menos la mitad de esa inicialización consiste en reiterarle los tipos al compilador. Lo interesante es que el compilador sabe cuales deberían ser esos tipos (por que si los ponen mal, emite un error). Lenguajes como Haskell y ML se aprovechan de eso (y de un poco más de cosas, claro) y proporcionan inferencia de tipos. Basta con que le digan al compilador algunos de los tipos y él mismo averiguará cuales son todos los demás.

En nuestro caso, en Haskell bastaría con decir que
rolPermiso :: Map(Rol, Map(Permiso, Bool)

y luego seguirse usandolo casi igual que como lo haríamos en perl. El compilador inferirá entonces que si le pido

rolPermiso rol

“rol” debe ser una variable de tipo “Rol”, y si algúna parte del código la usé con un tipo incompatible (si le intenté asignar “Adninistrador”, por ejemplo) protestará ruidosamente por la inconsistencia, en lugar de fallar de formas misteriosas en algúna otra parte del código.

Claro que todo esto no es mucho consuelo cuando se ven obligados a usar Java, pero bueno.

Enero 22, 2008

Libros

Archivado en: — rodrigo @ 8:57 am

Sales un día de tu casa y empiezas a caminar al azar. Avanzas con decisión por la acera, llegas a la esquina, tomas alguna de las calles que se cruzan ahí. Quizá incluso regresas por donde venías. Persistes en esto el suficiente tiempo, sabes que las leyes que rigen al mundo casi garantizan que vas a llegar, tarde que temprano, a una librería de viejo. Una de esas con desordenados pasillos estrechos, pequeñas puertas de media altura entre algunas estanterías y un dependiente a la entrada que toma nota de todos los presentes sin dejar de leer. Entras.

Revuelto entre libros de texto resueltos y viejas novelas rosas en algún idioma que no sabes leer hay un libro ahí esperandote. A veces es el tomo dos de aquel recetario que nunca completaste. O una edición bien conservada del famoso libro con el dragón en la cubierta, ese que es obra seminal y que todos en tu profesión deben leer. Esta vez es un libro de fotos. De edificios y casas. Lo ojeas, lentamente, reconociendo a veces los estilos, maravillándote de no haber visto nunca otros. Das un par de pasos y te sientas con él en la escalerita, la de alcanzar los estantes de arriba, a seguir hojeando.

Y entonces, en la exploración detenida del libro, en esa fase de oler las cubiertas y revisar el colofón y buscar notas apresuradas en los márgenes, te encuentras con la dedicatoria. Con bonita y regular caligrafía, de esa de rotular planos, en la página en blanco después del título: “Para mi amigo Barragán.” Te pierdes un rato pensando en los extraños caminos que tu libro (sí, ya es tuyo, aunque no lo hayas pagado) tuvo que haber recorrido para llegar al mismo estante que tú, y no un día antes o después, sino hoy. ¿Estaría metido meses en alguna caja de cartón de una mudanza? ¿En la lista de bienes a repartir como parte de un legado en disputa? ¿Olvidado detras de un librero, dónde cayo tras una fiesta particularmente ruidosa?

Y sales entonces, abrazando tu nuevo libro y pensando que tal lista de concidencias seguramente debe ser material suficiente con qué inspirar un cuento en el que expliques como fue que te topaste con él.

Gestionado con WordPress