Publicidad:
La Coctelera

Procesamiento del lenguaje natural para Abredatos 2010

El fin de semana del 17 y 18 de Abril de 2010 tuvo lugar una de las primeras iniciativas españolas para promover el Open Government en España, el desafío Abredatos 2010.

Tuve el placer de participar en este desafío junto a  mis buenos amigos Javi Santana, Edu Lanchares y Félix López. Como buenos hijos de la diáspora castellano-leonesa por el país intentando buscarse la vida (por lo menos en mi caso) nos apetecía hacer algo cercano a nuestra tierra, así fue como decidimos trabajar con los datos ofrecidos las cortes de Castilla y León.

El problema de estos datos, como el de otros muchos datos públicos, es que la posibilidad de que alguien tenga la intención de reutilizarlos (esa visión de los sistemas de la administración como una plataforma abierta) es, en el mejor de los casos, remota.

De esta manera, nos encontramos con una buena cantidad de datos, no estructurados o semiestructurados, situados en un remoto sitio web y con un formato propietario, más por obligación legislativa, que por una verdadera intención de servicio público.

Procesar estos datos y transformarlos en información es un problema en absoluto trivial.

No existen soluciones algorítmicas que funcionen. La forma más frecuente de afrontar esta dificultad entre los equipos del desafío, así como en los últimos años a raíz de la explosión de la web, consiste en trasladar el problema de las máquinas a las personas, transformando un problema clásico de inteligencia artificial, el procesamiento de texto en lenguaje natural, en un problema de inteligencia colectiva: que sea la multitud de usuarios que visitan esa información los que la filtren.

Nosotros sin embargo, con un claro gusto por lo retro (que podéis esperar de un amante de Lisp), decidimos darle una nueva oportunidad a las técnicas clásicas de inteligencia artificial. El desprestigio de la inteligencia artificial después del conocido como AI Winter es tal, que ahora mismo, nadie que se dedique a la AI puede decirlo si quiere conseguir financiación, así que se utilizan todo tipo de eufemismos para las diferentes parcelas del campo: web semántica, minería de datos, machine learning o procesamiento del lenguaje natural (NLP) que es el que nos interesa.

La idea era utilzar algún tipo de técnica que, dado un volumen de datos textuales fuese capaz de extraer el significado de esos datos y mostrarlos con alguna visualización atractiva en la web.

Afortunadamente, numerosos hombres con barba se han enfrentado a este problema desde los 60 y existen herramientas que nos permiten hacer cosas asombrosas con el texto sin mancharse las manos con matemáticas. Entre ellas está la herramienta que elegimos: GATE un proyecto Java de código abierto que permite construir de forma sencilla sistemas para procesar lenguaje natural. (El proyecto UIMA de Apache también es interesante).

GATE se compone de diferentes componentes que se deben conectar unos con otros en un flujo y que van realizando transformaciones en el texto de entrada.

Nuestro flujo se componía de los siguientes componentes:

- SentenceSplitter: un componente que dado texto de entrada, es capaz de descomponerlo en frases
- DefaultTokeniser: un componente que es capaz de dividir las frases en palabras, teniendo en cuenta guiones y otros accidentes léxicos
- Gazetter: un componente que dado un diccionario de tokens y anotaciones anota todos los tokens producidos por el tokeniser. Utilizamos este componente para marcar palabras sin significado semantico, conocidas como stop words en la jerga del gremio, por ejemplo, preoposiciones, artículos etc.
- TreeTagger: un componente que es capaz de realizar el análisis gramátical de los token, anotándolos con su categoría: verbos, sustantivos, adjetivos, artículos, etc.
- Stemmer: el último componente que dado tokens anotados por el tagger, es capaz de reducir la palabra a su raíz o lexema y forma base o lema. Por ejemplo, para la palabra amaría, la raíz sería am, y la forma base, el infinitvo amar

Al final de este flujo, hemos transformado un montón de texto opaco en una secuencia de palabras con información gramatical. WIN.

El siguiente paso fue agrupar estas palabras con su frecuencia de aparición para pasar a deducir su relevancia. Con un poquito de código Java llevamos a cabo esta labor, aunque tomando algunas decisiones importantes:

- Sólo tendríamos en cuenta sustantivos, adjetivos y verbos que no fuesen stop words
- No contaríamos ocurrencias por su aparición literal, sino que contaríamos como éxito dos palabras con el mismo stem y la misma categoría gramatical.

La siguiente dificultad fue de carácter más práctico, cómo procesar todo el volumen de información a través del sistema construido. Teniendo en cuenta nuestra asombrosa capacidad de cómputo (1 portatil) tuvimos que descartar las herramientas más idóneas y arreglarnoslas poniendo el sistema de procesado en un tomcat con un sencillo servicio web, un script ruby para hacer el scrapping y MongoDB como almacén temporal de datos. Tras conseguir que Java no se comiese toda la memoria disponible por fugas de todo tipo, tuvimos un sistema capaz de procesar una sesión de las cortes en aproximadamente 20 minutos. No es el mejor código que hemos escrito en nuestra vida, pero sirvió para bordear del problema.

Una nueva dificultad apareció debido a las numerosas transformaciones de formato a las que sometimos a los datos: HTML, YAML, documento MongoDB, JSON, tabla SQL, objeto Ruby, pero el verdadero problema surgió cuando visualizamos finalmente los resultados y encontramos que las palabras mas frecuentes eran... totalmente irrelevantes.

Rápidamente nos dimos cuenta, que la relevancia de una palabra desaparecía si por ejemplo se encontraba en todos los documentos indexados (e.g. gracias) o si aparecía con frecuencia muy baja. Con esta idea construimos un algoritmo con dos criterios:

- si una palabra aprecía, en más de N documentos era marcada como irrelevante
- si una palabra aperecía con una frecuencia menor de P era marcada como irrelevante
- El resto de palabras eran marcadas como relevantes.

Jugando con los dos parámetros ajustamos las palabras que se visualizaron finalmente.

Si queréis comprobar la diferencia entre aplicar el algoritmo o no, podéis consumir la API de cortesabiertas.org y comprobar la diferencia entre pedir todas las palabras del corpus:

curl http://cortesabiertas.org/api/words

O pedir las palabras filtradas:

curl http://cortesabiertas.org/api/words?relevant=true

El resultado ya lo conocéis. No es una maravilla, pero con los recursos que teníamos y nuestras carencias estamos bastante contentos. Hay muchas ideas que no nos dio tiempo a implementar, la más interesantes surgen del hecho de que dado el corpus global de palabras, y un objeto: sesión, intervención o procurador, se puede asignar un vector binario o numérico a ese objeto, en función de si dijo esas palabra o cuantas veces la dijo. Esto abre la puerta a utilizar numerosas técnicas de minería de datos sobre los objetos, clasificación o clusterización. Por ejemplo, clusterizar los procuradores por su léxico y ver si coinciden con sus agrupaciones políticas.

En cualquier caso, creemos que sería una herramienta interesante para este tipo de proyectos, y nos gustaría arreglar un poco el código y empaquetarlo de forma que fuese fácilmente usable para otras ideas. Mientras tanto podéis echarle un vistazo al código en github (¡mejoradlo por favo!r)

Parsing SPARQL in Erlang with a monadic combinatory parser

When I had to learn Haskell, some years ago, the task was frustrating for some time. I had the impression of not being able to put all the pieces together, of missing the big picture. Of course, I had to learn the syntax of the language, lazy evaluation, currying and partial application of functions, monads, do notation, arrows, etc. All of those parts seemed to be powerful tools, nice building blocks to build something, but what I was missing was the way of assembling all these components together in order to build something useful in an elegant way.

After a few weeks of struggle I eventually had my "aha!! moment", the moment I realized how powerful and elegant Haskell functional code was. That moment for me happened using the Parsec (http://www.haskell.org/haskellwiki/Parsec) library to build a parser.

Parsec is a monadic parser combinatory library for the Haskell language. It can be used to build parsers for context-sensitive infinite look-ahead grammars in a very different way we are used to with tools like YACC or events based parsers.

With Parsec, the grammar of the language to parse is not defined in an external file using some kind of BNF notation, but in plain Haskell code. Each rule of the grammar can be defined as a parser function of a monadic data type, accepting a state and a type of tokens. The function will do the parsing consuming tokens and returning either a successful result and the not consumed input or an error. The simplest parser you can build, as stated in the Parsec documentation, is the char parser. It is already defined in the Parsec module and just consumes a character:

simple :: Parser Char
 simple  = letter
 

Others Parsers are already defined in the Parsec module, e.g. for strings. All of these parser functions are full blown parsers that can be run over some input using the parse function:

 >parse simple "" "some characters"
 Right 's'
 

'Right' in the output is signaling the success status and the tokens consumed, 's' in this case. If the parser fails, 'Left' is returned:

 >parse simple "" "123"
 Left (line 1, column 1):
 unexpected "1"
 expecting letter
 

The interesting thing about Parsec's parsers is their monadic nature. It allows us to combine simple parsers to build more powerful and expressive parsers. The Parsec library comes loaded with tons of monadic combinators for parsers: many, many1, skipMany, skipMany1, sepBy, sepBy1, endBy, endBy1, sepEndBy, sepEndBy1, count, between, option, choice, manyTill, chainl, chainl1, eof, notFollowedBy, anyToken... For instance, we can create a parser to parse any number of chars using the previously defined parser and the combinator 'many':

 complex = many simple
 >parse complex "" "abc123"
 Right "abc"
 

Since parsers are monad instances, Haskell's "do notation" can be used to write the parsers in a more convenient way. The final result is that using Parsec you can write a parser for a grammar almost by simply translating the rules to Haskell code. An example from the Parsec documentation:

 ------->EBNF:
 receipt     ::= product* total
 produkt    ::= "return" price ";"
                      | identifier price ";"
 total       ::= price "total"
 price       ::= digit+ "." digit digit
 
 
 ------>Parsec:
 receipt = do{ ps <- many produkt
 ; p <- total
 
 ; return (sum ps == p)
 }
 
 produkt = do{ symbol "return"
 ; p <- price
 ; semi
 
 ; return (-p)
 }
 <|> do{ identifier
 ; p <- price
 ; semi
 
 ; return p
 }
 "produkt"
 
 total = do{ p <- price
 ; symbol "total"
 
 ; return p
 }
 
 price = lexeme (do{ ds1 <- many1 digit
 ; char '.'
 ; ds2 <- count 2 digit
 
 ; return (convert 0 (ds1 ++ ds2))
 })
 "price"
 where
 convert n [] = n
 convert n (d:ds) = convert (10*n + digitToInt d) ds
 

The code should be pretty straight forward. One can see how productions are implemented using Parsec and Haskell's do notation, and how the semantic transformation of the production rule is incorporated after the definition of the production.

I really encourage you to take a good look at Haskell and the little wonders, like Parsec,that it contains.

Nevertheless, my current work is based in another interesting functional language, Erlang. Recently I've been needing some parsing power for my project. After spending some time taking a look at Erlang's parsing libraries like YEPP, I really started missing Parsec. For a moment I had the idea of building a Parsec-like library for Erlang. Fortunately Jeffrey Meunler has already done it: http://www.engr.uconn.edu/~jeffm/Source/Erlang/.

This version of Parsec is much smaller than the original Parsec library for Haskell, but is handy and really does its work. The most annoying part is dealing with Erlang's unconvenient functional syntax. The Erlang's version of the 'simple' and 'complex' char parsers shown before will look like this:

 simple(S) ->
     parser:pAlphaNum(S) .
 
 >simple("abc")
 {"a","bc"}
 
 complex(S) ->
 (parse:pMany( fun parser:pAlphaNum/1 ))(S) .
 
 >complex("abc")
 {"abc",[]}
 

I've used this library to build a small parser for a subset of the SPARQL query language. Here you can see the production and how they are implemented in Erlang:

 %% @doc
 %% Main public function of the parser. It accepts a SPARQL query in a string
 %% and returns a tuple with a list of variables and a list of triple patterns {S,P,O,G} as binaries or
 %% error, if some error was found during the parsing process
 -spec parse_sparql_query(list()) -> {[bitstring()],[{bitstring(), bitstring(), bitstring(), bitstring()}]} | error .
 parse_sparql_query(Input) ->
     parser:parse(sparql_parser(), Input) .
 
 
 %% @doc
 %% This function returns the SPARQL parser composing top level parsers
 %% [5] 	SelectQuery  ::=  'SELECT' ( 'DISTINCT' | 'REDUCED' )? ( Var+ | '*' ) DatasetClause* WhereClause SolutionModifier
 sparql_parser() ->
     fun( I ) ->
             case (parser:pAnd( [
                                   parser:pString("SELECT"),
                                   fun parser:pSpaces/1,
                                   parser:pOr([parser:pString("*"),
                                               parser:pMany( fun var_parser/1 )]),
                                   fun parser:pSpaces/1,
                                   parser:pMaybe(parser:pString("WHERE")),
                                   fun parser:pSpaces/1,
                                   fun group_graph_pattern_parser/1
                                 ])) ( I ) of
                 {["SELECT",_,Vars,_,_W,_,Triples],[]} -> {Vars,lists:map(fun({triple,S,P,O}) -> {S,P,O,undefined} end, Triples)} ;
                 Other                                 -> Other
             end
     end .
 
 %% @doc
 %% [32] TriplesSameSubject ::=  VarOrTerm PropertyListNotEmpty | TriplesNode PropertyList
 triples_same_subject_parser( S ) ->
     case (parser:pOr([parser:pAnd([fun var_or_term_parser/1,
                                    fun property_list_not_empty_parser/1]),
                      parser:pAnd([fun triples_node_parser/1,
                                   parser:pMaybe(fun property_list_not_empty_parser/1)])]))( S ) of
 
         {[Sub,Ps],SP} -> case Sub of
                              {blank, BId, BTs} -> {lists:flatten(
                                                      lists:map(fun({predicate, P, O, Ts}) ->
                                                                        [{triple, BId, P, O} | Ts]
                                                                end,lists:flatten(Ps)) ++ BTs),
                                                    SP} ;
                              _Other            -> {lists:flatten(
                                                      lists:map(fun({predicate, P, O, Ts}) ->
                                                                        [{triple, Sub, P, O} | Ts]
                                                                end, lists:flatten(Ps))),
                                                    SP}
                          end ;
         error         -> error ;
         Other         -> Other
     end .
 
 %% @doc
 %% [38]    	TriplesNode  	  ::=    	Collection | BlankNodePropertyList
 triples_node_parser( S ) ->
     (parser:pOr([fun blank_node_property_list_parser/1]))( S ) .
 
 
 %% @doc
 %% [39]    	BlankNodePropertyList  	  ::=    	'[' PropertyListNotEmpty ']'
 blank_node_property_list_parser( S ) ->
     case (parser:pAnd([fun parser:pSpaces/1,
                        parser:pString("["),
                        fun parser:pSpaces/1,
                        fun property_list_not_empty_parser/1,
                        fun parser:pSpaces/1,
                        parser:pString("]"),
                        fun parser:pSpaces/1]))( S ) of
         error              -> error ;
         {[_,"[",_,Ps,_,"]",_], SP} -> BId = gen_blank_id(),
                                       {{blank, BId, lists:flatten(lists:map(fun({predicate, P, O, Ts}) -> [{triple, BId, P, O} | Ts] end, Ps))}, SP}
     end .
 
 
 %% @doc
 %% Generates a new identifier for a blank node.
 gen_blank_id() ->
     list_to_binary(io_lib:format("?_~p",[trunc(random:uniform() * 100000)])) .
 
 
 %% @doc
 %% [74]    	VAR1  	  ::=    	'?' VARNAME
 %% [75]   	VAR2 	  ::=   	'$' VARNAME
 var_parser( S ) ->
     case (parser:pAnd( [parser:pOr([parser:pString("?"),
                                     parser:pString("$")]),
                         fun( Inp ) ->
                                 {Res, More} = (parser:pMany( fun parser:pAlphaNum/1 ))( Inp ),
                                 case Res of
                                     []  ->  fail ;
                                     _Other -> {_Sp, MoreP} = parser:pSpaces(More),
                                               {plaza_utils:to_binary(Res),MoreP}
                                 end
                         end]))( S ) of
         fail            -> fail ;
         {[_S,Var], SP}  -> {Var,SP}
     end .
 
 
 %% @doc
 %% [20]	GroupGraphPattern  ::= 	'{' TriplesBlock? ( ( GraphPatternNotTriples | Filter ) '.'? TriplesBlock? )* '}'
 group_graph_pattern_parser( S ) ->
     case (parser:pAnd([parser:pString("{"),
                        fun parser:pSpaces/1,
                        fun triples_block_parser/1,
                        fun parser:pSpaces/1,
                        parser:pString("}")]))( S ) of
         error              -> error ;
         {["{",_,Ts,_,"}"],SP}  -> {Ts, SP}
     end .
 
 
 %% @doc
 %% [31]	ConstructTriples  ::=  	TriplesSameSubject ( '.' ConstructTriples? )?
 triples_block_parser( S ) ->
     case (parser:pAnd([fun triples_same_subject_parser/1,
                        parser:pMaybe(parser:pAnd([fun parser:pSpaces/1,
                                                   parser:pString("."),
                                                   fun parser:pSpaces/1,
                                                   fun triples_block_parser/1]))]))( S ) of
         error                      -> error ;
         {[Ts,[[_,".",_,OTs]]], SP} -> {lists:flatten(Ts ++ OTs), SP} ;
         {[Ts, OTs],SP}             -> {lists:flatten(Ts ++ OTs), SP}
     end .
 
 
 %% @doc
 %% Parses a var or a term. This rule is NOT in the SPARQL grammar.
 var_or_term_parser( S ) ->
     case (parser:pOr( [fun var_parser/1,
                        parser:pAnd([parser:pString("<"),
                                     parser:pMany(fun parser:pAlphaNum/1 ),
                                     parser:pString(">")])
                       ] ))( S ) of
         fail             -> fail ;
         {["<", Other, ">"], Sp}  -> {list_to_binary(Other), Sp} ;
         {Var, SP}                -> {list_to_binary([<<"?">>,Var]),SP} 
     end .
 
 
 %% @doc
 %% [35]	ObjectList  ::=	Object ( ',' Object )*
 object_list_parser( S ) ->
     case (parser:pAnd( [parser:pOr([fun var_or_term_parser/1,
                                     fun triples_node_parser/1]),
                         fun( Sp ) -> case (parser:pMany(parser:pAnd([fun parser:pSpaces/1,
                                                                      parser:pString(","),
                                                                      fun parser:pSpaces/1,
                                                                      parser:pOr([fun var_or_term_parser/1,
                                                                                  fun triples_node_parser/1 ])]))) ( Sp ) of
                                          error        -> error ;
                                          {Terms, Spp} -> {lists:map(fun([_,",",_,VarOrTerm]) -> VarOrTerm end, Terms), Spp}
                                      end
                         end ] ))( S ) of
         fail             -> fail ;
         {[P,Ps],Sp}      -> {lists:map(fun(Obj) -> case Obj of
                                                        {blank,BId,Ts} -> {obj, BId, Ts } ;
                                                        _Other         -> {obj, Obj, [] }
                                                    end
                                        end, lists:flatten([P|Ps])), Sp}
     end .
 
 
 %% @doc
 %% [33]	PropertyListNotEmpty  ::=  Verb ObjectList ( ';' ( Verb ObjectList )? )*
 property_list_not_empty_parser( S ) ->
     case (parser:pAnd( [fun var_or_term_parser/1,
                         fun object_list_parser/1,
                         parser:pMany(parser:pAnd([fun parser:pSpaces/1,
                                                   parser:pString(";"),
                                                   fun parser:pSpaces/1,
                                                   parser:pMaybe(parser:pAnd([fun var_or_term_parser/1,
                                                                              fun object_list_parser/1]))]))
                        ]))( S ) of
         fail   -> fail ;
         {[P,[],Triples],Sp} ->  {lists:map(fun({obj, O, Ts}) -> {predicate, P, O, Ts} end, Triples), Sp};
         {[P,O,Ps],Sp}       ->  {lists:flatten(
                                    lists:map(fun([Pr,Triples]) ->
                                                      lists:map(fun({obj, Ob, Ts}) -> {predicate, Pr, Ob, Ts} end,
                                                                Triples) end,
                                              [[P,O]|lists:map(fun([_,";",_,[[IP,IObjs]]]) -> [IP, IObjs] end, Ps)])), Sp}
     end .
 

Whit these definitions you can parse SPARQL queries easilly as the next test shows:

 select_sparql_1_test() ->
     Res = parse_sparql_query("SELECT * WHERE { ?s ?p ?o . ?x ?U ?Z}"),
     ?assertEqual({"*",
                   [{<<"?s">>,<<"?p">>,<<"?o">>,undefined},
                    {<<"?x">>,<<"?U">>,<<"?Z">>,undefined}]},
                 Res ) .
 

Parsec clones are also available for other languages like Java, Ruby or Python.

Pattern matching support in clojure

Lisp is loved and hated because of its syntax, but the fact is that the homogenous treatement of data and code in the language has allowed Lisp to evolve and incorporate into the language those features considered necessary by their users without modifications in the core language.
Clojure, as any other incarnation of Lisp, has inherited this extensibility capacities and takes advantage of it in different ways.
This post is introduces a library for adding pattern matching support in the Clojure in about 100 lines of code.

You can download the library from my github repository: http://github.com/antoniogarrote/clj-tuples

The library adds two constructs 'match' and 'case' to test patterns against values, and execute clojure forms associated to a succesful match.

These are some examples of how to use the library, any user of Haskell or Erlang will recognize the syntax inmediately:

 user>(tuples/match true true nil)
 nil
 
 user>(tuples/match true false nil)
 matching exception
 
 user>(tuples/match a true a)
 true
 
 
 user>(tuples/match a true (not a))
 false
 
 user>(def v '(1 2 (3 4) 5))
 #'user/v
 
 user>(tuples/match (z u (s1 s2) t) v (println s1))
 3
 
 user>;you can use _ if you are not interested in a part of the value
 nil
 user>(tuples/match (_ _ s _) v s)
 (3 4)
 
 user>(def *extracted* 
           (tuples/match (z u (s1 s2) t) (1 2 (3 4) 5) s2) s2))
 #'user/*extracted*
 user>*extracted*
 4
 
 user>(tuples/match (3 u (s1 s2) t) (1 2 (3 4) 5) (println s2))
 matching exception
 
 user>(def vu '(2 2))
 #'user/vu
 
 user> (tuples/case (vu 2)
                    (z 1) (println (str "a " z))
                    (z 2) (println (str "b " z))
                    t     :error)
 b (2 2)
 
 user>(tuples/case (vu 3)
                   (z 1) (println (str "a " z))
                   (z 2) (println (str "b " z))
                   t     :error)
 :error
 

The library is not meant to be complete, it only works with lists for doing deep matching but is a good example of how Clojure can be extended.

Hope someone finds it useful!

egearmand: a gearman server written in erlang

I've just pushed to my github repository (http://github.com/antoniogarrote/egearmand-server/tree/master)  the version 0.0.5 of egearmand, an erlang implementation of the gearman (http://gearman.org/index.php) server.

Gearman (http://en.wikipedia.org/wiki/Gearman) is a project initiated by Danga (the creators of memcache), specifying a very simple platform for distributed computing around the gearman protocol.

Egearmand offers a distributed implementation of the protocol running on erlang nodes using mnesia. If you want to try it, just clone the project from github, compile it with rake and starts egearmand from the command line. Take a look at the README document for the details.

Egearmand tries to take advantage of erlang strengths offering a fault tolerant distributed version of gearman (it is already an OTP compliant application) and adding some interesting ideas, like the support for extensions. For instance, an extension allowing gearman clients to create, publish and consume RabbitMQ queues is included.

The current version of egearmand is 0.0.5, and it implements the full gearman protocol but it is not, by any means, production ready yet. It might break in unsuspected ways, so have fun with it but be careful :)

Installing rabbitmq-erlang-client in OS X

Installing the erlang client for rabbitmq can be a little tricky, specially if you are new to erlang development and you have tried to follow the instructions at http://hopper.squarespace.com/blog/2008/1/12/introducing-the-erlang-amqp-client.html.

Requirements: erlang OTP, python, mercurial.

This is how I set up the beast under OS X, hope it can be helpful.

1.- Use mercurial to clone last version of rabbitmq:

hg clone http://hg.rabbitmq.com/rabbitmq-codegen

hg clone http://hg.rabbitmq.com/rabbitmq-server

cd rabbitmq-server

make PYTHON=/opt/local/bin/python2.5

It should have compiled the server without any problem. You can set up python path with the PYTHON flag.

2.-Then you need to download and install the erlang client:

hg clone http://hg.rabbitmq.com/rabbitmq-erlang-client/

cd rabbitmq-erlang-client/

make

It should have compiled the client code. If you get some warnings about the ssl_record, it means you don't have a recent version of rabbitmq at ../rabbitmq-server. The change in that record can be seen at: http://hg.rabbitmq.com/rabbitmq-server/rev/9d6c6a354a395257493cbf8a84332326bfb71a59

The erlang client needs access to the rabbitmq server code. By default it will look for it at ../rabbitmq-server.

After compiling the code, I made it available to erlang applications by copying the output to the erlang's OTP home directory:

sudo cp -rf rabbitmq-server /usr/local/lib/erlang/lib/rabbitmq_server-1.6.0

sudo cp -rf rabbitmq-erlang-client /usr/local/lib/erlang/lib/rabbitmq_erlang_client-1.6.0

ln -s /usr/local/lib/erlang/lib/rabbitmq_server-1.6.0 /usr/local/lib/erlang/lib/rabbit_common

Now you are ready to start rabbitmq server and test the client. The last soft link allows to retrieve rabbitmq server record definitions from the erlang client. With that symlink you can include the client's records with -include_lib("rabbitmq_erlang_client/include/amqp_client.hrl"). The code at http://hopper.squarespace.com/blog/2008/1/12/introducing-the-erlang-amqp-client.html seems to be out of date, so you will need to start the client in a different way:

$erl -mnesia dir  -boot start_sasl -s rabbit

...OTP and rabbitmq starting...

1> amqp_connection:start_direct().
<0.140.0>

If everything is fine you should obtain a beautiful Pid as a result of the previous function invocation. Take a look at the code of the client to see how to use the client's API

RESTful JSONP in Rails using Rack middleware

Rails 2.3 has finally included support for Rack. The Rack project (http://rack.rubyforge.org/) consists of an abstraction layer on top of the webserver that provides a common set of services for developers while hiding the complexity of the web server software whatever it should be (mongrel, glassfish, thin, etc).

The support for Rack is a major accomplishment of the Rail Core Team and opens a lot of interesting new capabilities for the framework users.

In this post entry I will comment a possible use of Rack to provide support for RESTful-like requests for JSONP in Rails.

As you surely know, current desktop browsers only give support for GET and POST HTTP requests. This is a serious difficulty for clients trying to follow REST design guidelines in browser clients, since PUT and DELETE methods are needed to carry request to the server asking for the update and deletion of a resource.

Rails address this problem allowing to pass a '_method' parameter in POST requests that overwrites with its value the POST method of the request. In this way, PUT and DELETE requests can be sent from a HTML form or from an AJAX request.

But this feature only solves half of the problem. Web browsers implement a security model known as the Same Origin Policy (http://en.wikipedia.org/wiki/Same_origin_policy), where only requests to the same domain of the current URL are allowed. This policy effectively forbids cross domain AJAX requests. At least until a common specification for cross domain XMLHTTPRequests can be agreeded and implemented (http://dev.w3.org/2006/webapi/XMLHttpRequest-2/).

One work-around for this problem has been found in the use of JSONP (http://bob.pythonmac.org/archives/2005/12/05/remote-json-jsonp/) a technique where a new SCRIPT tag is inserted into the web page DOM with a callback parameter. The server answers with a script where the callback parameter value is used as a function name that is invoked with the data from the server passed as the actual parameter invokation value.

JSONP, though nothing more than a hack, is been used by many web pages and services to provide data to javascript clients and mashups. A big problem with JSONP appears when you try to use JSONP in a RESTful way. Including the SCRIPT tag in the page initiates a new GET request to the server, so if you wants to use JSONP to send a request for creating a new resource (POST), updating it (PUT), or deleting it (DELETE), you must use some work-around like the use of the _method parameter in Rails. But the overwriting of the HTTP method in Rails only works with POST request: no RESTful JSONP for you my friend.

Of course, you can always try to modify the behaviour of the method override mechanism of Rails with a little bit of monkey patching. Solutions in these direction has been proposed (http://www.actsasflinn.com/2008/06/13/cross-domain-restful-json-p-with-rails) but they are very likely to break with each new Rails release.

With the support for Rack in Rails 2.3 the situation has changed and a cleaner way to change the behavior of MethodOverride in Rails can be built.

Now, MethodOverride is a class of the Rack Rails middleware stack (http://rack.rubyforge.org/doc/classes/Rack/MethodOverride.html). Rack Rails allow developers to insert new middleware classes in the stack (http://guides.rubyonrails.org/rails_on_rack.html), so you can easily add a new middleware before MethodOverride that allows the use of _method parameters with GET requests, without the need of monkey patching:

 module SemanticResource
   
   # Allows overwriting of HTTP method also in
   # GET HTTP requests in order to allow
   # RESTful JSONP calls
   class RestfulJsonpMiddleware
   
     HTTP_METHODS = %w(GET HEAD PUT POST DELETE OPTIONS)
     METHOD_OVERRIDE_PARAM_KEY = "_method".freeze
     HTTP_METHOD_OVERRIDE_HEADER = "HTTP_X_HTTP_METHOD_OVERRIDE".freeze
     
     attr_accessor :method_parameter_name
     
     def initialize(app)
       @app = app
       @method_parameter_name = METHOD_OVERRIDE_PARAM_KEY
     end
 
     # We check if the method parameter is in the request
     # and set up the request to allow the execution of the
     # overwritten HTTP method
     def call(env)
       req = ActionController::Request.new(env)
       method = req.params[@method_parameter_name]
       method = method.to_s.upcase
       if HTTP_METHODS.include?(method)
         env["rack.methodoverride.original_method"] = env["REQUEST_METHOD"]
         env["REQUEST_METHOD"] = "POST"
         env[HTTP_METHOD_OVERRIDE_HEADER] = method
       end
      @app.call(env)
     end
   end
 end
 

And now we can add the middleware in config.rb

 config.middleware.insert_before(Rack::MethodOverride,SemanticResource::RestfulJsonpMiddleware)

That's it, cross domain RESTful AJAX for Rails in a little bit cleaner way! You must take some time to consider the security implications of this change in your application though.

π calculus

A la hora de afrontar cualquier problema lo suficientemente complicado, uno de los principales problemas a los que nos enfrentamos es el de conseguir pensar con suficiente claridad acerca del mismo. Esto implica caracterizarlo de forma adecuada, eliminando todos los elementos superfluos o subproblemas no esenciales, lo que supone casi siempre, siguiendo el ejemplo de uno mis héroes intelectuales (http://en.wikipedia.org/wiki/Wittgenstein), conseguir un lenguaje que nos permita razonar de forma exacta sobre dicho problema.

Ese lenguaje siempre es el de las matemáticas y cuando el problema tiene que ver con la computación, toma forma de cálculo, no como el cálculo diferencial, sino en la acepción que se usa en lógica: la de un sistema formal para la manipulación de expresiones simbólicas. El 'calculus' más relevante para la computación y el más conocido es el cálculo lambda de Alonzo Church y compañía, que supone la base de cualquier lenguaje de programación funcional, pero otros muchos calculi han sido construidos para el estudio de problemas concretos.

El Cálculo Pi, es uno de ellos. Pretende faciliar el estudio de procesos paralelos de computación que evolucionan dinámicamente. A pesar de lo ambicioso del objetivo, los componentes del cálculo π no podían ser más simples:

Un proceso se identifica por una letra mayúscula:

P, Q, R...

Cuando los procesos se ejecutan en paralelo el proceso se identifican por el símbolo '|':

P | Q | R

Así se expresarían tres procesos que se ejecutan paralelamente. Pero lo interesante viene cuando los procesos empiezan a comunicarse entre sí:

P = <y>x.P'

Expresa que el proceso P envía el nombre 'x' por el canal 'y' y sigue comportandose como P'.  Por el contrario:

P = y(x).P'

Indica que el proceso P espera recibir el nombre 'x' por el canal 'y' y a continuación se sigue comportando como P'. A través de los canales se pueden enlazar los procesos, por ejemplo:

<y>x.P | y(z).Q

En este ejemplo el proceso P envía por el canal 'y' el nombre 'x', el proceso Q recibe el nombre por el canal 'y' y en el contexto de Q renombra 'z' con el nombre 'x' enviado {x/z}. Si expandimos la expresión:

<y>x.P | y(z).Q -> P|Q{x/z}

Tendríamos el proceso P ejecutándose en paralelo con el proceso Q donde el nombre 'z' pasa a valer 'x'.

El calculo tiene sólo unos cuantos símbolos más: P! sería un proceso recursivo de tal forma que P! = P! | P, y una forma de expresar que un canal es privado a dos procesos.

La aportación seminal del Cálculo Pi como cálculo de procesos, es la brillante idea de tratar tanto canales como símbolos de la misma manera, como nombres que se pueden enviar por otros canales con lo que se puede "enviar el proceso" pasando canales de comunicación, si hay colisión de nombres se hace una alfa-conversión como en el cálculo lambda:

P | Q | R

<x1>y1.y1(z1) | x1(y2).<z2>y2  |  z2(x3).<x3>"hola"

Si computamos este sistema:

y1(z1) | {y1/y2}.<z2>y2 | z2(x3).<x3>"hola"

y1(z1) |  <z2>y1 | z2(x3).<x3>"hola"

y1(z1) |  0  |  {y1/x3}.<x3>"hola"

y1(z1) | 0 | <y1>"hola"

{"hola"/z1} | 0 | 0

"hola" | 0 | 0

0 | 0 |0

Podemos observar como el proceso P le ha pasado un enlace hacia si mismo a R a través de Q y R luego le ha enviado la cadena "hola" a través del enlace enviado.

Lo atractivo del Cálculo Pi es que su álgebra asociada nos permite tratar formalmente problemas de computación paralela y responder a preguntas como "¿se comportan dos sistemas de la misma manera?" lo que se conoce como el problema de bisimulación, Y problemas de computación paralela los hay muy interesantes, piénsese en el web y el envío de enlaces hacia servidores a través de documentos HTML.

processing javascript

Por esas cosas que tiene la vida me tengo que leer Visualizing Data de Ben Fry, uno de los popes del idem, o de la analítica visual, o de como como quiera que llamen a eso de intentar meter datos de 15 dimensiones en 400x400 pixels.

El caso es que el amigo Ben, se pasa el segundo capítulo de su libro hablando de un lenguaje de manipulación de gráficos llamado Processing, un lenguaje de scripting bastante apañado que hace muy sencillo hacer dibujitos y animaciones interactivas. Cuando digo que lo hace sencillo, me refiero a que intenta conseguir que diseñadores gráficos y "artistas multimedia" escriban código.

El caso es que jugar con el entorno de processing es bastante divertido y se consiguen resultados bastante vistosos con muy poco esfuerzo. Me recuerda un montón a la versión de LOGO con la que escribí mis primeras líneas en un ordenador (¡qué tiempos aquellos!).

La tortuga de LOGO en toda su gloria

Processing está escrito sobre Java. Esto es una ventaja pero también un inconveniente, sobre todo cuando quieres colgar una de tus animaciones en la web, ya que tienes que hacerlo vía applet. Generar el applet es muy sencillo con Processing, pero aún así un applet es, bueno, un applet.

Afortunadamente, recordé que el gran Javi Santana me envío en algún momento un mail hablándome de processing.js, un port de parte de la API de processing a javascript, escrito por John Resig, el autor de JQuery, al que tanto debemos todos los que hacemos programación web.

Processing.js funciona usando el objeto canvas que ya soportan algunos navegadores (mis pruebas las he hecho con Firefox 3.0.6) y funciona bastante bien. La API tiene dos formas de trabajar, puedes suministrarle código processing a un objeto javascript y un parser compuesto por un puñado de expresiones regulares lo transforma en código canvas javascript que luego evalúa, o se puede trabajar con un objeto javascript Processing, e ir invocando sobre él las funciones de la API de processing.
Este último modo resulta quizás más interesante, ya que permite en el repintado interaccionar con otros elementos HTML de la página. Como ejemplo os paso el código de una de las demos del sitio de processing reescrito con processing.js, con un pequeño extra que hace posible repintar el fondo del canvas usando los valores suministrados desde el HTML de la página.

Para hacerlo funcionar cambiad la ruta hasta processing.js del código:

 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
 <html> <head>
 <title></title>
 <script type="text/javascript"
 src="../processing/processing.js"></script>
 <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js" type="text/javascript"></script>
 <script type="text/javascript">
  window.onload = function(){
    var p = Processing("canvasElement");
    p.setup = function() {
       p.size(400,400);
       p.stroke(255);
       p.background(0,0,0);
    };
    p.draw = function() {
       p.background($("#r").val(),$("#g").val(),$("#b").val());
       p.line(150,25,p.mouseX,p.mouseY);
    }
 
    p.init();
 };
 </script>
 </head>
 
 <body>
 <h1>Canvas demo</h1>
 
 <p>
 <label for="r">red</label>
   <input type="text" id="r"></input>
 </p>
 <p>
 <label for="g">green</label>
   <input type="text" id="g"></input>
 </p>
 <p>
 <label for="b">blue</label>
   <input type="text" id="b"></input>
 
 </p>
 <br/>
 
 <canvas id="canvasElement" height="150" width="600"/>
 
 </body> </html>
 

Os recomiendo que le probéis tanto processing, como processing.js, seguro que pasáis un buen rato.