Humble Indie Bundle

The Humble Indie Bundle es un experimento bien interesante. Varias compañías desarrolladores de videojuegos independientes han decidido vender cinco de sus juegos en un sólo paquete. Lo interesante es los clientes pueden pagar lo que quieran por el pack de cinco juegos. Desde un centavo de dólar hasta el infinito.

Además de eso es interesante ver que los juegos no tienen DRM, se pueden descargar las veces que uno quiera una vez comprados y están disponibles para los tres sistemas operativos más comunes: Windows, Mac OS X y Linux.

Un lado interesante es que el experimento ha dado buenos resultados. En pocos días se ha recaudado más de un millón de dólares. También es interesante ver las estadísticas de ganancias, en donde Linux y Mac OS X tiene una buena tajada. Entre los dos casi se llevan el 50%. Otra cosa curiosa es que el promedio de aporte por sistema operativo es: $14.55 para Linux, 10.99 para Mac OS X y 7.98 para Windows (al momento de escribir esto). Esto es importante porque en cierto modo ayuda a derrumbar el mito que dice que los usuarios de Linux no quieren pagar por el software, excusa usada por muchos vendedores de software para no sacar versiones para Linux. También, por supuesto, se muestra que el mercado de Linux no es tan despreciable como se pensaba.

A mi me parece que está bien pagar por buen software y cobrar por hacerlo. Compré este paquete con gusto. Compraría más buen software para el sistema operativo que uso. Me gusta el software libre pero me molesta que sea difícil imaginar como combinar el cobrar por él y que sea libre. Ya sé que en la teoría se puede, pero en la práctica no parece tan fácil. De todos modos iniciativas como The Humble Indie Bundle parecen una forma de hacer este tipo de cosas. Los juegos ahora los están liberando.

También me gusta escribir software sólo por diversión. Y lo he hecho varias veces. Y muchas cosas las he abandonado. Eso también lo hacen mucho desarrolladores de software libre. Últimamente siento que Linux en el escritorio está en declive. Hay muchas cosas que se han abandonado. Y conozco muchos desarrolladores que se pasaron a Mac. Si hubiera una versión de pago de Ubuntu yo la pagaría si supiera que se está avanzando más.

La web

En estos días hubo una discusión en el blog de Sergio sobre la web. Sergio escribió por qué le gusta la web. Yo iba a responder ahí, pero lo que escribí se me alargó y además quería revivir el blog, así que decidí publicarlo aquí. Primero voy a dar una descripción de lo que, a mi juicio, es la historia de la web. Esto no está precisamente en orden cronológico y puede tener errores si mi memoria ha fallado. Las correcciones son bienvenidas.

La web no fue diseñada para hacer aplicaciones. La web fue diseñada como un medio para compartir documentos conectados por enlaces en Internet. El detalle aquí fue la web se popularizó de una manera increíble, llevando a que se quisiera usar para muchas otras cosas más. El resultado de todo esto es que la web tal y como la conocemos actualmente es sólo una suma de hacks sobre aquel sistema de documentos con enlaces.

Uno de los primeros hacks fue CGI. La web, como el correo electrónico o el FTP, no estaba diseñada para correr programas. Pero a alguien se le ocurrió que sería interesante que se pudiera mostrar contenido creado dinámicamente por un programa. Junto con el CGI vinieron los formularios. Y el sistema de documentos ahora también servía para ingresar información y mandarla a un servidor web, el cual podía responder y generar un documento personalizado acorde a los datos ingresados.

Después vino la explosión de la web. Y el primitivo sistema era cada vez insuficiente para mostrar los contenidos que se querían. Llegaron lo GIFs animados, los MIDIs de fondo, los frames, las tablas para hacer el “layout” de la página, los “blink”, etc. Aquí vino el primer colapso de la web. Resultó que hacer sitios web era un trauma increíble. El código HTML de cualquier página medianamente compleja era horrible. Decidieron que había que cambiar muchas cosas. Crearon CSS para separar el contenido de la presentación, comenzaron las campañas en contra de los frames y las tablas. Pero ahí no terminó todo. Con la web cada vez más popular, ya no queríamos sólo documentos bonitos, ahora queríamos cosas parecidas a las aplicaciones de escritorio: menús desplegables, drag and drop, etc.

En esa carrera por ampliar un centímetro más las capacidades de la web, los navegadores dejaron de ser simples visualizadores y se convirtieron en entornos de ejecución. Aparecieron los lenguajes como Javascript o VBScript. Con esto las páginas, originalmente pensadas para ser estáticas, se volvieron dinámicas. Con Javascript era posible cambiar la página a medida que corre un programa en el navegador. Esta abominación se llama DHTML. Luego, resultó que no era tan útil sólo tener páginas dinámicas, que no pudieran comunicarse con el exterior. Y casi por casualidad, en algún momento de la guerra de los navegadores, a alguien se le ocurrió agregar una función de Javascript que pudiera acceder a un servidor web. Y nació otra abominación: AJAX.

En el lado del servidor las cosas tampoco eran fáciles. El esquema de trabajo de la web era bien simple: el cliente manda una petición de un documento y el servidor la recibe y manda el documento. Se acabó, eso era todo. Pero ahora la web no era para mostrar documentos, sino para ejecutar aplicaciones. Había un problema enorme y era que el esquema de petición-respuesta de la web no tenía un concepto de sesión. ¿Cómo saber que una petición fue realizada por el mismo cliente que antes hizo otra relacionada? Los hacks para que hubieran sesiones en HTTP no se hicieron esperar: nacieron los campos de formulario ocultos, las crípticas largas cadenas HTTP GET con información de la sesión, etc.

Y esta es la historia de la web: hack tras hack, machetazo tras machetazo. La historia del desarrollador web es un continuo batallar por hacer que algo funcione en una forma para lo que no fue diseñado. La web es una mala experiencia para el usuario y para el desarrollador.

Una mala experiencia para el usuario se evidencia en varios sitios. ¿Alguien ha probado YouTube en HTML5? (por cierto HTML5 es el último machetazo de moda) ¡Qué mal que funciona! Por Dios, estamos en 2010, hacer un sencillo reproductor de vídeo debería ser trivial ¿no? Si a los brillantes desarrolladores de Google les cuesta hacer esto, ¿qué podríamos esperar de hacer un editor de vídeo como Adobe Premier? Tocará esperar al 2050. Mis experiencias con Google Docs también son frustrantes. Son programas con características muy simples, más simples que sus equivalentes de escritorio de hace décadas, y llenos de bugs, incluso cuando corren en Chrome.

Hay un aplicación web que me gusta, a pesar de que es bastante simple: Flickr. Pero no dudo de que sus desarrolladores sudaron bastante.

La web también es mala experiencia para los desarrolladores, especialmente en el lado del cliente. Por ejemplo, si en una aplicación de escritorio yo quiero pintar un cuadro, simplemente digo algo como pintar_cuadro(x, y, w, h). En la web, para crear el mismo efecto hay que crear un bloque donde iba a ir un párrafo, sólo que sin ponerle ningún texto adentro. Utilizar CSS para que ese cuadro tenga el tamaño y apariencia deseado, que se ajuste adecuadamente al documento, que no corra el texto a su alrededor. Luego hay que decirle que tenga la propiedad de ser invisible. Y por último, para lograr el efecto “pintar cuadro” hay que hacer que la propiedad del cuadro pase de “invisible” a “visible”. Y eso que no hablamos de los hacks que se tienen que hacer para X y Y navegador. Es por esto que la gente no hace esto a mano, sino que usa herramientas como JQuery o GWT. Pero estas capas hacen las cosas lentas y son difíciles de extender. Por eso, por más que uno tenga un computador potente, la aplicación web funciona como si estuviera en un 286.

Para aplicaciones con interacciones sencillas la web está bien. Pero cualquiera que esté al tanto de las innovaciones en experiencias de usuario e interfaces, sabrá que la web se queda muy corta. Hacer aplicaciones web que tengan una interacción con el usuario medianamente compleja es una labor titánica hoy en día. No conozco ninguna aplicación web que me convenza. Y las que más o menos impresionan, tienen un excesivo trabajo de sus desarrolladores.

Otra cosa: la web sólo permite un lenguaje en el cliente: Javascript. Y según la gente de StackOverflow.com, es uno de los lenguajes con cosas más raras que existen.

Bueno, aparte de rajar. Me gustaría decir que hay cosas de la web que me gustan mucho: La ubicuidad, como mencionaba Sergio. Que chévere es poder acceder a las cosas desde cualquier PC y tener ahí los datos y todo. (Aunque esto a veces puede ser una pesadilla, preguntenle a mi amigo Diego Escalante a quien Google le cancelo sin motivo aparente su cuenta y quedó frito). Que bueno que las aplicaciones sean multiplataforma. Que se actualicen automáticamente, etc. Hay algunas tecnologías que usan las ventajas de la web, con mayor flexibilidad en el desarrollo: un ejemplo es Silverlight o Flash/Flex/Air. Pero no me gusta mucho que detrás de estas estén empresas tratando de ser amo y señor de la web. A mi me gustan los estándares. Pero HTML5 me desilusiona. La W3C debería replantear todo, ya no queremos más hacks sobre lo mismo, sino algo que realmente esté diseñado para lo que debe hacer.

Optimizing a Python Program

These days, I’ve been optimizing a Python program I wrote. Optimizing is a fun task, but very difficult. Most of the time, the first solution I think is even worse than the previous situation. I need more experience.

Some processes were too slow in my program and I realized it was because I was performing too much disk I/O operations. I thought a solution could be read more data in memory and operate there. Now I have excessive memory consumption.

Here is a very simplified description of my memory consumption problem:

I have a text file. Each line in the file represents an item of a large list. Each line has two string values separated by a character. Something like a CSV file. I have to read the file content and put it in a list.

A line in the file looks like this:

Content of the first value|Content of the second value

The separator is '|'

Here is a simple Python program that read the file:

class Field:
    def __init__(self, line):
        self.value1, self.value2 = line.split('|')

fields = []

with open('test_data') as file_:
    for line in file_:
        fields.append(Field(line))

Running this program with a test file of about 42 MB gives this results:

Execution time (time): 0m4.108s
Memory consumed (pmap): 166652K

I was surprised by the high memory usage of the program. If the file is 42MiB, I thought the program should use a similar amount of memory, obviously a higher amount, but not almost four times the size of the file.

An equivalent program in C (error checking is omitted):

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

#define VALUE1_MAX 30
#define VALUE2_MAX 80
#define LINE_SIZE VALUE1_MAX + VALUE2_MAX + 3
#define BUFFER 10000

typedef struct
{
    char value1[VALUE1_MAX+1];
    char value2[VALUE2_MAX+1];
} field;

int main()
{
    FILE *file = fopen("test_data", "r");

    field *fields = (field*) malloc(BUFFER*sizeof(field));
    char line[LINE_SIZE];
    char *part;
    long i=0;
    long size = BUFFER;
    while(fgets(line, LINE_SIZE, file) != NULL) {
        part = strtok(line, "|");
        strcpy(fields[i].value1, part);
        part = strtok(NULL, "|");
        strcpy(fields[i].value2, part);

        i++;
        if (i == size) {
            size += BUFFER;
            fields = (field*) realloc(fields, size*sizeof(field));
        }
    }
    fclose(file);
    free(fields);
    return 0;
}

Results for the C program:

Execution time (time): 0m0.537s
Memory consumed (pmap): 57548K

This is much better.

The problem with the Python program seems to be the Field objects using more memory than they need. Testing the program without the Field creations, changing fields.append(Field(line)) withfields.append(line) seems to perform better:

Execution time (time): 0m0.575s
Memory consumed (pmap): 66808K

Clearly, the Field object is the bottleneck both in memory consumption and execution time. This is probably because of some default memory allocations that Python makes for the object and its fields. Python is a really cool language, but it doesn’t let you control the way the memory is used. This is a positive thing in most of the cases, but in some of them, like this one, is negative.

Most of the times, there are only very small parts of a program that really need to be optimized. And a programmer is much more productive with Python than with C. It doesn’t make sense to rewrite the program in C. Instead, a C module could be written for the bottlenecks.

I was too lazy to learn how to use the Python C API, so I looked a this project called Cython. Cython is a language designed for writing Python extensions. It’s very similar to Python, but is translated to C and compiled to an easy to use Python module. Cython also lets you mix C code and Python code easily. It lets you use high level python objects or low level C data types as you need and mix them properly.

I decided to rewrite the Field class in Cython:

#field.pyx 
DEF VALUE1_MAX = 30
DEF VALUE2_MAX = 80

cdef extern from "string.h": 
    char *strcpy(char *dest, char *src)

cdef class Field: 
    cdef readonly char value1[VALUE1_MAX+1] 
    cdef readonly char value2[VALUE2_MAX+1] 
    def __init__(self, line):
        v1, v2 = line.split('|')
        strcpy(self.value1, v1)
        strcpy(self.value2, v2)

This extension type can be used almost in the same way than a real Python object:

>>> f = Field('Hello|World')
>>> f.value1
'Hello'
>>> f.value2
'World'
>>>

I had to modify the original Python script to use the new module:

from field import Field

fields = []

with open('test_data') as file_:
    for line in file_:
        fields.append(Field(line))

Results of the new program:

Execution time (time): 0m1.257s
Memory consumed (pmap): 69800K

This is a huge improvement. With a very small change, the program now consumes almost 100MB less memory and it runs three times faster. I could write more parts in Cython, using strtok() instead of str.split(), or even rewriting the entire list and reading process. I would probable get a performance very similar to the C program. But I’m comfortable with the results now. I’m still surprised with the small effort compared to the awesome results.

If you want to do your own tests. Here is a simple script to generate a test file with 500k values:

import string
import random

with open('test_data', 'w') as f:
    for i in range(500000):
        value1 = ''.join(random.choice(string.letters)
                         for s in range(random.randint(15, 30)))
        value2 = ''.join(random.choice(string.letters)
                         for s in range(random.randint(50, 80)))
        f.write(value1 + '|' + value2 + '\n')

La Magia de las Piramides

En estos días estaba pensando en que las famosas pirámides o “empresas captadoras de dinero”, como eufemísticamente las llaman algunos medios, dejaron de ser algo que se veía sólo en la televisión, en los noticieros, para convertirse algo de casi todo el mundo. Me puse a pensar y me dí cuenta que de las personas que conozco aquí en Popayán, son más los que han metido plata en las pirámides que aquellos que no. En toda parte se oye hablar de eso: en el bus, en la calle, en la universidad, donde el odontólogo, en el banco. Familiares, amigos y desconocidos hablan de todo el dinero que están ganando con estos sistemas. No se trata de gente poco educada que por ignorancia y ganas de dinero fácil se mete, ahora es normal ver profesores universitarios, con todo y sus títulos de doctorado, “invirtiendo” en estas “empresas”. Lo mismo para los estratos económicos, ahora se ve desde el celador hasta el juez.

Las pirámides siempre argumentan su toque de midas diciendo que lo que pasa es que estamos acostumbrados a que los bancos nos roben y se vuelvan ricos a costa de nuestro dinero. Aunque no soy defensor de los bancos (Como decía mi profesor de derecho laboral, el sistema financiero es la causa de todo mal), estoy convencido que esta justificación es un grave error.

Para tener perspectiva, hagamos un ejemplo: DRFE (Dinero Rápido Fácil y Efectivo o “El Hueco” como le dicen aquí), una de estas “empresas”, pagaba hasta hace unos días 100% del valor invertido en un mes. Esto significa que si yo meto un millón de pesos, en un año puedo reclamar 4.096 millones. ¿Les parece una exageración? No lo es. Lo que pasa es que no estamos acostumbrados a hacer proyecciones exponenciales, y nos pasa lo mismo que le pasó a Rai Bhalit. Expliquémolo paso por paso:

Si me dicen que en un mes me dan el 100% de lo que invertí, significa, en otras palabras, que mi dinero se duplica en un mes. Si meto un millón, al mes recibo dos. Ahora:

Monto inicial: 1 millón.

1° mes: 2 Millones.

2° mes: 4 Millones.

3° mes: 8 Millones.

4° mes: 16 Millones.

5° mes: 32 Millones.

6° mes: 64 Millones.

7° mes: 128 Millones.

8° mes: 256 Millones.

9° mes: 512 Millones.

10° mes: 1.024 Millones.

11° mes: 2.048 Millones.

12° mes: 4.096 Millones.

En resumen: total = monto_inicial * 2 ^ numero_meses

Increíble ¿Verdad?

Ahora, digo que hasta hace unos días esto es lo que estaba ofreciendo DRFE, porque últimamente he escuchado que ahora está dando el 150%. Lo que significa que metiendo un millón, al año puedo reclamar casi 60.000 Millones (total = monto_inicial * 2.5 ^ numero_meses).

Por otro lado, la esperanza de muchos está en otras “empresas captadoras” que son más “moderadas” comoDMG. DMG entrega un 10% mensual o un 100% semestral. Sin embargo, si hacemos cuentas, igual obtenemos resultados mágicos. Con DMG si meto 1 millón de pesos en cuatro años tengo 256 millones. Puedo comprar casa, ¡adiós hipotecas a 30 años!

Este sistema exponencial de reproducir el dinero no puede ser igualado por ningún modelo de negocios. Ni siquiera el narcotráfico puede generar tanto. Ni siquiera Google, una de las empresas que más rápido ha generado dinero en todo el mundo. ¿Cuanto tendría Google en sus diez años si hubiera invertido el monto con el que comenzó en algo como DRFE?

Entonces ¿Cómo diablos es que funcionan estas empresas? Pues es simple, son pirámides. Las pirámides son simplemente mecanismos de flujo de dinero diseñados para quitarle el dinero a muchas personas y dárselo a unas pocas. En elblogboyacense.com tienen una explicación muy sencilla que me voy a permitir copiar:

Imagínense que yo envío un correo a 5 personas, que llamaré “mis amigos”; les indico que sólo han de depositar 10.000 pesos en mi cuenta.

Yo gano 50.000 pesos, y no me costó nada. Mis 5 “amigos” han gastado 10.000 pesos cada uno.

Si aquí se rompe la cadena, yo gané 50.000 pesos sin hacer nada, y 5 “amigos” perdieron 10.000 pesos cada uno.

Si yo les digo a esos 5 “amigos” que cada uno escriba un correo a 5 personas y que repitan la operación, tendremos ya 25 personas involucradas.

Mis 5 “amigos” habrán recibido 50.000 pesos cada uno. Y como se gastaron 10.000 en mi, habrán ganado 40.000 pesos, es decir, el 400% de la inversión que hicieron conmigo.

Si aquí se rompe la cadena, resulta que:

– yo gané 50.000 pesos, sin hacer ningún trabajo productivo – mis 5 amigos ganaron 40.000 pesos cada uno, sin hacer ningún trabajo productivo – los 25 amigos de mis amigos han perdido cada uno 10.000 pesos

Total de ganancias de la cadena: 50.000 + 5*40.000 = 250.000 pesos

Total de pérdidas de la cadena: 25*10.000 = 250.000 pesos

Por tanto, 6 personas (mis 5 amigos y yo) han engañado a 25 personas.

Ese es el esquema tradicional de las pirámides. Es obvio que es algo insostenible y también que al final los perdedores resultan ser un número exponencialmente mayor a los ganadores. Pero entonces ¿Cómo diablos se mantienen estas empresas como DRFE? Según dicen, esta empresa ya lleva 3 años funcionando. DMG también lleva un buen tiempo en el escenario. Tengo una hipótesis al respecto. Creo que estas empresas han perfeccionado el sistema clásico de la pirámide hasta un modelo que dura mucho más y por tanto hace caer a mucha más gente.

En primer lugar, a diferencia del ejemplo de elblogboyacense, en estas empresas no es cada persona la que se encarga de conseguir las otras que servirán de su base en la pirámide, en cambio, de esto se encarga la empresa, lo cual automáticamente genera confianza entre los que entran al sistema.

En segundo lugar, la pirámide se hace mucho más angosta, reduciendo el coeficiente sobre el que se paga. En lugar de pagar 400% como en el ejemplo, se paga 100%, esto hace que para pagar a una persona se necesitan dos en lugar de cinco. Por tanto la pirámide dura mucho más con el mismo número de gente. En el caso de DMG con el 10% la pirámide es mucho más angosta todavía.

En tercer lugar, está lo que he denominado “ganancia virtual vs ganancia real”. Para explicarlo voy a dar un ejemplo: Hace un mes, un amigo mio decidió meter un millón de pesos en DRFE. Por estos días fue a recoger sus ahora dos millones. En vista de lo bueno del negocio, decidió sacar un millón y dejar otro millón para que vaya engordando. Sus cuentas son que en Febrero tendrá 17 Millones. Si lo vemos detenidamente, mi amigo invirtió un millón y al cabo de un mes tiene el mismo millón en sus bolsillos, más otro millón “invertido”. Ese otro millón es “ganancia virtual”. En este caso él no tuvo ganancia real, porque metió un millón y salio con un millón. Por supuesto que uno puede decir que él hubiera podido sacar los dos millones y haber tenido una ganancia real, y es cierto, pero lo relevante aquí es que este patrón, según lo que he observado, es muy común en la mayoría de los inversores. Ellos meten un monto de dinero, al primer ciclo sacan lo invertido o una cantidad menor y dejan las ganancias engordando. Al hacer esto comprueban que la empresa si cumple y además les da confianza pensar que no van a perder mucho si se cae. Mi teoría es que la mayor parte de las ganancias de las que habla la gente son virtuales, es decir, que no han llegado a sus bolsillos realmente y probablemente nunca llegarán. Todo esto en otras palabras, quiere decir que la gente está usando sus propias ganancias para generar nuevos eslabones inferiores de la pirámide, otro factor que influye en que esta dure más.

Lo grave es que no todo el mundo juega con su propio dinero. Hay muchas personas haciendo prestamos al banco y vendiendo todo lo que tienen para meterlo a la pirámide. Los clasificados del periódico están cada vez más llenos de anuncios de gente vendiendo negocios, bienes raíces y carros.

De todos modos, con todos los trucos que han usado estas empresas para alargar la vida de las pirámides, todavía tienen que cumplir con una regla esencial: El crecimiento de la pirámide debe ser exponencial. Esto quiere decir que para que la pirámide se sostenga, cada mes deben tener un numero exponencialmente mayor de nuevos clientes. En el momento que esto se deje de cumplir, el sistema colapsa. Estas empresas han sabido muy bien como cumplir con esta meta. Es por eso que en sólo tres años, DRFE que comenzó sólo en Pasto, ahora ya está en un montón de ciudades. Sin embargo, eventualmente sera imposible seguir con el mismo ritmo. El hecho de necesitar cada vez un numero mayor de personas nuevas concuerda sospechosamente con el patrón de pagos. DRFE comenzó pagando el 20%, luego fue subiendo poco a poco, llegó a 50%, 80%, luego 100% y ahora anuncia 150%. Peligrosamente anchan la pirámide con el objetivo de atraer más gente. Pero en este tipo de pirámides, entre más ancha, más rápido cae. Podría estar cerca la caída de DRFE.

Esto va a terminar mal, muy mal. Los perdedores van a ser demasiados. Probablemente será una de las más grandes estafas que se hayan conocido. Probablemente haya mucha violencia también. Aquí en el Cauca ya se han presentado casos de pequeñas pirámides de barrio, en donde los enfurecidos perdedores han ajusticiado a cuanta persona relacionan con los estafadores. No quiero imaginarme como terminará todo esto con estas súper pirámides.

Hoy cuando iba caminando por la calle, alguien estaba vendiendo un CD con “Un documental sobre El Hueco” el cual compré y resultó ser una entrevista a los dueños de DRFE hecha por un canal de Pasto. Decidí subirlo a YouTube:

Parte 1

Parte 2

Parte 3

Parte 4

Se los dejo a los curiosos.

Update

Datos curiosos:

En vista de que Federico me hablaba por Twitter sobre invertir la plata que nos dio Google por el Summer of Code (US $4.500). Manuel y yo hablabamos sobre algunas de las maravillas que se podrían hacer:

Si la dejo a dos años al 100% tendría: US $75.497’472.000:

  • Sería más rico que Bill Gates
  • Podría pagar la deuda externa de Colombia y de otros países más.

Si la dejo a dos años al 150% tendría: US $15’987.211’554.602:

  • Podría declararle la guerra a un pais del medio oriente.

¿Más ideas?

Update 2

  • Manuel me mandó un texto de Groucho Marx sobre una situación muy similar que se dio hace 80 años en los Estados Unidos y que finalizó en lo que se conoció después como la crisis del 29. Vale la pena leerlo.
  • René me comenta que DRFE tiene un tope máximo de 5 Millones semanales. Es decir que sólo se pueden ganar 240 Millones en un año. ¡Qué miseria!

Summer of Code: Lunar Eclipse Final Report

I had a lot of fun participating in the Google Summer of Code. As I said before, I worked improving Lunar Eclipse, an open source visual designer for Silverlight/Moonlight. This is what I achieved during the summer:

Tools & Handle Support:

I worked on editing support for the following Silverlight Elements:

Circle, Ellipse, Rectangle and Square: These are the basic boxed shapes. Previous version of Lunar Eclipse already had support for this elements. Anyway I almost refactored the entire Handle & Tool subsystem. This helped me to adapt it to more complex shapes like bezier paths.

Line and PolyLine: Simple two point line and multiple point line.

Bezier Path and Pen Tool: I worked on two tools for creating Path figures. One let you create a path using Bezier Segments. This is working fine but there are a couple of bugs regarding translations of points. I also implemented a Pen tool for creating paths based on manual movement, it’s working, but it needs to be optimized to produce less nodes.

TextBox: I have partial TextBox support. I couldn’t implement graphical text editing because the Entry control is not yet implemented in Moonlight.

I also tried to add Image support. But it was impossible to use the Downloader and set media to objects using the GtkSilver widget (used for creating Desktop Moonlight applications).

Other features

Selection Rect – Select All – Clear Selection – Delete Selection: I improved the selection rectangle and all the selection subsystem. This was important in order to implement other operations such as ordering and alignment. The use of keys for selection (like control to add to selection) is not working because the Keyboard class was not implemented in Moonlight at the time.

Property Panel: The properties panel was fixed. I believe that in the future, Lunar Eclipse will use the toolbox system of MonoDevelop, but some of the internal parts of this work, such as property introspection, can be reused in the future.

Infinite Undo Redo: Undo and Redo was implemented in previous versions of Lunar Eclipse, anyway, I fixed a lot of bugs related with this and implemented Undo – Redo support for all tools and operations.

File Open / Save: As simple as that. Files can now be saved and loaded 🙂

Zooming and Scrolling: Zooming is now possible. There are some ugly effects caused by the GtkSilver implementation. I guess this will be fixed in the future. Scrolling is working good but it needs improvements to be more usable.

Ordering and Alignment: Send to Front, Send Backwards, Align Right, etc.

Serialization and Back: Serialization has been fixed. Loading from XAML is working too so you can move back and forth between Xaml and Design. Serialization still need a lot of love. Output is too verbose and the text indentation structure is not ‘remembered’ by the serlializator. I want something similar to the MS Expression Blend’s system, which is awesome.

Copy – Paste – Clone: Clipboard operations. Copy was a bit difficult because Silverlight elements don’t have a clone method. I Implemented my own Clone method based on the serialization work.

New interface: This wasn’t a critical feature, but I rewrote the GTK# based user interface. This was an easy task thanks to the awesome MonoDevelop.

Video Demo:

Here is a demo video showing some of the features of Lunar Eclipse:

http://video.google.com/googleplayer.swf?docid=7961478585740522243&hl=en&fs=true

Video en alta resolución

Future

It was really difficult to work on Lunar Eclipse the last weeks of the Summer of Code. Moonlight hackers started to work heavily on the 2.0 version. Moonlight became very unstable and its API changed a lot, (besides Silverlight 2 Beta 2 API changed too). All these changes broke Lunar Eclipse. The last objective, animation support, was not completed. Anyway, I definitely want to keep working on this project. I decided to freeze Lunar Eclipse while Moonlight gets more stable, but I started to work on adding support for Silverlight projects to MonoDevelop. After that, I want to fix bugs in LE and start isolating the design surface to be easily plugged in applications such as MonoDevelop or a Web Based Lunar Eclipse.

Python vs C#: Queries

One of the most beloved C# 3.0 features is Linq. Linq brings great power to C#, it allows you to easily write structured queries over collections or remote data sources. Now with C# is possible to make queries as easy as with other languages like Python. I decided to compare the way you make queries with C# and with Python. I found a great page showing 101 Linq examples, I decided to write Python versions of this examples. Which version do you like more?

Where – Simple 1

C# version:

int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
var lowNums = from n in numbers where n < 5 select n;

Python version:

numbers = [5, 4, 1, 3, 9, 8, 6, 7, 2, 0]
low_nums = (n for n in numbers if n < 5)

 

Where – Indexed

C# version:

string[] digits = { "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine" };
var shortDigits = digits.Where((digit, index) => digit.Length < index);

Python version:

digits = ['zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine']
short_digits = (digit for index, digit in enumerate(digits) if len(digit) < index)

 

Select – Simple 1

C# version:

var numsPlusOne = from n in numbers select n + 1;

Python version:

nums_plus_one = (n + 1 for n in numbers)

 

Select – Anonymous Types 1

C# version:

string[] words = { "aPPLE", "BlUeBeRrY", "cHeRry" };

var upperLowerWords =
    from w in words
    select new {Upper = w.ToUpper(), Lower = w.ToLower()};

Python version:

The exact Python version would be something like:

words = ['aPPLE', 'BlUeBeRrY', 'cHeRry']

upper_lower_words = ( type('', (), {'upper': w.upper(), 'lower': w.upper() })
                      for w in words)

But I feel more Pythonic this:

upper_lower_words = ( (w.lower(), w.upper()) for w in words)

Or even this:

upper_lower_words = ( {'upper': w.upper(), 'lower': w.upper() }
                      for w in words)

SelectMany – Compound from 1

C# version:

int[] numbersA = { 0, 2, 4, 5, 6, 8, 9 };
int[] numbersB = { 1, 3, 5, 7, 8 };

var pairs =
    from a in numbersA,
         b in numbersB
    where a < b
    select new {a, b};

Python version:

numbersA = [0, 2, 4, 5, 6, 8, 9]
numbersB = [1, 3, 5, 7, 8 ]

pairs = ( (a, b) for a in numbersA 
                 for b in numbersB 
                 if a < b)

SelectMany – from Assignment

C# version:

var orders = from c in customers,
                  o in c.Orders,
                  total = o.Total
             where total >= 2000.0M
             select new {c.CustomerID, o.OrderID, total};

Python version:

I couldn’t find how to make the assignment in Python, so the version is:

orders = ( {'customer_id': c.customer_id,
            'order_id': o.order_id,
            'total': o.total }
           for c in customers
           for o in c.orders
           if o.total > 2000)

SelectMany – Multiple from

C# version:

var orders = from c in customers
             where c.Region == "WA"
             from o in c.Orders
             where o.OrderDate >= cutoffDate
             select new {c.CustomerID, o.OrderID};

Python version:

orders = ( (c.customer_id, o.order_id)
           for c in customers if c.region == 'WA'
           for o in c.orders if o.date >= cutoff_date)

Take Simple

C# version:

var first3Numbers = numbers.Take(3);

Python version:

if we are working with something like a list, we could do:

first_3_numbers = numbers[:3]

but, if we are working with iterators, we must do:

first_3_numbers = itertools.islice(numbers, None, 3)

Skip – Simple

C# version:

var allButFirst4Numbers = numbers.Skip(4);

Python version:

all_but_fist_4_numbers = numbers[4:] # list version all_but_fist_4_numbers = itertools.islice(numbers, 4, None) # iterator version 

TakeWhile – Simple

C# version:

var firstNumbersLessThan6 = numbers.TakeWhile(n => n < 6);

Python version:

fist_numbers_less_that_6 = itertools.takewhile(lambda x: x < 6, numbers)

SkipWhile – Simple

C# version:

var allButFirst3Numbers = numbers.SkipWhile(n => n % 3 != 0);

Python version:

all_but_first_3_numbers = itertools.dropwhile(lambda x: x % 3 != 0, numbers)

First & Last

C# version:

numbers.First()
numbers.Last()

Python version:

numbers[0]  # first for a list numbers[-1] # last for a list 
numbers.next()   # first for iterator list(numbers)[0] # first for iterator 
list(numbers)[-1] # last for iterator 

First – Indexed

C# version:

int evenNum = numbers.First((num, index) => (num % 2 == 0) && (index % 2 == 0));

Python version:

even_num = [n for i, n in enumerate(numbers) if n%2 == 0 and i%2 == 0][0]

or:

even_num = (n for i, n in enumerate(numbers) if n%2 == 0 and i%2 == 0).next()

to be continued…

Segunda GUADEC

A menos que algo extraordinario ocurra, este año asistiré por segunda vez a GUADEC. La primera vez, en Birmingham, la pasé muy bien. Esta vez será en Estambul, lugar que, según he escuchado, es uno de los más bellos en el mundo. Estoy muy emocionado por este viaje. Muchas gracias a la Fundación GNOME por patrocinar gran parte de mis gastos.