Publicidad:
La Coctelera

un rato de sol

i will work harder, ja ja ja, no ahora en serio.

1 Noviembre 2009

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.

servido por Antonio 2 comentarios compártelo

28 Septiembre 2009

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!

servido por Antonio 2 comentarios compártelo

7 Septiembre 2009

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 :)

Tags: erlang, gearman

servido por Antonio sin comentarios compártelo

27 Agosto 2009

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

Tags: erlang, rabbitmq

servido por Antonio sin comentarios compártelo

4 Julio 2009

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.

servido por Antonio sin comentarios compártelo

11 Marzo 2009

π 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.

servido por Antonio 1 comentario compártelo

4 Febrero 2009

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.

servido por Antonio 2 comentarios compártelo

28 Enero 2009

Haml en Clojure

Acabo de subir a Github clj-haml, un apaño para tener algo parecido a Haml (http://haml.hamptoncatlin.com/) para clojure.


Haml:

%div#things
     %span#rice Chicken Fried
     %p.beans{ :food => 'true' } The magical fruit
     %h1.class.otherclass#id La La La
 

clj-haml:

(h= :div#things
              (h= :span#rice "Chicken Fried")
              (h= :p.beans {:food "true"} "The magical fruit")
              (h= :h1.class.otherclass#id "La La La"))
 

El HTML generado en ambos casos:

<div id='things'>
   <span id='rice'>Chicken Fried</span>
   <p class='beans' food='true'>The magical fruit</p>
   <h1 id='id' class='class otherclass'>La La La</h1>
  </div>

Sólo hay una función:

(h= :selector attributes contents /)
 

Todos los parámetros son opcionales, :selector es un keyword que puede incluir el nombre de una etiqueta HTML seguido de un id y algunas clases especificadas siguiendo la notación CSS: :p#myid.class1.clas2, :.class1#myid, :br

Attributes es un mapa asociativo con pares clave valor para nombre de atributo y valor, por ejemplo: {:href "/test.html"}.

Contents son unos cuantos forms, potencialmente conteniendo otras llamadas (h=) que se evaluarán y cuyo valor devuelto se insertará en la etiqueta que se está generando.

Por último, el / final opcional, si presente, hace que la etiqueta generada sea con autocierre (h= :br /) -> <br/>, (h= :br) -> <br></br>.

Como en Haml vamos.

La podéis descargar aquí (http://github.com/antoniogarrote/clj-haml/tree/master).

Tags: haml, ruby, clojure

servido por Antonio sin comentarios compártelo


Sobre mí

Avatar de Antonio

un rato de sol

Barcelona/Salamanca, España
ver perfil »
contacto »
Trabajador del metal y del acero, en la gloriosa XING AG, escribo software con el que poder ganarme el jornal. En mi tiempo libre sigo tecleando código de bonitos colores a medio camino entre lo sublime y lo terrible. Últimamente me gustan mucho los gatos.

Fotos

Antonio Garrote Hernández todavía no ha subido ninguna foto.

¡Anímale a hacerlo!

Buscar

suscríbete

Selecciona el agregador que utilices para suscribirte a este blog (también puedes obtener la URL de los feeds):

¿Qué es esto?

Crea tu blog gratis en La Coctelera