ya! library for go routines in JavaScript (Next, ES6)

ya! is a go-routine implementation in ES6 using generators and promises, inspired by task.js

A month ago I discovered Go language, a kind of modern C designed by Robert Griesemer, Rob Pike, and Ken Thompson inside Google and after passing the online tutorial I ended fascinated by go-routines and channeled communications.

In Go language a go-routine is any function launched by using the go keyword.  Unlike co-routines, a go-routine has no explicit control over its execution and it is a runtime scheduler who is in charge of pausing and resuming routines. A go-routine is a green thread ruled by the go runtime.

In Go, communications between go-routines are made explicit by using channels (the communication model is based on Communicating Sequential Processes). A channel can receive or send data. When a go-routine is sending data to a channel but nobody is waiting for that data in the same channel, the go-routine blocks until someone consumes the data. The same happens when getting data and there is no data available.

Generators in JavaScript in addition to a simple scheduler enable this kind of collaboration and ya! is a library to emulate Go go-routines related features such as go-routines themselves, channels (buffered and unbuffered) and special Go statements like select (range is coming!).

You can find the library in bower under the name of ya.js and read the docs or see the annotated source on GitHub.

As an extra, here you have the go version of the dinning philosopher problem and the JS version using ya!

Seguir leyendo «ya! library for go routines in JavaScript (Next, ES6)»

Introducing strongforce: an infrastructure for game engines

The strongforce library is a light-weight framework to build game engines. It provides a backbone for a powerful game loop and helper classes for efficiently implement new game components such as models, renders and simulators. Main framework features include:

  •  Model oriented.
  •  Decoupled simulation and render loops.
  •  Real time simulation loop for integration with physics engines.
  •  Separated read and write stages during simulation to avoid expensive model copies.
  •  Versatile and dynamic visitor pattern to implement the Game Model Hierarchy.
  •  Use of JavaScript functors to quickly implement simple renders or simulators.
  •  Helper classes to ease implementation of Renders and Simulators.
  •  Events for Model to allow asynchronous programming.
  •  Fully control of the loop step and execution.
  •  Careful separation of responsibilities allowing the developer to focus on data model, business logic and rendering process separately.

Strongforce is not a graphic engine, not even a game engine. It is only a proposal for a game architecture and a main loop. You can read more about the framework internals and philosophy of design in the wiki pages of the repository or further in this post. Please feel free to provide any feedback by opening new issues on GitHub.

Seguir leyendo «Introducing strongforce: an infrastructure for game engines»

Non alphanumeric JavaScript

This post is a translation of JavaScript no alfanumérico which was linked by Diario Linux some days ago so I decided to write an English version. Enjoy it!

I have a discussion with Adri about if JavaScript is a good programming language or not. In one of our conversations, Adri shows criticism about JS’ weak type and links the article Brainfuck beware: JavaScript is after you! by Patricio Palladino.

I encourage the lecture. It is quite interesting.

In short, Patricio has created a tool to convert a JS script into another equivalent formed by symbols +[](){}! only. Don’t you believe me? Try to enter this in a JS console from any browser (ensure you are visiting a web site).

[][(![]+[])[!+[]+!![]+!![]]+({}+[])[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][({}+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]]+(![]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+[]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+({}+[])[+!![]]+(!![]+[])[+!![]]]((+{}+[])[+!![]]+(![]+[])[!+[]+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]+[][(![]+[])[!+[]+!![]+!![]]+({}+[])[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][({}+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]]+(![]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+[]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+({}+[])[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+([][+[]]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+([][+[]]+[])[+[]]+([][+[]]+[])[+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(![]+[])[!+[]+!![]+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(+{}+[])[+!![]]+([]+[][(![]+[])[!+[]+!![]+!![]]+({}+[])[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][({}+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]]+(![]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+[]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+({}+[])[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+([][+[]]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+(![]+[])[!+[]+!![]]+({}+[])[+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(+{}+[])[+!![]]+(!![]+[])[+[]]+([][+[]]+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]])())[!+[]+!![]+!![]]+([][+[]]+[])[!+[]+!![]+!![]])()([][(![]+[])[!+[]+!![]+!![]]+({}+[])[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][({}+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]]+(![]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+[]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+({}+[])[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+([][+[]]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(![]+[])[!+[]+!![]+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(+{}+[])[+!![]]+([]+[][(![]+[])[!+[]+!![]+!![]]+({}+[])[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][({}+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]]+(![]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+[]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+({}+[])[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+([][+[]]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+(![]+[])[!+[]+!![]]+({}+[])[+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(+{}+[])[+!![]]+(!![]+[])[+[]]+([][+[]]+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]])())[!+[]+!![]+!![]]+([][+[]]+[])[!+[]+!![]+!![]])()(({}+[])[+[]])[+[]]+(!+[]+!![]+[])+(!+[]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+[]))+(+!![]+[])+[][(![]+[])[!+[]+!![]+!![]]+({}+[])[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][({}+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]]+(![]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+[]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+({}+[])[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+([][+[]]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+([][+[]]+[])[+[]]+([][+[]]+[])[+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(![]+[])[!+[]+!![]+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(+{}+[])[+!![]]+([]+[][(![]+[])[!+[]+!![]+!![]]+({}+[])[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][({}+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]]+(![]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+[]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+({}+[])[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+([][+[]]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+(![]+[])[!+[]+!![]]+({}+[])[+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(+{}+[])[+!![]]+(!![]+[])[+[]]+([][+[]]+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]])())[!+[]+!![]+!![]]+([][+[]]+[])[!+[]+!![]+!![]])()([][(![]+[])[!+[]+!![]+!![]]+({}+[])[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][({}+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]]+(![]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+[]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+({}+[])[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+([][+[]]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(![]+[])[!+[]+!![]+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(+{}+[])[+!![]]+([]+[][(![]+[])[!+[]+!![]+!![]]+({}+[])[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][({}+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]]+(![]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+[]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+({}+[])[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+([][+[]]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+(![]+[])[!+[]+!![]]+({}+[])[+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(+{}+[])[+!![]]+(!![]+[])[+[]]+([][+[]]+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]])())[!+[]+!![]+!![]]+([][+[]]+[])[!+[]+!![]+!![]])()(({}+[])[+[]])[+[]]+(!+[]+!![]+[])+(!+[]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+[])))()

It should have appeared a dialog with the number 1. What the hell happened?

Seguir leyendo «Non alphanumeric JavaScript»

μTask

Bea's kanban in English
Bea’s kanban in English

Os presento μTask, una pequeña aplicación para gestionar tareas usando un panel kanban en fase alpha. Últimamente me ronda mucho por la cabeza las aplicaciones con modelos representables en texto plano. Como todo.txt, por ejemplo.

μTask es un pequeño parser de una notación de mi invención para gestionar tareas, pensada para interacturar con los comandos estándar cortar y pegar.

Tenéis un ejemplo de una tarea aquí mismo:

To do:
  Task 153: LMDL fixes

Y una escueta lista de características:

  • Tareas con identificador, descripción y detalles
  • Dos espacios para definir tareas: uno para reflejar el estado del kanban y otro para clasificar y detallar las tareas
  • Clasificaciones por temas y colores
  • Expresa el progreso de una tarea y su fecha límite

Podéis encontrar más información así como un lugar donde dejar vuestros comentarios en la página del proyecto:

http://unoyunodiez.com/proyectos/mtask/

El tutorial en GitHub:

https://github.com/lodr/mtask/#%CE%BCtask

ask.js

Bueno, hace tiempo que no hay actividad por el blog pero vuelvo para anunciar un nuevo proyecto que acabo de hacer entrar en fase alpha a la espera de que los usuarios me den algo de feedback.

Su nombre es ask.js y se trata de un módulo JavaScript que permite hacer búsquedas en un array de objetos utilizando la sintaxis de consultas de MongoDB.

No sé si conoceis un poco MongoDB pero se trata de una base de datos NoSQL orientada a documentos, documentos BSON, de hecho. BSON es una implementación binaria de JSON. Realizar búsquedas en Mongo se hace mediante una suerte de «objetos parciales» del tipo { name: 'Anne', 'address.city': 'Madrid', ...} aunque también admite restricciones complejas por campo como en { manufacturer: { $in: ['Ford', 'Munstang', 'Land Rover'] }, year: { $gte: 1990, $lt: 2000} }.

Una vez añadidos ask.js, puedes hacer ask.mongify() sobre un array para extender el mismo con un nuevo método find() que acepta, hasta el momento, las siguientes características de las búsquedas de MongoDB:

  • Restricciones por valor: { field: value }
  • Expresiones JS en cláusulas $where
  • Subobjetos, elementos parciales: { 'field.subfield': value } y $elemMatch
  • Comprobación de existencia $exists
  • Restricciones por expresión regular: { field: /value/i } y $regex
  • Operadores de comparación <, <=, >= y >
  • Operadores lógicos $or, $and y $nor
  • Módulo $mod
  • Comprobación de tipo $type
  • Operadores de inclusión $in, $nin y $all
  • Metaoperador $not
  • Comprobación de tamaño $size
  • Comprobación de desigualdad $ne

Además, es mi primer proyecto siguiendo TDD utilizando Jasmine como framework de testing. Algún día os hablaré de Jasmine y de cuánto me gusta su API.

Seguir leyendo «ask.js»

JavaScript no alfanumérico

Tengo una discusión con Adri acerca de si JavaScript es un buen lenguaje o no. En una de nuestras conversaciones critica el tipado débil de JS y enlaza el artículo Brainfuck beware: JavaScript is after you! de Patricio Palladino.

Os recomiendo encarecidamente su lectura. Es muy interesante.

Resumiendo, Patricio ha creado una herramienta con la que convertir un script JS a otro, equivalente, formado únicamente por símbolos +[](){}! ¿No me creeis? Probad a introducir esto es una consola JS de un navegador cualquiera (la de Firebug o la de Chrome servirán).

[][(![]+[])[!+[]+!![]+!![]]+({}+[])[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][({}+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]]+(![]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+[]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+({}+[])[+!![]]+(!![]+[])[+!![]]]((+{}+[])[+!![]]+(![]+[])[!+[]+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]+[][(![]+[])[!+[]+!![]+!![]]+({}+[])[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][({}+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]]+(![]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+[]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+({}+[])[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+([][+[]]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+([][+[]]+[])[+[]]+([][+[]]+[])[+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(![]+[])[!+[]+!![]+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(+{}+[])[+!![]]+([]+[][(![]+[])[!+[]+!![]+!![]]+({}+[])[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][({}+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]]+(![]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+[]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+({}+[])[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+([][+[]]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+(![]+[])[!+[]+!![]]+({}+[])[+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(+{}+[])[+!![]]+(!![]+[])[+[]]+([][+[]]+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]])())[!+[]+!![]+!![]]+([][+[]]+[])[!+[]+!![]+!![]])()([][(![]+[])[!+[]+!![]+!![]]+({}+[])[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][({}+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]]+(![]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+[]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+({}+[])[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+([][+[]]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(![]+[])[!+[]+!![]+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(+{}+[])[+!![]]+([]+[][(![]+[])[!+[]+!![]+!![]]+({}+[])[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][({}+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]]+(![]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+[]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+({}+[])[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+([][+[]]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+(![]+[])[!+[]+!![]]+({}+[])[+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(+{}+[])[+!![]]+(!![]+[])[+[]]+([][+[]]+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]])())[!+[]+!![]+!![]]+([][+[]]+[])[!+[]+!![]+!![]])()(({}+[])[+[]])[+[]]+(!+[]+!![]+[])+(!+[]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+[]))+(+!![]+[])+[][(![]+[])[!+[]+!![]+!![]]+({}+[])[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][({}+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]]+(![]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+[]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+({}+[])[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+([][+[]]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+([][+[]]+[])[+[]]+([][+[]]+[])[+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(![]+[])[!+[]+!![]+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(+{}+[])[+!![]]+([]+[][(![]+[])[!+[]+!![]+!![]]+({}+[])[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][({}+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]]+(![]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+[]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+({}+[])[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+([][+[]]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+(![]+[])[!+[]+!![]]+({}+[])[+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(+{}+[])[+!![]]+(!![]+[])[+[]]+([][+[]]+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]])())[!+[]+!![]+!![]]+([][+[]]+[])[!+[]+!![]+!![]])()([][(![]+[])[!+[]+!![]+!![]]+({}+[])[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][({}+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]]+(![]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+[]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+({}+[])[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+([][+[]]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(![]+[])[!+[]+!![]+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(+{}+[])[+!![]]+([]+[][(![]+[])[!+[]+!![]+!![]]+({}+[])[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][({}+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]]+(![]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+[]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+({}+[])[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+([][+[]]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+(![]+[])[!+[]+!![]]+({}+[])[+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(+{}+[])[+!![]]+(!![]+[])[+[]]+([][+[]]+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]])())[!+[]+!![]+!![]]+([][+[]]+[])[!+[]+!![]+!![]])()(({}+[])[+[]])[+[]]+(!+[]+!![]+[])+(!+[]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+[])))()

Seguir leyendo «JavaScript no alfanumérico»

method_missing en JavaScript

Os decía en la última entrada que podíamos solucionar en JavaScript el problema de metaprogramación de generar los comandos sobre la marcha, en el momento mismo de ser invocados y no antes. Os decía también que creía que me había quedado muy bien y en honor a la verdad debo decir que la idea la saqué del artículo The Command Pattern in JavaScript de Peter Michaux, ¡pero sólo me había leído el primer párrafo!, en especial la parte en la que dice:

In JavaScript, building our own dot operator function is one way to make indirect object function property calls and escape the built-in paradigm.

En JavaScript, construir nuestro propia función operador punto es una manera de hacer llamadas a función de manera indirecta y así escapar del paradigma por defecto.

Así que me puse a implementar este operador «punto» y cuando hube terminado me di cuenta de lo parecido con la solución propuesta por Peter.

Antes de implementar nada merece la pena entender cómo JavaScript busca los atributos de un objeto: para empezar, busca en el objeto y si ahí no está, busca en su cadena de prototipos que es otro objeto. Si ahí tampoco está, busca en la cadena de prototipos de la cadena de prototipos y así, hasta alcanzar cierto objeto cuya cadena de prototipos es null. Entonces devuelve undefined. En Firefox y en node.js, podemos consultar la cadena de prototipos de un objeto mediante el atributo especial __proto__,

La manera que tiene JavaScript de establecer la cadena de prototipos de un objeto es mediante el operador new y la propiedad prototype presente en todos los objetos. Cuando hacemos var o = new F(), aparte de construir el objeto mediante F(), hacemos que la cadena de prototipos del mismo apunte al atributo F.prototype.

Veamos una interacción en node.js* asumiendo que hemos cargado el siguiente listado:

function HTTP() {};
HTTP.prototype.get = function() {
    function makeGet(path) {
        return function(){
            return 'GET ' + this.url + '/' + path;
        };
    }

    Array.prototype.forEach.call(arguments, function(name){
        this[name] = makeGet(name);
    }, this);
};

function MyCompany() {
    this.url = 'mycompany.com';
};
MyCompany.prototype = new HTTP();
MyCompany.prototype.get('projects', 'employees', 'customers');

function YourCompany() {
    this.url = 'yourcompany.com';
}
YourCompany.prototype = new HTTP();
YourCompany.prototype.get('projects', 'employees');
&gt; mine = new MyCompany();
{ url: 'mycompany.com' }
&gt; mine.__proto__
{ projects: [Function],
employees: [Function],
customers: [Function] }
&gt; mine.__proto__.__proto__
{ get: [Function] }
&gt; mine.__proto__.__proto__.__proto__
{}
&gt; mine.__proto__.__proto__.__proto__.__proto__
null

* Si probáis esto mismo en Firefox, podéis llevaros la sorpresa de que el prototipo de mine contenga el método get(). Realmente esto no es así, lo que ocurre es que la consola de Firefox nos muestra todos los atributos de un objeto: los suyos y los disponibles a través de la cadena de prototipos. Podéis consultar si get() pertenece al objeto y no a su cadena con el método hasOwnProperty() que devolverá false para ‘get’ y true para ‘employees’, por ejemplo.

Ahora sí, hemos repasado todos los conceptos que nos hacían falta y parece, por tanto, que no hay forma de interceptar o manipular cómo se recuperan los atributos de un objeto… ¿o sí?

method_missing

Como el título de la entrada indica, la idea consiste en emular el comportamiento de method_missing de Ruby. Cuando Ruby busca un método en un objeto, busca en el propio objeto; sino está ahí, en su clase y si tampoco está, sube por la jerarquía de clases. Si aun así no se encuentra, se llama al método especial method_missing() y si este tampoco está, se lanza la excepción NoMethodError.

Voy a extender el prototipo de Object para que incluya un método similar cuya implementación por defecto consiste precisamente en lanzar una excepción con el mensaje ‘NoMethodError’. Si ya tenías cargados los listados anteriores puedes copiar y pegar los siguientes y añadirás los métodos nuevos sobre la marcha: piénsalo bien, añades los métodos en tiempo de ejecución. ¡Esa es la gracia!

Object.prototype.method_missing = function() {
    throw 'NoMethodError';
};

Como adelantábamos al comienzo de la entrada, construiremos nuestro operador ‘punto’. Una implementación podría ser:

Object.prototype.dot = function(name) {
    var args = Array.prototype.slice.call(arguments, 1);
    var f = this[name];
    if (!f) {
        args.splice(0, 0, name);
        return this.method_missing.apply(this, args);
    } else {
        return f.apply(this, args);
    }
};

Es interesante hacer notar que realmente, esto no sustituye al operador punto dado que no recupera un atributo sino que lo invoca así que sólo debería usarse para realizar llamadas a métodos. El nombre ‘dot’ es apropiado para recordar qué estamos haciendo pero resultaría más correcto que se llamase ‘send’ como ocurre en el artículo de Peter.

Podemos cargar los ejemplos anteriores y probarlos en node.js:

&gt; yours = new YourCompany();
{ url: 'yourcompany.com' }
&gt; yours.employees();
'GET yourcompany.com/employees'
&gt; yours.dot('employees');
'GET yourcompany.com/employees'
&gt; yours.customers();
TypeError: Object #&lt;HTTP&gt; has no method 'customers'
&gt; yours.dot('customers');
NoMethodError
&gt; yours.url
'yourcompany.com'
&gt; yours.dot('url')
TypeError: Object yourcompany.com has no method 'apply'

Ahora es fácil, sencillamente reimplementaremos method_missing() en el prototipo de HTTP para que, si llega a invocarse, cree un nuevo método mediante get() y lo invoque. Vale la pena recalcar que method_missing() se llama de tal manera que this hace referencia al objeto para el que se trató de invocar el método:

HTTP.prototype.method_missing = function(name) {
    this.constructor.prototype.get(name);
    return this.dot.apply(this, arguments);
};

Mediante this.constructor.prototype llegamos al prototipo del constructor que es precisamente el objeto al que apunta la cadena de prototipos del objeto. Contra lo que dicta el sentido común, esto no añade los nuevos métodos al prototipo

Ahora sí, tenemos una forma de crear métodos sobre la marcha sin preocuparnos de si existían antes o no y resuelto como lo haría Ruby. Además, las nuevas funciones extienden el prototipo de Object por lo que cualquier objeto dispondrá de ellas.

No discutiré más y con esto doy por terminadas las entradas sobre metaprogramación a la espera de vuestros comentarios y aportaciones. Espero que os haya sido tan entretenido como a mí.

Si os ha gustado la entrada, compartidla, por favor.

Edit 2012/04/09: He corregido el código de la función dot(), añadido al repositorio de github los ejemplos y he extendido la explicación sobre this.constructor.prototype dado que no era del todo correcta.

Además, creo que voy a hacer un último post con algo de discusión… no sé, ya veré.

Tenéis todo el código de los ejemplos en:
https://github.com/lodr/metaprogramming

Si os gusta la entrada, ¡comentadla y compartidla!

Metaprogramación en JavaScript

¡Hola de nuevo!

Me he pasado toda la tarde del martes preparando esta entrada, pensando que iba a ser coser y cantar y ha resultado ser un horror debido en parte ciertos aspectos de JavaScript que hacen las cosas pesadillescas. Eso sí, me ha dado para una segunda entrada la mar de interesante que veréis dentro de poco.

Como os adelantaba en la entrada de metaprogramación en Python, aquí va la adaptación de la misma a JavaScript. Si queréis profundizar sobre metaprogramación podéis leer la entrada anterior y echarle un vistazo al material que colgué allí. Como entonces, es necesario un nivel intermedio de conocimientos teniendo claro el modelo de datos de JavaScript. De hecho, me vais a permitir advertiros que si cosas como…

  1. La cadena de prototipos
  2. Las funciones constructoras
  3. El hecho de que el objeto this sea dependiente de contexto en el que es utilizado

…os suenan a chino, leais antes esta estupenda introducción a JavaScript en la Mozilla Development Network o no entederéis na de na.

Como ocurría en el ejemplo de Python, nuestro objetivo será el de crear una clase de utilidad para obtener diversos recursos de una compañía con sitio web mycopany.com que publica su contenido en /projects, /customers y /employees. Una primera aproximación podría ser:

function MyCompany() {
    this.url = 'mycompany.com';
};

MyCompany.prototype.projects = function() {
    return this._get('/projects');
}

MyCompany.prototype.employeecs = function() {
    return this._get('/employees');
}

MyCompany.prototype.customers = function() {
    return this._get('/customers');
}

MyCompany.prototype._get = function(path) {
    return 'GET ' + this.url + path;
}

Como veis, los métodos tienen todos prácticamente la misma forma, la técnica del copy & paste nos permite añadir cuantos métodos nuevos necesitemos pero todos sabemos quién carga el copy & paste. Como decía en la última entrada, el objetivo de la programación dinámica es:

DRY
Don’t Repeat Yourself

Así que vamos a ver cómo expresar lo mismo sin escribir tanto:

function MyCompany() {
    this.url = 'mycompany.com';
};

function makeGet(path) {
    return function(){
        return 'GET ' + this.url + '/' + path;
    };
}

['projects', 'employees', 'customers'].forEach(function(name){
    MyCompany.prototype[name] = makeGet(name);
});

La función makeGet devuelve una función con el comportamiento deseado, derivado del nombre del método. Si ahora a la compañía le diera por cambiar los recursos a /items/projects, /items/employees e /items/customers, bastaría modificar la función anónima devuelta por makeGet() para actualizar el comportamiento.

Lo que hemos hecho es sencillamente construir una lista con los nombres de los métodos que nos interesan, por cada uno de ellos llamamos a makeGet() para obtener la función adecuada y la añadimos al prototipo de la función MyCompany bajo el campo indicado por name para que quede a disposición de todas las instancias creadas por dicha función.

Podéis usar la shell d8 del motor V8 de JavaScript, node.js o, simplemente, Firebug en algún navegador moderno para probar los ejemplos:

d8> load("mycompany2.js")
d8> c = new MyCompany();
[object Object]
d8> c.employees()
GET mycompany.com/employees
d8> c.customers()
GET mycompany.com/customers
d8> c.projects()
GET mycompany.com/projects

Bien, esto es bueno, tenemos una forma de mantener el código y de extender la funcionalidad fácilmente pero la solución está demasiado acoplada al problema. Veamos cómo la herencia puede ayudarnos:

function HTTP() {};
HTTP.prototype.get = function() {
    function makeGet(path) {
        return function(){
            return 'GET ' + this.url + '/' + path;
        };
    }

    Array.prototype.forEach.call(arguments, function(name){
        this[name] = makeGet(name);
    }, this);
};

function MyCompany() {
    this.url = 'mycompany.com';
};
MyCompany.prototype = new HTTP();
MyCompany.prototype.get('projects', 'employees', 'customers');

function YourCompany() {
    this.url = 'yourcompany.com';
}
YourCompany.prototype = new HTTP();
YourCompany.prototype.get('projects', 'employees');

No muy claro, ¿verdad? ¡bienvenidos a JavaScript! Veamos qué ocurre aquí:

Lo primero que hemos hecho es crear una función constructora HTTP() cuyo prototipo contiene el método get(). Éste toma uno o más parámetros con los nombres de los métodos que se quieren crear y se encarga de llamar a makeGet() para construirlos. Para ello recorre los argumentos utilizando la variable especial arguments que parece un array. Si en vez de parecerlo, fuera un array, podríamos hacer arguments.forEach() y reproducir el comportamiento de hace un par de listados, pero como sólo lo parece, tendremos que aplicar explícitamente la función forEach() sobre arguments mediante call().

function f() {
    arguments.forEach(function(item) {
        return item;
    });
};
f();
TypeError: Object #<Object> has no method 'forEach'

Aclarado cómo recorrer los nombres de los métodos, veamos qué ocurre por cada uno de ellos. Lo que hacemos es añadir al objeto al que apuntará this, el campo indicado en name y como valor la función construída con makeGet(). ¿Qué vale exactamente this? Bueno, pues vale lo que hayamos indicado como tercer parámetro del método forEach() que es precisamente… this también. Sí, esto es así de cierto pero ahora en serio, ¿a qué apuntará this? Pues apuntará al objeto desde el que se llame la función get(), es decir, apuntará al prototipo de MyCompany y por tanto estaremos añadiendo los métodos al prototipo de MyCompany tal y como ocurría anteriormente.

Ahora todo está modularizado: la función HTTP() puede dejarse en un fihcero http.js mientras que las compañías pueden situarse en otro companies.js (así lo encontraréis en el repositorio de github). Ambas compañías heredan (a la manera de JavaScript) de HTTP() por lo que sus prototipos son instancias de HTTP(). Probemos el código:

d8> load("http.js")
d8> load("companies.js")
d8> mine = new MyCompany();
[object Object]
d8> yours = new YourCompany();
[object Object]
d8> mine.projects();
GET mycompany.com/projects
d8> mine.employees();
GET mycompany.com/employees
d8> mine.customers();
GET mycompany.com/customers
d8> yours.projects();
GET yourcompany.com/projects
d8> yours.employees();
GET yourcompany.com/employees
d8> yours.customers();
(d8):1: TypeError: Object #<HTTP> has no method 'customers'

Bien, esto se comporta tal y como lo hacía el ejemplo de Python y el de Ruby aunque debo admitirlo: JavaScript no es precisamente amigable a la hora de metaprogramar. De hecho, JavaScript no es un lenguaje amigable pero posee un modelo de datos tan regular y versátil que otorga a los desarrolladores toda la libertad que necesitan. Como dirían los ingleses:

JavaScript gives the developers enough rope to hang themselves with if they want to

Ahora bien, cerrábamos la entrada anterior sobre Python mostrando una técnica en la que proporcionábamos los métodos sobre la marcha. Ni siquiera eran creados explícitamente llamando a get() sino que la clase los proporcionaba conforme se le iban pidiendo. Os voy adelantando que en JavaScript esto puede hacerse, y creo que la técnica me ha quedado tan bien, que le voy a dedicar una entrada completa y a mostrároslo en breve.

Tenéis todo el código de los ejemplos en:
https://github.com/lodr/metaprogramming

Dudas y opiniones, ¡a los comentarios! Espero que haya sido entretenido.

Diferencias entre null y undefined en Javascript

¡Uy! Cuánto tiempo sin pasar por aquí…

Resulta que últimamente, en el trabajo, dedico mucho tiempo a Javascript. Es un lenguaje que cada vez me gusta más: flexible, simple y ortogonal. Es increíble a dónde se llega con 4 reglas bien aplicadas. Eso sí, se enseña MUY mal. Por ello, recomiendo encarecidamente leer la (re)introducción a Javascript por parte de Mozilla:

(…) Javascript probablemente sea el lengauje más incomprendido del mundo.

Volviendo al tema de este post, en Javascript hay dos «valores» especiales: null y undefined. ¿Pero qué significan exactamente y cuándo usar uno u otro? La mejor respuesta la he encontrado en StackOverflow. Traduzco la parte más divertida:

(nombre es undefined)

Programador: ¿qué es nombre?
Javascript: ¿nombre? ¿qué es un  nombre? No sé de qué hablas, ni siquiera has mencionado nombre antes. ¿No te referirás a otro lenguaje?

(nombre = null)

Programador: ¿qué es nombre?
Javascript: no lo sé

(…)

La clave para entender la diferencia radica en la noción de existencia. Si una variable es:

  • undefined: para Javascript, no existe. O bien no ha sido declarada o jamás se le asignó un valor.
  • null: para Javascript, la variable existe. En algún momento, explícitamente, la variable se estableció a null.

Las principales confusiones vienen de que, en Javascript, las expresión:

undefined == null

es verdadera por efecto del «type coercion» (otro día hablamos más de ello).

No obstante os recuerdo que el operador == no funciona en JS cómo están acostumbrados la mayoría de los programadores. El operador de comparación al uso en JS es === y la expresión:

undefined === null

es falsa como cabía esperar.

Así que, a la hora de usar uno u otro debemos ver si estamos preguntando por la existencia de una variable. Si es el caso, usaremos undefined. En caso de que preguntemos por si la variable contiene un valor seguro o no, como ocurriría en Java, C++ o C# usaremos null.