viernes, mayo 27, 2011

Weaving en AspectJ (I)

Hace un tiempo escribí una serie de entradas relacionadas con la orientación a aspectos en general y AspectJ en particular (podéis encontrar dichas entradas aquí) que posteriormente agrupé en un documento.

Hasta este momento nos hemos centrado en la teoría de la orientación a aspectos, cómo escribir los mismos y algunas de sus posibles aplicaciones. Sin embargo, no hemos prestado especial atención al modo en el que los aspectos se combinan con las clases para componer nuestro sistema final. Durante esta entrada y las siguientes profundizaremos en diferentes aspectos del proceso de tejido (weaving).

El mecanismo de tejido es aquel que nos permite combinar la definición de nuestras clases y de nuestros aspectos en un ente ejecutable que conforma nuestro sistema final.

Podemos clasificar los diferentes modelos en base al momento en el que llevan a cabo el tejido. En este caso tendríamos:

  • Build time. Las clases y los aspectos son combinados durante el proceso de construcción de nuestra aplicación.
  • Load time (LTW). En este caso el tejido se realiza en el momento en el que una clase se carga en la máquina virtual, por lo que en este caso no será necesario llevar a cabo ningún proceso adicional en nuestro proceso de construcción. 
Otra posible clasificación podría establecerse en función del los tipos de entrada del proceso de weaving
  • Source code weaving. La entrada al proceso son archivos de código fuente (clases y aspectos)
  • Weaving binario (bytecode). La entrada al proceso es el bytecode obtenido como resultado del proceso de compilación de nuestras clases
De manera esquemática, a continuación se reflejan las diferente combinaciones ofrecidas por el weaver de AspectJ:

  • Tiempo de construcción: las entradas admitidas son tanto código fuente como clases compiladas (bytecode)
  • Tiempo de carga: las entradas pueden ser bytecode o un archivo xml (lo veremos más adelante)

Con esta pequeña entrada nos hemos aproximado de manera sencilla a los procesos de tejido presentes en AspectJ de una manera teórica. En futuras entradas analizaremos con un poco más de detalle los contenidos aquí presentados, haciendo especial hincapié en los mecanismos de tejido en tiempo de carga.

Hasta pronto!

sábado, mayo 21, 2011

Libros: últimas y futuras lecturas (II)

Esta es la primera entrada que escribo desde mi reciente adquisición: un IMac 27''. Lo siento, tenía que decirlo :). A ver si me acostumbro a este, para mi, nuevo sistema operativo y le comienzo a sacar partido.

En la entrada anterior hablaba sobre algunas de las últimas y/o futuras lecturas realizadas durante esta última temporada. Puesto que no quería convertir dicha entrada en un ladrillo, se me quedaron algunos libros en el tintero que analizaremos a continuación.


Este libro llego a mis manos gracias a un profesor visitante de la UPM que estuvo impartiendo un seminario sobre wait free computing. Siento no recordar ahora su nombre (vaya cabeza que tengo).

Los profesores Maurice Herlihy, uno de los precursores de la memoria transaccional,  y Nir Shavit nos ofrecen una visión novedosa de los nuevos paradigmas de programación para arquitecturas multinúcleo.

Los primeros capítulos son más teóricos, dejando la parte más divertida para la segunda mitad del libro. No he tenido ocasión de leérmelo de principio a fin pero si que me he leído capítulos independientes y he de reconocer que me han parecido mucho mejor de lo que esperaba. Espero sacar un poco de tiempo libre para poder leerlo al completo porque todo apunta que se puede convertir en uno de mis libros favoritos.

Desde que programo en Scala he aprendido muchas cosas nuevas, ya no sólo del lenguaje, sino de la programación funcional en general.

Siempre he querido dar mis primeros pasos en Haskell pero nunca he tenido tiempo y, sinceramente, me parecía un poco complicado. Tras el inicio de mi andadura en Scala creo que lo veo con otros ojos así que, en la medida de lo posible, voy a intentar dar mis primeros pasos en este lenguaje. 

Todas las referencias que tengo de este libro son muy buenas pero ni siquiera lo tengo así que, cuando me decida a dar el paso, creo que será mi próxima adquisición.

Mi fascinación por la máquina virtual java crece por momentos (creo que ya quedó patente en la entrada anterior).

Clojure es un lenguaje dinámico (dialecto de Lisp) que se ejecuta sobre la JVM (compila directamente a bytecode). Destaca, y esto es lo que más me gusta, por su eficiente infraestructura para la programación multihilo.

Básicamente es un lenguaje puramente funcional que destaca por su amplio abanico de estructuras de datos inmutables y persistentes. En aquellos casos en los que el cambio es algo necesario, Clojure incluye en el propio lenguaje un sistema de memoria transaccional.


De nuevo se me quedan en el tintero muchos otros libros pero, de nuevo, no quiero que la entrada sea demasiado larga. En futuras entradas apostaré por comentar de uno en uno los libros ya leídos, intentando profundizar un poquito más de manera que si alguien estaba dudando si leérselo o no, puede tener una opinión más al respecto.

Hasta pronto! 

sábado, mayo 14, 2011

Libros: últimas y futuras lecturas (I)

Durante estos últimos meses mi tiempo libre se ha convertido en un bien escaso (prácticamente nulo) por lo que muchas de las cosas que me gusta hacer las he tenido que ir aplazando hasta encontrar un mejor momento. Una de ellas es actualizar el blog con una frecuencia un poco mayor a la habitual, y aprovechando que estoy en Esslingen (Alemania), que todo el mundo está durmiendo y que llevo rondando por ahí desde las seis y veinte de la maniana he decidido pasarme un rato por aquí :).

En esta ocasión me gustaría compartir con vosotros algunos de los últimos libros que me he leído, estoy leyendo, o tengo intención de leer.

Me he leído la primera edición y ahora estoy terminando la segunda. Creo que se trata de una referencia excelente tanto para aquellas personas que desean comenzar su andadura en Scala así como para desarrolladores más experimentados con conocimientos del lenguaje.

Si nunca habéis programado en Scala os recomiendo que le echéis un vistazo, os aseguro que no os defraudará. En este blog podréis encontrar una pequenia guía de introducción (en espaniol) así como numerosas entradas relacionadas con diferentes aspectos del lenguaje (espero que en un futuro este tipo de entradas puedan ir incrementando, tanto en calidad como en cantidad)

Una de mis últimas adquisiciones. Está en Beta pero ya me lo he leído. Necesitará una lectura completa de nuevo, aunque en esta ocasión tendrá que ser delante de una pantalla de manera que pueda "ensuciarme" :) las manos.

En este libro se tratan algunas de las técnicas/plataformas más novedosas de programación concurrente sobre la máquina virtual java. Nuevos mecanismos de concurrencia en las últimas versiones de Java, análisis de las bondades de Clojure para la programación concurrente, memoria transaccional, modelos de actores, Akka (hablaremos de Akka en futuras entradas), Scala, GPars.

Una lectura recomendable para aquellos que quieran sacarle partido a los procesadores multinúcleo o que quieran actualizar sus conocimientos sobre programación concurrente.

Qué son DSL? Para que se utilizan? Cómo escribo un DSL? En este libro , Debasish Ghosh desgrana hasta el último detalle de un temática tan popular en la actualidad como los lenguajes de dominio específicos. Una visión pragmática sobre la creación y utilización de lenguajes de dominio específicos ante la que ninguno se quedará indiferente. 

A lo largo del libro se desarrollan numerosos ejemplos (complejos en muchas ocasiones) de diversa índole. Scala, Groovy, Clojure, Ruby y Java son los lenguajes utilizados en el desarrollo de los ejemplos por lo que, este libro, además de la temática principal, puede servir como una primera toma de contacto con alguno de los lenguajes de la máquina virtual Java que están adquiriendo una notable popularidad en estos días.

En este caso de trata de una recomendación de Matín Pérez a la que hacía tiempo que tenía ganas de meterle mano. No tengo una opinión forjada porque sólo llevo leídos dos capítulos pero la verdad que hasta el momento me está gustando mucho, tanto por la temática como por la manera que está escrito.

Recientemente he tenido la suerte de hacer algunas "cosillas" con la JVM pero por lo que he podido ver hasta el momento, este libro adquiere un nivel de detalle notable.

Prometo un post independiente cuando lo termine por completo pero me quedo con la idea de que este libro puede ayudar a escribir mejor código.

No quiero convertir esta entrada en un ladrillo (si no lo he hecho ya :) ) así que vamos a detenernos aquí. Continuaremos en la siguiente entrada con alguno que se ma quedado en el tintero.

Hasta pronto!!

PD: Espero que no haya demasiadas faltas de ortogafía; me está costando escribir desde este teclado alemán.

sábado, mayo 07, 2011

Parser Combinators: Parsing simple bytecode

Anteriormente, http://miguelinlas3.blogspot.com/2011/03/parser-combinators-un-poco-de-teoria.html, introducíamos de manera somera el concepto de parser combinators y parte de la teoría relacionada con los mismos. Durante esta entrada vamos a construir un sencillo parser que nos permita analizar un conjunto mínimo de instrucciones de la máquina virtual java (simplemente permitiremos la definición de funciones y la capacidad de ejecución de las mismas).

No analizaremos en detalle el funcionamiento de la máquina virtual java ni el conjunto de instrucciones  de la misma (podríamos llevarnos algo más que unos cuantos posts :) ) dado que el objetivo principal de esta entrada es profundizar en el concepto de parser combinators y presentar un ejemplo de uso real de los mismos. Pongámonos manos a la obra.

El código fuente que nos servirá como conductor de la entrada será el de la función factorial mostrado a continuación:

static int fact(int);
Code:
Stack=2, Locals=3, Args_size=1
0: iconst_1
1: istore_1
2: iconst_1
3: istore_2
4: iload_2
5: iload_0
6: if_icmpgt 19
9: iload_1
10: iload_2
11: imul
12: istore_1
13: iinc 2, 1
16: goto 4
19: iload_1
20: ireturn

Podréis objener el código desensamblado como el mostrado anteriormente mediante el comando javap -v Class donde Class represente un archivo bytecode.

El proceso de parsing se lleva a cabo mediante el uso de parser combinators (no perdamos de vista el objetivo de nuestra entrada ;) ) y presenta las siguientes características

  1. Para cada una de las diferentes tipos de instrucciones soportadas definimos un parser combinator que es el encargado de parsear este tipo de instrucciones.
  2. Cada uno de los parsers anteriores sabe cómo generar un objeto del tipo correspondiente. Se utiliza una jerarquía de tipos en la que cada una de las instrucciones bytecode soportadas está representada mediante una clase Scala.
  3. El parser global construye un AST (en este caso es un simple lista de objetos) y un conjunto de infraestructura adicional con el objetivo de implementar un intérprete que ejecute la secuencia de instrucciones obtenida (fuera del ámbito de esta entrada podéis echarle un vistazo al código fuente)
¿Cómo actúan nuestros diferentes parsers? Analicemos por ejemplo el componente encargado de parsear las instrucciones ISTORE

lazy val beginInstructionFragment: Parser[Int] = integer <~ ":" ^^ { _.toInt}

lazy val iStoreBytecode: Parser[List[BytecodeInstruction]] = beginInstructionFragment ~> "istore_" ~ integer ^^ {
    case name ~ value => new IStoreInstruction(name,value)  :: List()
  }

El parser beginInstructionFragment es el encargado de parsear la primera parte de la instrucción, la que corresponde al lugar que ocupa la instrucción en el array de instrucciones. 

El segundo parser hace uso del primero y además sabe cómo parsear el texto restante de manera adecuada. En este caso nuestro este analizador es capaz de parsear las instrucciones istore_N donde N representa un número entero positivo cualquiera y devolver un objeto (dentro de una lista) de tipo IStoreInstruction en el que se incluyen el nombre de la instrucción y el entero correspondiente.

Como seguramente a muchos de los lectores de este post el fragmento de código anterior les suene un poco raro intentaremos analizar las diferentes opciones de combinaciones de parsers disponibles:
  • Si escribimos un parser en el que utilizamos una cadena, como por ejemplo "istore", el resultado es la propia cadena.
  • Podemos utilizar expresiones regulares, como por ejemplo, "[A-Z]".r . En este caso el resultado también sería la propia cadena
  • Si utilizamos el símbolo ~ (composición secuencial) el resultado del proceso de parseo será la combinación de ambos resultados. Por tanto, si A devuelve "X" y B devuelve "true", el resultado de A~B sería  ~("X","true") (el tipo de este resultado sería una case class de tipo ~ . Adicionalmente podemos hacer uso de los operadores <~ y ~> los cuales mantienen los valores a la izquierda y a la derecha respectivamente.
  • Otro elemento de combinación de parsers sería | . En este caso el valor de retorno sería el de aquel parser que pudiera parsear la entrada con éxito.
  • rep(P) o rep(P,separator) retornan una lista de las múltiples ejecuciones de P.
  • opt(P) retorna una instancia de tipo Option.
Adicionalmente a la combinación de los parsers, necesitaremos un mecanismo que nos permita reescribir la salida de los mismos, en nuestro caso para instanciar los objetos de tipo necesario y configurar nuestro AST. Para ello tendremos que hacer uso del operador de transformación ^^.

Una definición formar del operador anterior sería: Dado un parser P y una funcion f cualesquiera, una expresión del tipo P^^f implica que para cada resultado R de P el resultado de la expresión global es f(R) 

En el ejemplo que estamos tratando nuestra función es un simple pattern matching que transforma la case class retornada por nuestra combinación de parsers en un objeto de tipo IStoreInstruction.

No me ha resultado sencillo ponerlo por escrito y tampoco estoy seguro de que me haya explicado con la mayor claridad posible. Si estáis interesados os recomiendo que clonéis el repositorio alojado en GitHub

git://github.com/migue/parser-combinators.git

Hasta pronto!