Mostrando entradas con la etiqueta programacion. Mostrar todas las entradas
Mostrando entradas con la etiqueta programacion. Mostrar todas las entradas

TuentiChallenge4: Diario de un developer yonki (3): Ejercicios más chungos y conclusiones

¡OJO!: Esta entrada es la última parte de una saga que empieza en:
TuentiChallenge4: Diario de un developer yonki (1)

Los fuentes Java de mis soluciones están disponibles en Github.

Día 7

Querido diario:

Hoy el día ha sido muy largo. O quizá debería decir la noche. Después del bajón del día de ayer, decidí que lo mejor que podía hacer era descansar muchas horas y dedicar la noche a hacer un ejercicio más, ¡pero hacerlo bien!. Así que dediqué la mayor parte del día a descansar y a la tranquila vida familiar.

A última hora de la tarde, encendí el ordenador y le reté a un duelo de miradas. Esta vez iba a darlo todo.

de vergag.com

Entonces me puse con el ejercicio 14, Train Empire, que propone un juego de trenes con combustible limitado, en el que se incluyen rutas, estaciones y vagones con puntuaciones si consiguen alcanzar una estación de destino. Se trata de obtener la máxima puntuación posible a partir de un estado inicial dado. El ejercicio ha confirmado que estamos ya en fase chunga, y hay que darle bastantes vueltas para resolverlo.

He planteado la resolución en dos fases. En la primera, por cada vagón obtengo todas las posibles combinaciones que llevarían a que ese vagón llegara a su destino, sin tener en cuenta la estación en la que comience el tren. En la segunda, pruebo todas las permutaciones de las posibilidades de cada estación, probando también a cambiar el orden en el que se obtendrá cada vagón. Al hacerlo, recorro antes las posibilidades que exploran primero los vagones con más puntuación, porque así es más fácil obtener rápidamente una solución buena. En cuanto una permutación no sea ya capaz de mejorar la mejor puntuación obtenida hasta el momento, se descarta.



Esta prueba de cada permutación tiene una sutileza con la que hay que tener cuidado: al mover un tren anterior, puede que en parte de su camino no necesite llevar ningún vagón con puntos, por lo que cabe la posibilidad de que el tren esté libre para mover un vagón en su recorrido y dejarlo en alguna estación intermedia. Así que en dicho caso hay que ver cuál de esas estaciones va a ser más adelante la más cercana a su destino, y considerarlo para la situación inicial del siguiente tren. Tengo la impresión de que esta sutileza era la gran dificultad del ejercicio, y que puede hacer que mucha gente no resuelva bien el ejercicio.

En el enunciado se dice que no va a haber más de 2 trenes, por lo que seguramente exista alguna solución más sencilla que la mía que tenga en cuenta esto. La mía es genérica, valdría lo mismo para 2 trenes que para más, aunque por supuesto el rendimiento se resentiría mucho en caso de haber muchos trenes.

Al ejecutarlo crucé los dedos para ver si era lo suficientemente rápido, pensando que el hecho de que solo hubiera 2 trenes debería ser suficiente, y efectivamente ha ejecutado la entrega en 10 segundos. Muy satisfecho con mi solución, a pesar de no ser demasiado elegante, llegó el momento que ya había vivido varias veces estos últimos días: las tantas de la noche, agotado, lo normal sería irse a dormir, aunque eso supusiera ya el final del concurso. Pero ains, volví a caer... me he leído el enunciado del siguiente ejercicio y como buen developer yonki... ¡tenía que hacerlo!.


Y tenía que hacerlo porque el ejercicio 15, Take a corner, es... ¡¡¡un Othello!!!, o sea... ¡¡¡un Reversi!!!. ¡¡¡Me encanta el Reversi!!!. ¡Con mi primer ZX Spectrum, hace ya potorrón de siglos, venía de regalo un juego de Reversi!. Pero es que hay más... ¡YO tengo programado un juego de Reversi!. Lo hice hace un montón de años para JavaME, y lo pasé a Android el año pasado.

Mi Reversi, ¡snif!

En el ejercicio se plantea una solución inicial y se pide qué movimiento hay que hacer para que en N jugadas más, haga lo que haga el contrario, se gane una esquina. Mi juego es malo con ansia, así que no cubría esa posibilidad, pero sí he aprovechado para copiar el código de gestión del tablero de Reversi y que obtiene los posibles movimientos a partir de una situación. Una vez teniendo eso, he hecho una sencilla exploración de todas las posibilidades, considerando que cuando le toca mover al jugador inicial basta conque un movimiento sea exitoso, y cuando le toca mover al contrario tienen que serlos todos.

Estaba tan emocionado ante la posibilidad de reutilizar el mismo código por tercera vez (soy un gran fan de la reutilización) que me recreé especialmente en la solución. ¡Y qué demonios!, ¡¡¡se lo iba a contar a mi amigo el revisor de Tuenti!!!

/**
* Dear Tuenti Engineer:
* I've programmed a solution a bit more elegant this time, thinking that this one
* was going to be the last. I'm not 100% sure, although it's almost 6:00 in the
* morning and I think this week has been exhausting enough.
* Thanks for your work and please keep doing things like this.
* I need a bed. Now!
* Good night!
*/


Ahora ya sí, con mi consciencia perdiéndose en ovejillas saltando vallas, ¿qué debería haber hecho?: dormir. ¿Qué he hecho?: pffff, está claro... ¡leerme el siguiente ejercicio!


Y así he llegado al ejercicio 16, ÑAPA. Y, ¡ay!... me volví a picar. El ejercicio se trataba de calcular cuántos círculos intersectan entre un mogollón que te dan (potencialmente millones). La solución obvia era... ¡tan obvia!. Recorrerlos todos entre sí y hacer un sencillo cálculo de colisiones, aplicando para ello el teorema de Pitágoras... sí, ese mismo que habría hecho bien en aplicar en el ya lejano ejercicio 3 en lugar de las malditas interpolac... ¡¡¡AGH, NO QUIERO PENSAR EN ESO!!!.


El caso es que... qué demonios, igual no había trampa. Igual en Tuenti querían premiar a la gente que había llegado hasta aquí y ponerles algo facilón. Las ovejas me decían que no había otra solución posible. Y, bueno, me daba tiempo a probar. Los tests iniciales me funcionaban suficientemente rápido. ¿Qué pasaría con la entrega?. Por supuesto, tenía que compartir esto con mi amigo el revisor de Tuenti:

/**
* Dear Tuenti engineering:
*
* Ouch! I shouldn't have read this challenge... just when I was dreaming on my
* feet and ready to go to bed, I had the terrible idea of reading it. And...
* bad idea, really really bad idea, because it's quite easy! Actually, I think it's
* the easiest challenge!... unless you pretend to process the 8 million points in
* a reasonable time and there's a hidden trick, but I don't think so (maybe
* that's because I'm almost asleep). I'll see it right now. I'm scared. If
* there's a trick, you are very clever, my friend... hmmm maybe "painting" the
* points in a grid with the limited 100x100 space, maybe?, and so iterating
* points in order N -and not N!-, and after that iterating the 100x100 points?...
* yeeks, I'll better not think on it, it's late!.
*
* I'll try as well to NOT reading the next challenge. Seriously. Promised. Or so.
* I don't think I have enough time to solve it, anyway...
*
* P.S.: Ooooooooook, I got it! I have and idea, will I correct it in time???
*
* Sincerely,
* @author andres
*/

Probé y... nada. Se tiraba mogollón de tiempo (ni sé cuánto, pero apuesto a que horas). Como se puede ver al final del comentario, dejé de escuchar a las ovejas y se me ocurrió una solución. Tampoco era demasiado complicado de hacer, aunque el tiempo se me estaba acabando, incluso aunque no durmiera nada.

Se trataría de dividir el espacio, que tiene unos límites definidos (y que no era ni mucho menos 100x100 como pongo en el comentario, prueba inequívoca de que estaba ya en babia), en varias zonas cuadradas. Cada cuadrado se recorre enterito combinando todos los círculos que tenga dentro. Al pasar al siguiente cuadrado, se guardan en un hash las colisiones que se han detectado, porque los mismos dos círculos que colisionan en una zona pueden colisionar también en la siguiente, y no queremos contarla dos veces. Para que el hash no tenga demasiados elementos, calculamos el rectángulo que contiene la intersección de la colisión, y cuando estemos en la zona que contiene la esquina inferior derecha, lo quitamos del conjunto hash. Esto es así porque las zonas las recorría de arriba a abajo y de izquierda a derecha.

Conseguí que la ejecución se hiciera en solo unos segundos, pero al hacerlo el resultado dejó de ser correcto. Creo que la solución que he planteado está muy bien y seguramente sea "la buena", pero he debido hacer algo mal en la implementación. Posiblemente al dividir el espacio en zonas, no sé.

Ya no podía seguir, se me había acabado el tiempo, a pesar de que al final no he dormido nada. Me ha fastidiado no poder entregar correctamente el ejercicio, porque creo que ya casi lo tenía, pero así es la vida...


Conclusiones

El concurso es... mortal. Requiere muchas energías, son demasiados ejercicios, y no son sencillos. Incluso aunque le dediques solo ratos sueltos, si tienes un trabajo exigente y poco tiempo libre, te va a dejar agotado. Mi gran problema, además, es que cuando leía un ejercicio me picaba, independientemente de la hora que fuera. Y si algo he aprendido en este concurso es que cuando llegas a cierto nivel de cansancio y falta de sueño es mejor parar, porque las soluciones que encuentres no van a ser buenas, y te va a costar mucho pensar.

Personalmente preferiría que el concurso tuviera pocos ejercicios y que fueran los más difíciles. Que cada ejercicio que resuelvas tenga su mérito. Solo eso ya es suficiente para hacer un concurso chulo.

Aparte de eso, tengo que decir que el concurso es también muy interesante. Los ejercicios, aparte de algún enunciado ambiguo o no suficientemente definido, están bastante bien, y algunos son realmente divertidos de resolver.


Por otra parte, mi crítica va también a la forma en la que estoy viendo que Tuenti trata el concurso una vez acabado. Mientras estábamos concursando teníamos un ranking con el número de ejercicios que había entregado cada uno, aun sin saber cuántos estaban bien o mal. En cuanto el concurso acabó, el ranking desapareció. Vale que se supone que la puntuación final tiene un componente subjetivo que le dan los revisores de Tuenti a la limpieza de cada solución, pero creo que un simple ranking automático en base al número de ejercicios resueltos por cada uno nos hubiera encantado a todos los participantes. Aunque luego no fuera definitivo y los ganadores no tuvieran por qué ser los primeros puestos de dicho ranking. Creo que basta con dejarlo claro. Es que así es tan... ¡cortarrollos!

Por mi parte, en el ranking provisional de ejercicios entregados sin corregir creo recordar que quedé en el puesto 25. Luego por email me han confirmado que estoy entre los 50 primeros, aunque no haya aún un ranking definitivo (que no tengo muy claro que al final vaya a existir).

No me siento especialmente orgulloso de mi participación, creo que he hecho demasiados ejercicios con demasiado sueño, y quizá debería haberme centrado en hacer menos ejercicios pero con más calma y energías. Pero sí estoy contento con cómo planteé los últimos ejercicios que hice, los que comento en esta tercera parte del mega-artículo. Y oye, quedar entre los 50 primeros está bastante bien.

Pero qué demonios, aun con todo, tengo que decir que lo más importante es que me lo he pasado genial.

de protocoloipv6.blogspot.com

TuentiChallenge4: Diario de un developer yonki (2): la cosa se pone chunga

¡OJO!: Esta entrada es continuación directa de
TuentiChallenge4: Diario de un developer yonki (1)

Los fuentes Java de mis soluciones están disponibles en Github.

Día 4

Hoy me he puesto otro rato con el concursillo de marras, para no variar.

Primero he empezado con el ejercicio 5, Tribblemaker, que se trata de implementar el Juego de la Vida (aunque no lo decían explícitamente, pero se daban pistas más que obvias), y detectar ciclos en la evolución. El ejercicio resultaba bastante entretenido de programar y daba para hacer código ordenadito, así que me he recreado en eso.

El siguiente, el ejercicio 6, Man in the Middle, me ha encantado. Se trataba de crear un servicio que se metiera en medio de la comunicación encriptada entre un cliente y un servidor, basada en claves que se van intercambiando entre ellos.


Para poder resolverlo, se proporciona el código fuente tanto de la parte cliente como de la parte servidor... en Javascript, o sea, NodeJs. Para alguien con conocimientos de seguridad y que sepa NodeJs imagino que el ejercicio estaba chupado. Para los que no, el ejercicio resultaba realmente divertido. Y mira que yo de criptografía ni flowers, nada de nada.

El ejercicio se podía hacer sin problemas en otros lenguajes, pero teniendo ya los fuentes en Javascript, lo normal es que sea mucho más sencillo resolverlo así. De esta forma, se trata de instalar NodeJs, ver cómo funciona, probarlo, revisar ambos códigos fuentes, hacerte tus esquemas de cómo funciona su intercambio de claves, y finalmente programar la solución, que básicamente se hace mezclando el código del cliente y el del servidor. Podría hacerse incluso sin saber Javascript ni nada de criptografía, que es lo que me parece más genial de este ejercicio. La clave en mi caso para que todo resultara más fácil fue crear un objeto diferenciado para el cliente y otro para el servidor, de forma que todo se hace bastante lógico y comprensible. Crecidito por lo bien que se me estaba dando el día, no he parado ahí.

El siguiente ha sido el ejercicio 7, Yes we scan, en el que se trata de encontrar una relación entre dos elementos a partir de un montón de conexiones. Temía que ya tuviera que ir pensando más en temas de rendimiento y algorítmica. Sin embargo, la verdad es que sólo con usar un par de hashes y tener un poco de cuidado con la forma en la que se obtienen nuevas conexiones indirectas por cada conexión nueva, conseguí que la prueba de entrega se resolviera en solo 10 segundos.


Y entonces me he crecido: ¡leche, en cuanto he dormido un poco qué bien me ha ido todo!. Eran ya las 3 y pico de la mañana, así que llegó ese momento que también conocen los jugadores de poker que están en racha: el momento de retirarse y continuar otro día. Por supuesto... ¡yo no lo hice!.

Así que toma porrazo con el ejercicio 8, Tuenti Reestructuration. Se trata de reorganizar posiciones de personas en mesas, basadas en permutaciones, hasta llegar a una situación final. O sea, segundo momento importante de la noche: ese en el que me doy cuenta de que posiblemente me encuentre ante un ejercicio típico de recorrido de caminos posibles, de esos que en algorítmica tienen tan trillados, pero que yo nunca me entero de cómo lo resuelvo. ¡Ains, ya estoy como siempre!

Lo peor es que el enunciado me ha parecido muy confuso, no deja muy claro cuáles son los movimientos posibles, así que estoy perdiendo muchísimo tiempo sólo para enterarme... y ya se está haciendo de día. ¡Mierda, con lo bueno que era el día! A dormir.


Día 5

Esto de dejarse un ejercicio a medias no mola nada, he estado todo el tiempo dándole vueltas al dichoso ejercicio hasta que me he podido poner con él. Por fin estoy seguro de qué movimientos son los válidos, y le he puesto las heurísticas que se me han ido ocurriendo para que no fuera demasiado lento: probar primero el camino que se acerque más a la solución, control para dejar de recorrer un camino cuando ya tengamos uno mejor, detectar estados ya recorridos... total, que entre unas cosas y otras me he tirado un montón de tiempo con el tema. Al final la entrega se ha tirado varios minutos de ejecución, así que imagino que habrá soluciones mejores, pero para no saber nada de algorítmica creo que ha quedado bastante decente. Eso sí, esto está empezando a complicarse de verdad.

Esto lo confirmó el ejercicio 9, Bendito Caos, que tenía tela. Se trataba de averiguar cuántos coches pueden llegar en una hora desde un punto a otro, calculándolo a partir de las velocidades de los caminos entre cada par de vértices. En este caso estoy aún más seguro de que existen algoritmos que implementan esto sin problema. Pero a mi me iba a tocar hacerlo a lo bruto.

Me ha llevado un rato sacar las reglas de mezcla de las velocidades, para saber cómo se puede calcular la velocidad global entre el vértice de inicio y el de fin, combinando las de todos los caminos posibles. También había que tener cuidado con los bucles. Pero sobre todo no tenía ni idea de cuál sería el rendimiento de mi sencilla solución. En el ejemplo iba rápido, pero el enunciado de este ejercicio no era, la verdad, demasiado bueno. No sólo la explicación era un poco confusa (no tanto como el anterior pero casi), sino que sobre todo, no dejaba claro en ningún momento cuáles eran los límites en número de nodos y de caminos. ¡Tercer WTF del concurso!

Yo, siempre optimista, lo interpreté como que eso sería que no le iban a meter mucha caña. Gran error. Lo puse a ejecutar y eso seguía y seguía y no acababa. ¿Qué pasaría si lo cortaba a medias e intentaba arreglarlo?. Hice la prueba y vi que aparentemente se podía. Hice un cambio en la línea de comando para obtener más información y lo volví a ejecutar. Grandísima cagada. Me equivoqué al poner la línea de comando, así que lo ejecuté de forma incorrecta, pero la entrega se consideró válida.

Por culpa del ejercicio casi llego tarde al cine, donde pude desengrasar un poco mis pobres neuronas viendo a Spider-man pegando tortazos a troche y moche. Mano de santo, oiga, que el dolor de cabeza que se me había quedado hoy era tremendo.

Spidey sí que sabe cómo hay que enfrentarse a los Challenges del Tuenti
¡¡¡A tortazos!!!

Al volver a casa le he echado un vistazo al siguiente ejercicio, con la firme intención de no ponerme con él. Y ha resultado ser un WTF con todas las de la ley. A ver si mañana entiendo algo, porque hoy lo veo realmente negro.


Día 6

El ejercicio 10, Random Password, es tan tan tan WTF, que el enunciado apenas dice: 

It seems to be a random password... how could it be?
Get the key for your input. Start at: http://random.contest.tuenti.net/?input=INPUT

¡¡¡¡¿¿¿Eeeeeeeeeeeeeeeeeeeeeeeeeeeeh???!!!! Espera que lo lea otra vez... sí. Ya veo. Entendido. Entendido que no entiendo nada, quiero decir. Probé a mirar el código fuente del servidor y poco más se me ocurrió. Como ya he dicho, no tengo ni idea sobre seguridad, que es de lo que obviamente trataba esto, así que tras estar un buen rato dándole vueltas, lo descarté.

El ejercicio 11, Pheasant, insiste en la seguridad. ¡Otra vez!. Aunque en este caso parece que algo se puede hacer sin saber nada de encriptación. Se trata de averiguar una clave AES en la que faltan 3 posiciones, a partir de datos conocidos. Lo único que se me ha ocurrido es probar todas las combinaciones en esas 3 posiciones hasta dar con la buena. En lugar de hacerlo con el fichero entero, creo que lo importante es hacerlo solo con los 32 primeros bytes, que he visto que era suficiente en cualquier caso según los límites dados, y que agilizaba muchísimo el proceso. Al hacer la entrega he visto que se ha tirado bastantes minutos, pero sin conocer cómo funciona el AES creo que era imposible hacer nada mejor. También sospecho que la implementación de AES que he usado, que es la que viene por defecto en Java (JCE), debe ser bastante lenta.

El ejercicio 12, Taxi Driver, volvía a ser algorítmico. Es más, en el fondo el problema era muy muy similar al número 8, el de la reestructuración de mesas que tanto tiempo me llevó y que no me quedó demasiado rápido, y que seguro que conociendo los algoritmos típicos se hace en un momentín... ¡¡¡Otra vez el mismo problema de siempre, el que he hecho millones de veces y nunca bien!!!.


He entrado en tal estado de enajenación que he decidido comunicarle mi frustración a un posible corrector. Así que he puesto este comentario en el código, con un par:

/**
* NOTE
* ===============
*
* Dear Tuenti Engineer:
*
* Oh, no, the same problem... again! This is almost the same as challenge 8!
* I'm pretty tired of doing the same problem, and not finding a really GREAT
* solution to avoid loops in a proper way and find good heuristics (I could
* swear I've found it also other times in my past... in Google Code Jam, maybe?).
*
* This time, I've decided to program a reusable solution, in the form of this
* PathFinder class. It's a pity I can't use it for challenge 8 yet, but I'm
* in the hope that I can find this same problem again. Call it a longshot if
* you may.
*
* I'm losing some precious minutes in this, but I'm sure you will appreciate
* it. Enjoy it, my friend.
*
* @author andres
*
*/

He hecho una preciosa clase PathFinder capaz de resolver todos estos problemas similares. Como ya he dicho, la falta de sueño y el dolor de cabeza que me habían causado los problemillas me han hecho entrar en un grave estado de enajenación. Y tanto, no sabía hasta qué punto...

El caso es que al ejecutar la entrega, sintiéndome en pleno deja vú, veo que eso sigue ejecutando... y sigue ejecutando... y sigue ejecutando... y no para. Mato el proceso, cambio la línea de comando, vuelvo a ejecutar y... adivina.

¡¡¡Sí!!!. ¡¡¡Vuelvo a equivocarme al modificar la línea de comando!!!.

Otra entrega a freír espárragos. Para una vez que me pongo a hacer una clase más genérica, primero la hago demasiado lenta y luego la dejo a medias por pura torpeza... qué grande.

Ejercicio 13, Tuenti Timing Auth.Con los ojos ardiendo en fuego, miro el siguiente ejercicio, con la esperanza de que sirviera para redimirme. ¿Y qué encuentro?.

¡¡¡Otro ejercicio de seguridad, similar al otro que me salté!!!
(en este caso sobre "side channels", tema sobre el que sé lo mismo que el anterior, o sea, nada).

No me lo podía creer.

Tras darle vueltas al tema la correspondiente ración de tiempo inútil, desistí.
¡¡¡¡¡WTF doble, diablos!!!!!

Al rato me levanté, tembloroso, casi sin fuerzas. En la espuma a mis pies yacían dos mundos muertos. Con mi propósito casi olvidado tras el torbellino de violencia, miré estúpidamente a la pantalla. Al poco, recuperé mi raciocinio. Buscaba venganza. ¿Podía aprovechar de alguna forma las circunstancias?...



¡OJO!
: Esta entrada continúa en:





TuentiChallenge4: Diario de un developer yonki (1)

Los fuentes Java de mis soluciones están disponibles en Github:
https://github.com/andresviedma/tuentichallenge4

Día 0

Querido diario:

Hoy he decidido que voy a presentarme al concursillo de programación que organiza Tuenti. Ya que no pude presentarme al Code Jam de Google como otros años porque estaba de viaje, voy a intentarlo con uno más patrio. No sé ni cómo va, y la verdad es que entre el curro y el finde que me he pegado de viajes y de dormir poco estoy bastante cansado. Pero pone que dura una semana y está el puente, así que seguro que hay tiempo de sobra para resolver los problemas que te pongan. ¡¡¡Esto lo gano fijo!!!.



Como voy a andar bien de tiempo creo que voy a hacerlo en algún otro lenguaje que no sea Java. ¿Qué tal en Dart, ya que le estoy dando caña últimamente?. O si no podría hacerlos en Groovy... bueno, bah, ya lo pienso cuando me ponga con ello.

¿Serán algorítmicos como los de Google?. Igual debería mirarme cosas de algorítmica, lo del backtracking, los A estrella, recorridos de grafos y todas esas mandangas, que lo di en la Facultad hace un mollón de años pero no me acuerdo de nada. Y siempre estoy igual, que oye, molaría poner cara de molón y decir: "¡Voy a resolverlo en un momentín con el algoritmo Magicflush!". O igual debería mirarme los problemas que han puesto otros años para hacerme una idea de cómo van... aunque bueno, seguro que estos serán facilones. Va, aunque sean las 8 de la tarde me voy a dormir una siesta y luego lo pienso.


Día 1

Querido diario:

Ayer al final también dormí muy poco, me quedé dormido cuando sonó el despertador de la siesta porque estaba baldado, y luego me desperté a la 1 o así y ya no me pude volver a dormir hasta casi por la mañana. Necesito un fin de semana para recuperarme del fin de semana. Menos mal que viene el puente y podré descansar.

Ya empezó el concursito de marras. Cuando he vuelto del teatro, aunque era ya tarde y yo estaba cansado, he mirado el primer ejercicio para ver qué pinta tienen los ejercicios. ¡Había 1582 personas registradas! En fin, igual no va a ser esto tan fácil.

Por lo que he visto, hay que ejecutar unas herramientas que te pasan para ejecutar una prueba corta y ver si vas bien, y luego otra más fuerte para enviarlo, que sólo se puede hacer una vez. Cuando fui a programar en Dart, me di cuenta de que iba a tener que usar Futures para toda la entrada / salida, y me dio tal pereza solo de pensarlo que al final pasé. Descarté también Groovy, por la pereza que me daba ya solo meterme cuando realmente no tengo fluidez ninguna con el lenguaje (estoy empezando a pensar que esto de programar con sueño no es bueno). ¡Qué demonios, voy a tirar con Java y así empiezo de una vez!.

El ejercicio 1, Anonymous Poll, es bastante sencillote, no tiene mucha ciencia. En estos concursos muchas veces la gente programa las cosas con gran economía de líneas, usando arrays a tope y con un número mínimo de funciones. Como a mi eso no me va mucho y además dicen que para el concurso se valorará la calidad del código, he optado por organizar los ejercicios con sus clases, sus métodos, etc., quedará todo más claro aunque también más largo.


El ejercicio 2, F1 Bird's Eye Circuit, se trataba de recibir en una sola línea el diseño de un circuito, con sus rectas y curvas, y ensamblarlo y pintarlo ya en dos dimensiones. A pesar de que era bien tarde pensé que era facilón y lo podía hacer en un momentín. Una vez terminado de programar me he dado cuenta de una cosa: tenía trampa. Al leerlo la primera vez me pareció que el primer caracter de la cadena se suponía que estaba mirando hacia la derecha. Pero al releerlo me he dado cuenta de que no, que el que se supone que está mirando a la derecha es un caracter '#' que hay por ahí, y que se supone que es la salida. ¡En el fondo fue una suerte que la haya releído, porque en el programa de la prueba daba la casualidad que todos los casos de prueba salían correctos!.

¡Primer What The Fuck!.



Si hubiera estado más despierto probablemente hubiera optado por comenzar a leer la cadena por la posición del caracter '#' y santas pascuas. Pero no, claro, he hecho algo mucho más bizarro: cuando llega el caracter # miro en qué dirección se supone que estoy mirando y la guardo en una variable, y luego ¡giro la pista al pintarla!, cambiando unos caracteres por otros según si miramos en horizontal o en vertical, o según cómo queden las curvas al girarlas. Un poco infernal, la verdad, con la tontería me han dado las 4 de la mañana. Estoy roto. A dormir.


Día 2

Querido diariozzz:

Lo volví a hacer. Después de la charla de @adelatorrefoss en MadriAgil y de ver el fútbol, por fin, a las tantas, me he vuelto a poner con el concursillo. No sé cuántos ejercicios hay, pero esto no parece tener fin.

El ejercicio 3, The Gambler's Club, es, es, es... ¡ey, es Guybrush Threepwood!, ¡¡¡Monkey Island!!!.


Está bien, ¡TENÍA que hacerlo!. ¡La de tiempo que habré pasado yo con los monkeys!. ¡Y seguro que un ingeniero amante del Monkey Island sería un genio que habría ideado un reto divertidísimo!...

Pues no.

Resulta que una vez leído te dabas cuenta de que se trataba de, a partir de un conjunto de datos, obtener la ecuación con la que se obtienen. Si conoces algún programa matemático, esto se resuelve en un pis-pas. YO NO. ¡Llevo sin usar un programa matemático desde la Universidad, hace cuatro o cinco millones de años! (redondeando).

Hice un programa que obtuviera los datos de la web y me puse a buscar en Google. Los ojos se me cerraban, pero ey! ¡en cuanto lo encontrara podría irme a dormir!. Rosario en mano y cantándole a los angelitos, probé con Excel. ¡Meeeeeeec!. Nones. Ni conseguí sacar ecuaciones con dos variables, ni conseguí sacar una ecuación convincente con una variable dejando la otra fija. Probé entonces con Wolphram Alpha, que sabía que se podían hacer cosas de este estilo. Al ir a meter los datos, veo que la cajica está deshabilitada y tiene un bonito botoncico donde pone "Subscribe to Pro". La madre que los parió. O dicho de otra forma, ¡segundo WTF!.

Buscando, buscando, buscando, vi que la cosa estaba chunga. Y a estas horas daba una pereza tremenda ponerse a instalar nada para probar programitas matemáticos, y encima en Linux. Viendo la curva en Excel, daba la impresión de que la ecuación tenía que ser polinomial, y me extrañaba que Excel no la sacara. ¡A ver si es que había que hacer una interpolación!. Dicho y hecho, me copié una clase que encontré por ahí que hacía una interpolación trilineal y ahí que la metí. Con un par. Porque yo lo valgo. Y encima funcionaba. Qué grande soy. Pues a tomar viento, la envío y fuera.

Claro, que en cuanto la he enviado me he dado cuenta de que eso no iba a funcionar ni de coña. Te avisaban de que el programa de prueba usaba números menores o iguales a 30, que eran justo los de la muestra de datos. Y claro, en cualquier interpolación, por chunga que sea, si le pides uno de los puntos de la muestra te lo va a devolver con una precisión perfecta. Por otra parte, también te avisaban de que en los datos de prueba que se ejecutaban al enviar, se usaban todos números mayores, los de muestra llegaban solo hasta 30, y los de la prueba final llegaban hasta mil y pico. O sea, que la interpolación a hacer gárgaras. ¿Para qué demonios sirve una interpolación si todos los datos están tan alejados de la muestra?.

Deprimido al darme cuenta de lo ocurrido y después de echar un trago del peor orujo que tenía como penitencia, he decidido que no podía acostarme así.

Así que he pasado al ejercicio 4, Shape shifters, y ¡oooooooooh, es Mística!. ¡Un ingeniero amante de Mística sería un genio que habría ideado un reto divertidísimo!... obviamente.



Se trataba de tener una cadena de caracteres de inicio y otra de fin, y un conjunto de estados intermedios posibles, y de crear transiciones de estados cambiando un caracter hasta llegar al objetivo. Con un recorrido de todas las posibilidades de transiciones y un control para no pasar por estados repetidos se resolvía con cierta facilidad, aunque ya me imagino que no será lo óptimo. Son casi las 5 de la mañana pero ¡entregado!. Y veo que han puesto un "top" y aparezco ya en él en el hipotético puesto 100, absolutamente falso porque no cuenta si los ejercicios están bien o mal, solo los que se han entregado. ¡Y hay un tío que ya tiene hechos 15! Para mi que la gente no trabaja...

En cualquier caso... ¡ay!, no me quito de la cabeza al puñetero Guybrush, la madre que lo engendró... ¡y el resto de su familia, obtenida a partir de estúpidas interpolaciones trilineales!.


Día 3

Queridozzz diariozzz:

Son las 12 de la noche y acabo de volver del entrenamiento de basket. Esta mañana he tenido que madrugar un poco más porque venían a revisarme la caldera, así que creo que estoy empezando a sentirme mucho más cercano a los zombis (más majos ellos!).

Viendo las máquinas que hay en el puñetero concurso, creo que voy a ver si presento un par de ejercicios más, que... ¡diablos!, ¡¡¡he mirado y resulta que hay 20 ejercicios!!!. Y a 2 por día no me va a dar tiempo aunque, Guybrush aparte, por suerte no son excesivamente complicados.

Pero primero voy a tirarme un rato en el sofá a relajarzzzzzzzzzzzZZZZZZZZZZzzzzzzzzzzZZZZzzz


¡OJO!: Esta entrada continúa en:





Historia de una promise (1): el modelo de concurrencia con ejecución asíncrona

Como ya habréis podido comprobar, últimamente estoy jugando mucho con el lenguaje Dart. Una de las cosas a las que más me ha costado acostumbrarme es al modelo de concurrencia basado en la ejecución asíncrona (o "basado en eventos"), heredado de Node.js. En Dart los elementos que se usan para esto se llaman futures o promises, patrones que no son realmente nuevos, aunque no ha sido hasta los últimos años que se han puesto de moda. Para alguien como yo acostumbrado al mundo Java y la concurrencia multithread, esto supone todo un cambio de enfoque en la programación, especialmente cuando programo la parte del servidor.

Yendo a las raíces de la asincronía, hoy nos preguntamos... ¿cómo se ha llegado a esto?. ¿Realmente es eficiente este modelo en el lado del servidor?. ¿Qué modelo de concurrencia es mejor?. Y en cualquier caso, ¿qué ventajas tiene cada uno?.

Si tú también te preguntas todo esto, sigue leyendo...


De Geek and Poke



Concurrencia multithread

Antes de la irrupción de Java, cuando se quería desarrollar un servidor que respondiera a algún protocolo de comunicación como por ejemplo RPC, lo más usual era programarlo en C ó C++. Habitualmente, se gestionaba la concurrencia con distintos threads de ejecución (o incluso procesos), y cada programador tenía que hacerse su propio pool de threads y su propia gestión de los mismos. Para controlar el acceso a la memoria compartida entre ellos y que no se pisaran unos a otros, lo más habitual era usar semáforos, aunque existían otras soluciones.

Entonces apareció Java, y encontró un filón en la programación para el servidor. No olvidemos que en C/C++ si se accedía a un puntero null o sin asignar, el servidor entero se caía. Y que cada vez que se reservaba memoria luego había que liberarla explícitamente, con la consiguiente alta probabilidad de memory leaks. Nada de excepciones en el log y nada de garbage collector, comodidades a las que ya nos hemos acostumbrado con cualquier lenguaje y ¡ay, como nos las quiten!.

En cuanto a la concurrencia, Java también añadía facilidades para el control del acceso a memoria compartida, gracias a la palabra clave synchronized, que permite que bloques de código no puedan ejecutarse por varios threads simultáneamente de una forma más intuitiva que con los semáforos.

A pesar de todo esto, programar un acceso concurrente mediante varios hilos a un objeto compartido por todo el servidor, algo muy necesario cuando se quiere cachear información de acceso frecuente, sigue siendo realmente peligroso.

Muchos novatos (o no tan novatos) programarían una información cacheada en un Map que forme parte de un objeto compartido así:

private Map<String, Elephant> elephantCache = null;

public Elephant getElephantByCode(String code) {
   this.loadElephants();
   return this.elephantCache.get(code);
}

private void loadElephants() {
   if (this.elephantCache == null) {
      this.elephantCache = new HashMap<String, Elephant>();
      datos = sqlElephantQuery();
      fillElephantCache(datos);
      log.debug("Elephant cache filled");
   }
   log.debug("Done");
}


¿El problema?. No hay control ninguno del acceso concurrente. Un hilo puede estar intentando meter elementos en el Map de la caché mientras otro intenta acceder a ellos. Lo peor de todo es que lo haces, lo pruebas y... funciona. Y de repente un día tu web empieza a hacer cosas raras. Como poco un error en uno de los hilos, o los dos, y con suerte el objeto se queda incluso en un estado estable.

Pero puede ser mucho peor. En algunas versiones de Java, incluso, puedo prometer que algo tan tonto como esto puede causar un bucle infinito, lo cual puede llegar a originar, con cierta facilidad, que los hilos empiecen a bloquearse y finalmente tu servidor se muera, llevándose junto a tu aplicación cualquier otra que pueda estar instalada en el servidor de aplicaciones. Y a ver entonces quién es el guapo que encuentra el error, que según las circunstancias de la instalación se puede haber convertido incluso en inencontrable...


Concurrencia asíncrona

Saltamos ahora al JavaScript, para explicar cómo se ha popularizado otro modelo completamente distinto.

Por motivos de seguridad y control, o por simplificar cosas que no necesitaban ser más complejas, JavaScript no permite ejecución multithread con memoria compartida. Sólo un hilo de código propio estará en ejecución.

Al principio ni falta que hacía más, el código prácticamente no podía hacer mucho más que manipular el DOM y los formularios. Si había varios eventos, se van encolando todos y procesando uno detrás del otro, nunca varios a la vez.

Luego llegó Ajax, y con él la posibilidad de hacer llamadas HTTP al servidor desde el propio código JavaScript, para luego manipular el resultado. Como sólo hay un hilo de ejecución, no nos conviene que se quede bloqueado esperando el resultado de la llamada HTTP, que obviamente puede ser lenta, como sí se haría en el modelo de ejecución de Java. Lo que nos conviene es que una vez lanzada la petición HTTP, sigamos procesando los siguientes eventos que pueda haber pendientes, y procesemos la respuesta ya cuando se reciba el evento y le toque dentro de la cola.

El tema se fue complicando cada vez más. La programación en el se ha ido haciendo cada vez más ambiciosa, moviendo al cliente buena parte de la lógica de negocio. Pero sobre todo, un día alguien pensó: ¿por qué no hacer un modelo de servidor basado en JavaScript, que mantenga el mismo lenguaje pero consiga ser concurrente y eficiente sin la problemática de la ejecución multithread?. Y entonces apareció Node.js, y JavaScript pasó al servidor.

Hay que considerar que en el servidor se usa este mismo sistema de parar la ejecución de un evento y pasar al siguiente cuando se accede a un fichero, o se llama a una base de datos, o se comunica con otro proceso... o sea, para un montón de cosas. Y que mientras que en el cliente se entiende que sólo va a haber una persona interactuando, en el servidor nos pueden llegar muchísimas peticiones en cuestión de segundos. Cada petición HTTP que recibamos se considera un evento, así que no pasaremos a la siguiente hasta que la actual no haya acabado o haga un acceso de entrada / salida. La concurrencia, por tanto, no se produce nunca en nuestro código, sino entre nuestro código y las peticiones de E/S. Es una concurrencia "falsa", en cierto modo. En el fondo se vuelve al modelo de multitarea de Windows 3.1 (y ya sabemos cómo acabó aquello... aunque, bueno, ¡aquello era un sistema operativo!).

Así que la pregunta es: ¿realmente este modelo funciona para una ejecución en servidor?.


Funcionamiento de los dos modelos y comparativa

Vamos a aprovechar que tengo aquí a los hermanos Marx y les voy a pedir que me ayuden. Groucho (hablando en azul) y Chico (en rojo) quieren hablar a la vez pero no pueden pronunciar ningún sonido al mismo tiempo (olvidad a Harpo, porque... bueno, es mudo, y además vive en su mundo). Supongamos que cada palabra es una ejecución de código de procesamiento puro: operaciones matemáticas, procesamiento de imágenes, gestión de colecciones en memoria, lo que sea; y que las pausas entre cada palabra son, por ejemplo, accesos a una base de datos. El modelo multithread sería así:

Concurrencia multi-thread

Aparentemente los dos están hablando al mismo tiempo, aunque en realidad la ejecución está pasando de uno a otro continuamente. El modelo asíncrono, por otra parte, sería así:

Concurrencia con ejecución asíncrona

Como se puede observar, en este caso se ejecuta todo el procesamiento de Groucho un tirón hasta que se llega a un acceso a base de datos (una pausa entre palabras), y mientras se espera a que la base de datos responda, se libera a Chico, hasta que él también llegue a otro acceso a base de datos.

¿Este modelo funciona, es eficiente?. Pues... depende.

Supongamos ahora que Groucho suelta su irrefrenable lengua y se pone a hablar y hablar sin hacer pausas entre palabras:

Atasco en la concurrencia asíncrona

O dicho de otra forma, supongamos que tenemos una ejecución que requiere mucho procesamiento pero no tiene apenas entrada/salida, es decir acceso a procesos o dispositivos externos como pueda ser los ficheros o una base de datos. Hasta que Groucho no termine su procesamiento, Chico se queda en espera sin poder hacer su parte, con lo que se causa un atasco absolutamente innecesario y que nos llevaría a una situación potencialmente muy ineficiente.

En el lado positivo, sin embargo, para ejecuciones cortas es de esperar que se ahorre algo de tiempo por no tener que cambiar de contexto entre los threads, y por no tener que controlar la concurrencia en la memoria compartida.

Buscando un poco se pueden encontrar un montón de comparativas de rendimiento entre los dos modelos. Pero... como podréis suponer, el resultado depende enormemente del tipo de procesos que se estén ejecutando. Es tan fácil hacer trampa con los datos para concluir que el de Node.js es el modelo más eficiente... tan tentador cuando Node.js es tan "cool" y aumenta tanto tu molómetro... hay tanta diferencia entre hacer la prueba con un tipo de procesamiento y con otro... Al final lo que parece que está ocurriendo (como impresión puramente personal) es que muchos piensan que el modelo de Node.js es más rápido y más escalable sólo porque "lo han oído por ahí".

Mi conclusión principal en cuanto al rendimiento, sin haber hecho yo ninguna prueba real y basándome sólo en la lógica, es que cada modelo será más o menos adecuado según el tipo de aplicación. Si no se hacen procesamientos pesados (pudiendo considerar "pesadas" ejecuciones incluso de sólo unas pocas centésimas de segundo), creo que el modelo asíncrono sí debería funcionar bastante bien. Al fin y al cabo, la mayor parte de las webs actuales dedican la mayor parte del tiempo en servidor a acceder a la base de datos, más que a hacer cálculos o manipulaciones costosas de memoria. Y cuando lo hacen, normalmente es en funcionalidades muy muy localizadas.

Aparte de con los procesamientos "pesados", es importante tener muchísimo cuidado con no programar ninguna situación que pueda llevar a un bucle infinito, porque eso podría llevar a que se bloqueara por completo el servidor. Y a Chico le podría dar un patatús con tanta espera.


Necesita un modelo complementario

En cualquier caso, en mi opinión no basta con eso. Además de usar el modelo asíncrono de forma general, sí queremos hacer aplicaciones de cierto tamaño pienso que el lenguaje nos debe proporcionar otro modelo de concurrencia con el que podamos crear procedimientos que sí se puedan ejecutar sin bloquear a los demás, aunque sea algo puntual y sólo para determinados tipos de procesos. Primero para que podamos aislar de esta forma procesos que podamos tener con uso intensivo del procesador, sin que bloqueen a los demás. Y si no tenemos aún procesos intensivos, porque las aplicaciones crecen, y en cualquier momento, cuando menos lo esperemos, es muy probable que nos vaya a hacer falta alguno.

Aparte de eso, hay otro motivo: los procesadores actuales tienen varios núcleos, cada vez más, y por tanto realmente que son capaces de ejecutar varias cosas a la vez. Vale que siempre podemos ejecutar varios procesos de nuestro servidor dentro de la misma máquina y balancearlos, pero aun con eso todo lo que podamos hacer para facilitar el aprovechamiento de los diferentes núcleos dentro de la misma instancia va a ser bueno.

Otro tema es cómo sea ese mecanismo alternativo que nos pueda proporcionar el lenguaje, y que no tiene por qué tener memoria compartida como en el caso de los threads, con los problemas y los quebraderos de cabeza que hemos visto antes, ya que al fin y al cabo es lo que estamos intentando evitar con todo esto.

El caso es que todos los lenguajes con ejecución asincronía dan una solución a esto, y todas son muy similares. Básicamente lo que permiten crear son elementos de ejecución en paralelo que tienen su propio espacio de memoria, y que se comunican entre sí mediante paso de mensajes. En JavaScript en el cliente (HTML5) existen los webworkers. En Node.js son subprocesos.

En Dart estos elementos reciben el nombre de isolates, y por definición no se indica cómo se implementan en el servidor, si con subprocesos o con threads. La diferencia entre hacerlo con uno u otro, al ser memoria independiente, no afecta a la forma de programarlos, pero sí al rendimiento. Crear un subproceso es bastante más costoso que un thread, y la comunicación entre procesos también es más lenta.

Lo curioso del caso es que los isolates se iban a implementar originalmente en Node.js, pero finalmente lo descartaron. Al parecer probaron a hacerlo pero esto causó inestabilidad en el servidor, así que decidieron pasar y quedarse sólo con los subprocesos. Más allá de que se me quedan un poco los ojos como platos con el tema, el acercamiento de Dart de crear la semántica de los isolates sin indicar cómo se implementan internamente parece prudente porque se cura en salud "por si acaso".


Conclusiones

Diablos, no me cae bien Node.js. Es hipster, sorprendentemente se ha puesto de moda y parece que se ha exacerbado de forma absurda. ¿Programar en JavaScript también en el servidor?. ¿Como si no tuviéramos bastante con el cliente?. ¡Uf!

Sin embargo, tengo que reconocer que sí me gusta su modelo de ejecución asíncrona. Yo ya estoy curtido con los threads y no tengo ningún problema en utilizarlos, pero reconozco que cada vez que veo a un novato creando código con objetos compartidos en memoria tiemblo tanto que al final le tengo que poner una velita a la Santa Virgen de los Javitos Desamparados.

Imagino que en el fondo lo que me pasa es que por mucho que no me caiga bien Node.js, odio mucho más la programación concurrente. Tantos ConcurrentModificationException, tantos servidores fritos, tantos días dedicados a perseguir errores imposibles, tantos WTF! al revisar código concurrente hecho por otros... (como ya sabemos, seamos quien seamos los WTF siempre son con el código de los demás, nunca con el nuestro propio!).

Salvo necesidad perentoria o extorsión, no pienso ponerme a programar en Node.js. Pero al final este mismo modelo de ejecución es el que ha heredado el lenguaje Dart. Y como ya comenté hace tiempo, Dart me parece un grandísimo avance sobre JavaScript.

En definitiva, el modelo me parece una buena alternativa. Principalmente, porque soluciona problemas antes de que ocurran. Y mientras se tenga un poco de cuidado en no poner ejecuciones con mucho procesamiento matemático o en memoria, no sé si el rendimiento será un poco mejor o un poco peor que en el caso de los threads, pero estoy seguro de que en cualquier caso será más que suficiente.

Como tantas y tantas cosas en la vida, esto también tiene un precio. Y es que aunque elimine los errores de concurrencia, programar el código con este modelo puede llegar a ser un pequeño dolor de cabeza, y requiere acostumbrarse a ello. Pero de eso ya hablaré en la segunda parte del artículo... pronto en su kiosko o ultramarinos más cercano.


Proyecto Picross: haciendo un videojuego HTML5 en Dart

Ya lo dije en mi anterior entrada sobre el lenguaje Dart: Dart está muy bien, puede suponer una revolución en la web y su futuro potencialmente es muy prometedor. Pero claro, una cosa es el posible futuro y otra es el presente. ¿Cuál es realmente el estado actual de la plataforma Dart?.

Por otra parte, otra tecnología que me llama la atención es la de los videojuegos HTML5. Sobre todo para videojuegos 2d, sencillos, que aparentemente ya deberían poder hacerse en la web. Con la gran ventaja de ser multiplataforma, que se puedan jugar en Android, en iOS, o hasta desde el Facebook, y en este último caso sin tener que instalar nada. ¿Están estas tecnologías suficientemente maduras?.



Con ánimo de comprobarlo y a la vez divertirme, me lié la manta a la cabeza, uní las dos inquietudes y me puse a hacer ¡un videojuego en Dart!. Y aquí cuento todo el proceso, que duró sólo una semana, pero que me dio para asistir a la Gameme5  e incluso participar en la PiWeek.


La GameMe5

Yo de videojuegos en HTML5, la verdad, ni idea. Afortunadamente, estaba apuntado a la GameMe5, un evento organizado por HTML5 Spain y Wimi5 en el que ofrecían un taller de programación de videojuegos en Javascript el Viernes y varias charlas el Sábado.

El taller lo daban Carlos Benítez (@EtnasSoft) y Sergio Ruiz (@serginator), y en él fueron explorando un juego que se habían currado aposta y que era muy muy completito, desarrollado enteramente en Javascript a pelo y sin librerías.

Fueron avanzando poco a poco en el desarrollo y cubrieron muy bien muchos aspectos del desarrollo de videojuegos en general y de Javascript en particular, desde los temas más básicos como el loop principal y el doble buffer hasta otros más complejos como las rotaciones, los fondos por trocicos (en mosaicos), los métodos de detección de colisiones, algoritmos de búsqueda (pathfinders), manejo de física y sistemas de partículas. Lo mejor del taller es que aparte de estos aspectos más o menos generales de los videojuegos, iban comentando también temas de rendimiento y problemas de Javascript. Dijeron que un buen juego JS puede llegar a estar a la altura de un juego de PS2, si es 2d, o entre PS1 y PS2, si es 3d. Pero además comentaron problemas que tenían los navegadores si querían hacer efecto espejo automáticamente en las imágenes, usar base64 o como no tuvieran cuidado con los arrays y el recolector de basura (al eliminar un elemento de un array no lo elimina de verdad, sino que lo deja pendiente de GC, lo cual puede llegar a causar un parón sensible en un videojuego).

En las charlas del día siguiente hubo de todo, pero me gustaría destacar sobre todo 3 que fueron especialmente interesantes. En la primera, José Javier García-Aranda de Alcatel-Lucent hablaba sobre un algoritmo en el que están trabajando para compresión de imágenes y vídeo, LHE (Logarithmical Hopping Encoding), y que puede ser muy potente para el Cloud Gaming y las videoconferencias, ya que se consiguen tiempos de codificación rondando los 2ms y una calidad decente a 0,1 bits por pixel. Fue una de esas charlas que destacan porque lo que se cuenta es muy interesante, se entiende muy bien y además el ponente es muy ameno.

En la segunda Jorge del Casar (@jorgecasar) hablaba sobre WebSockets, y nos hacía una demo con un Tetris multi-jugador.

Y por último Imanol Fernández (@MortimerGoro) de Ludei nos daba una apabullante lección sobre rendimiento de Javascript, haciendo una demostración de un pequeño programa que tardaba 600 veces más en su versión Javascript que en la versión C++, y luego le aplicaba muchos trucos hasta conseguir que fuera "sólo" 6 veces más lenta. Sobre todo se usaron técnicas para conseguir que el compilador JIT de Javascript hiciera "inline caching" de las funciones más importantes, ya que es muchísimo más rápido, con cosas tan aparentemente absurdas como borrar un comentario o borrar un try-catch. Por ahí rondaron conceptos como las "hidden classes" (que se crean cuando añades atributos nuevos en tiempo de ejecución, y que ralentizan bastante) y arrays de más de 64K (leeentos). Pero vamos, si os interesa el tema, lo mejor es que veais la presentación completa.

Aparte de eso, también nos habló de CocoonJS, una herramienta que ha hecho que permite publicar tus videojuegos HTML5 en móviles, pero que al contrario que Phonegap, en lugar de utilizar los browsers que vienen de serie en las respectivas plataformas, utiliza uno propio, con su propia implementación de canvas, audio, webGL, etc., y con un rendimiento optimizado respecto a los otros (are we crazy? yes, but we're from Bilbao). La verdad es que tenía una pinta tremenda.



El videojuego: Picross

La verdad es que la GameMe5, en la práctica, sirvió a mi proyecto sobre todo para una cosa: acojonarme. Tanta optimización, tanto comportamiento extraño de los motores JS, tanto lío con los navegadores, tanta facilidad para quedarse sin memoria... y voy yo y le meto una capa más de riesgo por encima, Dart.

Así que decidí que ya tenía bastante cubierto mi cupo de riesgo y decidí hacer un juego sencillito, que no requiere gran rendimiento pero que a la vez escala muy bien, y da para ir añadiéndole cositas poco a poco. Y por supuesto, ¡muy divertido!.

Picross es un juego de tipo puzzle que se conoce por muchos otros nombres de los cuales el parecer el más oficial es "nonogramas".  Habrán salido millones de versiones del juego, tanto para ordenador como en papel, pero la que se hizo más famosa en su día fue la versión de Nintendo DS, o al menos esta es la que yo conocía y de la que me vicié hace años. Como buen juego de puzzles, se puede dejar el rendimiento un poco de lado y que siga siendo jugable, y eso es lo que a mi me importaba llegado a este punto. Sobre todo, porque tenía que tener una versión presentable en una semana, ya que me había presentado a la PiWeek.

El funcionamiento del juego es sencillo: hay una matriz de celdas, y cada una de ellas es un píxel, que puede ser blanco o negro. Todas juntas forman un dibujo binario que es el que hay que averiguar. Para hacerlo, cada fila y cada columna tienen una serie de números que indican los grupos de píxels negros que son consecutivos. Por ejemplo, si en una fila pone "3 2" significa que hay 3 píxels negros seguidos y luego separados otro grupo de 2 píxels negros. Entre medias, antes y después, hay un número indeterminado de píxels blancos. Combinando las pistas de las filas y las columnas, se va averiguando el dibujo.


La PiWeek

Kaleidos es una empresa de desarrollo que probablemente pueda presumir de tener el mayor ratio de densidad de geeks por metro cuadrado en toda España. Se definen como una empresa orientada a proyectos de innovación, así que tienen varias iniciativas muy interesantes orientadas a la innovación y la mejora. Una de ellas es la PiWeek ("Pi" de "Personal innovation"), una semana entera en la que paran por completo todos los proyectos que estén haciendo en ese momento para hacer... lo que le dé la gana a cada uno. Así, tal cual, lo que sea que le interese. Las únicas reglas son usar software libre para el desarrollo, y tener algo enseñable para una demo el Viernes.

La PiWeek tiene vocación abierta, y por tanto admite gente que no sean empleados de Kaleidos, ya sean individuos o empresas. El caso es que a mi me quedaba todavía una semana de vacaciones, sin ningún plan concreto, y de hecho tenía pensado dedicarla a este proyectillo, así que, ¿por qué no presentarme a la PiWeek, y así por un lado obligarme a hacer algo y por otro poder compartir proyectos con más gente?. Dicho y hecho, el Viernes anterior a la PiWeek se presentan propuestas de proyectos para formar equipos... y allí estuvo Picross.

Yo ya conocía a unos cuantos de Kaleidos, sobre todo a través del grupo de usuarios de Groovy Madrid GUG, pero la semana me dio la ocasión de charlar con todos, y la verdad es que es una gozada, en general son gente muy preparada y sobre todo derrochan pasión por la tecnología y el desarrollo. Entre los proyectos que se presentaron a la PiWeek había algunos como un clon de Twitter usando Redis y Spring integration, un visor de comics Android con detección de viñetas, un plugin de Chrome capaz de capturar pantallazos como GIF animados, un traductor de Python a Javascript (bautizado como CobraScript) o un aplausómetro hecho en Arduino, junto a otros más... rarunos, como un MUD hecho en Eiffel o una librería que permite crear parsers en Clojure. Salieron un montón de proyectos, pero todos tenían algo que contar. El ambiente que se crea durante la PiWeek es impresionante.


Haciendo el juego

Y llegó el momento de ponerse con el juego. Las primeras sensaciones con Dart son muy buenas, no es un lenguaje ni unas herramientas a las que estés acostumbrado, no son ni mucho menos tu "zona de confort", pero tanto las herramientas como el lenguaje y las librerías resultan cómodos e intuitivos. Esperaba encontrarme con alguna característica del lenguaje que echara de menos (el típico "no me puedo creer que esto no exista aquí"), pero no encontré ninguna.

Integración de Dart con Javascript
Mi primera intención era hacer un interfaz Dart para una librería Javascript de videojuegos. Después de estar echando un vistazo a unas cuantas me decidí por MelonJS, ya que había oído hablar bien de ella y era más pequeña y ligera que otras como Cocos2D. Después de varias pruebas básicas que aparentemente funcionaron, fui creando clases "proxy" que intermediaran con la librería, usando sólo lo que iba necesitando... hasta que de repente una prueba, justo después de haber hecho un montón de código, dejó de funcionar.

Tras un rato pegándome con ello, descubro que no podía llevar a cabo lo que pretendía. El caso es que Dart permite hacer llamadas a funciones y objetos Javascript sin ningún problema, y de una forma bastante cómoda, convirtiendo colecciones de una manera sencilla. También se le pueden mandar funciones Dart que sirvan de "callback" y se las llame desde JS. Sin embargo, la integración tiene una limitación importante, y es que no se puede llamar a objetos Dart desde código Javascript. Esto invalida por completo la posibilidad de usar un framework como es MelonJS, ya que en él todas las entidades del juego se definen como objetos a los que invoca el núcleo Javascript de la librería.

Un día entero perdido con la tontería. Y en realidad es culpa mía, porque lo ponía muy claro en la documentación. Odio los Lunes.

Problemas APIs HTML5
Como había oído que el sonido daba problemas, decidí usar la librería Howler para que ella se encargue de elegir el fichero de audio adecuado según el navegador, poniendo versiones de los sonidos en mp3, ogg y wav. En este caso al no tratarse de un framework no tuve ningún problema para encapsularla en clases Dart. Pensaba que con esto ya conseguiría que los sonidos y músicas sonaran en todos los navegadores modernos pero... craso error. Ni así. El tema del audio en los navegadores es un berenjenal de cuidado. He ido probando en varias versiones y nunca sabes si te va a funcionar o no, a veces incluso versiones del mismo navegador con muy poca diferencia entre ellas. Con Chrome por ejemplo, en Dartium no funciona la música pero sí los sonidos, y lo mismo en otras versiones de Chrome que he probado. Sin embargo, en mi Chrome y la mayor parte de versiones que he probado sí funciona también la música. En Firefox más de lo mismo, aunque aquí a veces me ha ocurrido lo contrario, que funcionaba la música pero no los sonidos.

Rendimiento
La verdad es que hasta ahora no he cuidado nada el rendimiento del juego, ni siquiera dentro de las limitaciones que pudiera tener usando Dart, pero aun así los resultados son curiosos. En Chrome en general el juego parece funcionar bastante bien, al menos en los ordenadores en los que he probado (imagino que en ordenadores antiguos le costará más). Curiosamente, parece que funciona igual de rápido en Chrome, es decir, ejecutando el código compilado a Javascript, que en Dartium, ejecutando el código de forma nativa. En Firefox, al menos en Linux, sufre mucho más, notándose mucha mucha diferencia según el tamaño del dibujo que se intenta averiguar. En Windows me pareció que funcionaba un poco mejor, aunque pudo ser por la máquina.

En el Chrome de mi tablet Android el juego funciona, justita de rendimiento, pero funciona. Parece sufrir bastante en cuanto tiene que usar cualquier sonido, lo cual me hace sospechar que los sonidos pueden ser causantes en buena parte de los problemas de rendimiento. Al final se acabó quedando colgado. En cualquier caso no me parece muy preocupante por el momento, porque como ya he dicho no he mirado todavía nada del rendimiento, y para la versión móvil probaré con el ya mencionado CocoonJS, que espero que no tenga problemas con el sonido (y si los tiene ya les vale).

Imagino que Dart debe estar ralentizando en general todo el código, y de hecho puede que la lentitud en Firefox sea por eso, aunque tampoco me extrañaría nada que fuera sencillamente un tema de rendimiento de las librerías HTML5, que no las veo demasiado optimizadas. Aunque la conclusión principal que saqué de charlas del GameMe5 es que el rendimiento de Javascript es muy muy muy sensible, y un pequeñísimo cambio puede causar de repente un gran aumento en la velocidad. En cualquier caso, en principio para este juego me vale.

Programando en Dart
Esperaba que Dart me diera algún problema: que las herramientas no funcionaran bien, que me encontrara con algún bug molesto en alguna librería, que no encontrara documentación sobre las librerías... Sin embargo, no ha sido así, todo ha funcionado como la seda.

El IDE funciona muy bien. A pesar de tener tipado opcional, hace una buena inferencia de tipos y permite autocompletar, buscar referencias, refactorizar o navegar por las clases incluso en variables sin tipo explícito declarado. Encontré algunos errores en esto, en cuanto a variables de las que de repente es incapaz de inferir el tipo aunque un rato antes sí lo hacía, pero lo considero errores menores e imagino que se arreglarán. También detecta errores de llamada a métodos o propiedades inexistentes, aunque al ser un lenguaje dinámico los marca como warnings en lugar de errores. Hubiera preferido lo segundo, pero me vale, e imagino que en el futuro permitirán esta opción.

Dartium, el navegador que incluye la máquina virtual Dart para ejecutar de forma nativa, funciona también muy bien, y el debugger tampoco me dio ningún problema. Incluso, me encontré alguna sorpresa agradable como que cuando el debugger te muestra los atributos de un objeto, te incluye también los valores de sus getters, es decir, de las propiedades que tenga que se calculan a partir de una función en lugar de devolver directamente el valor de una variable. Esto es comodísimo.

Me encontré muy cómodo también con el lenguaje. Me fastidia el que las reglas de estilo recomienden tabulaciones de dos espacios, que creo que hacen el código menos legible, y al venir yo de lenguajes de tipado estático me costó un rato hacerme a la idea de que no tenía por qué declarar tipos en variables locales (en propiedades de clases y parámetros de métodos yo que tú sí lo haría siempre). Todo lo demás me encantó desde el principio. No eché nada de menos al plasmar un diseño orientado a objetos, tiene sus clases, objetos, clases abstractas, e incluso modificadores de atributos interesantes para el control del código como const y final. Pero sobre todo el lenguaje tiene un montón de "azúcar sintáctico", con comodidades muy importantes como los getters/setters implícitos, las funciones lambda, los parámetros opcionales y/o nombrados en los métodos, los constructores con inicialización directa de propiedades con argumentos, el operador en cascada y el manejo de colecciones.

Las librerías estándar también son muy completas y están muy bien documentadas, existen clases para cualquier cosa básica que se te puede ocurrir a hacer y, también muy importante, se encuentra cualquier cosa muy rápido buscando en http://api.dartlang.org/. Si se quiere hacer algo más especializado (aunque a mi no me ocurrió), también existe un repositorio de librerías en el que es fácil buscar: http://pub.dartlang.org/ y gracias al gestor de paquetes incluido en Dart, pub, usar cualquiera de esas librerías es muy sencillo.

Lo más importante que puedo decir es que olvidé por completo la sensación de que estaba programando para el navegador. Creo que la separación entre programación "front" y "back", aunque tenga cierto sentido, es un poco artificial. Se ha hecho muy necesaria de unos años a esta parte en programación web porque programar para la parte cliente era un infierno, en el que contaba más conocer los truquillos y las compatibilidades en los navegadores que la programación real. Pero eso está cambiando mucho: con jQuery y demás librerías JS nos podemos olvidar de las incompatibilidades en el acceso al DOM, con Bootstrap (como ya comenté en otro artículo) y otras librerías de LESS y SASS nos podemos olvidar en buena parte de los dolores de cabeza con el CSS. Con Dart podemos dar un paso más y olvidarnos de la problemática de Javascript y sus librerías, comenzando a programar exactamente igual a como se hace en la parte back, al menos en mi caso, con un diseño orientado a objetos, con encapsulación, con librerías potentes, con tipado, con las facilidades de un buen IDE...


El resultado

Se puede acceder al juego online en esta direcciónhttps://googledrive.com/host/0B-qqsnsC2gHuS210TnQ4cENGSkk/index.html

Es completamente jugable, aunque sólo tiene 4 niveles y a veces le pasan cosas raras. Esta es la versión que presenté en la demo del Viernes de la PiWeek. Tampoco hay instrucciones, así que lo cuento aquí: botón izquierdo del ratón = negro, botón derecho = blanco (se ve como una X con sombras).

Mi intención es mejorarlo y convertirlo en un "juego de verdad". Se pueden hacer un montón de cosas, como:

  • Añadirle muchas más fases: una de las grandes ventajas (y también alicientes) de este juego es que añadir más fases es tan fácil como añadir un pequeño dibujo pixelado... y si empiezas a tirar del hilo de los juegos retro ¡hay un montón!. Tres de los cuatro dibujos de la demo están hechos así, uno de ellos incluso con un gráfico propio de un mono que era el sello de mi "marca" de juegos de Spectrum, ¡sniffffff!. ¿Reconoces los otros dos?.
  • Mejorar el código, sobre todo el loop principal del juego y la carga de recursos, ya que me basé en el código del taller de Gameme5 y no me gusta mucho cómo les quedó esta parte. Tengo localizada ya una librería para el loop y otra para la carga de recursos, ambas con buena pinta.
  • Versión para móviles (probando CocoonJS)
  • Modo "duelo" multijugador, que me permitiría también probar a hacer un mini servidor en Dart (y reutilizar clases de la parte cliente), y probar los WebSockets.
  • Conectarlo con Facebook y/o Google Play Games.

En definitiva: Dart me ha funcionado muy bien, HTML5 un poco más regular, y sobre todo me ha encantado la experiencia y me lo he pasado muy bien. Y lo mismo... hasta acaba saliendo como resultado un juego de verdad. Permanece atento a la pantalla.

Aparta, Javascript. Llega... ¡DART!

Dos añitos se ha tirado la plataforma Dart de Google para conseguir alcanzar la suficiente estabilidad tanto en el lenguaje como en las librerías y herramientas, como para llegar a su primera versión estable, la versión 1.0, cuya salida según el equipo de desarrollo es inminente.

¿Y qué demonios es Dart?. Pues la forma más rápida de definirlo es que es un reemplazo de JavaScript. Un reemplazo que pretende aprovechar las virtudes de JavaScript, y a la vez superar sus defectos. Esto lo hace especialmente interesante cuando lo que se pretende desarrollar es una aplicación web (o de forma más general, un "rich client"), es decir, una aplicación con interfaz web y con mucho código en la parte cliente. No obstante, si pensamos más allá nos podemos dar cuenta de que sus posibilidades son aún mayores.

Te estoy viendo. Tienes serias dudas. ¿Cuántas veces te han vendido la moto del enésimo lenguaje de moda que todos deben usar porque es el más bonito e indudablemente el mejor, para finalmente darte cuenta de que no tenía nada especial y era lo mismo de siempre o, incluso, mucho peor?.

Lo cierto es que Dart cubre un hueco, tiene un propósito. Hoy por hoy no hay otro igual o del mismo estilo.

¿No me crees?. Sigue leyendo, a ver qué piensas al final del artículo...

Pero... ¡yo amo Javascript!

El bueno
JavaScript tiene indudablemente algunas virtudes. Es un lenguaje con un buen manejo de prototipos y con la posibilidad de usar funciones como valores dentro de variables. Pero también es un lenguaje muy guarro, especialmente si pretendes hacer una aplicación más o menos grande, algo que cada vez es más habitual con la evolución que han sufrido las tecnologías web. Se nota que el lenguaje está pensado "en pequeño".

Las variables son globales, y si quieres crear un espacio propio de nombres o modularizaciones adecuadas en general, te metes en problemas. No existe encapsulación, no se pueden tener elementos privados a nivel de clase (si aceptásemos que existen clases, quiero decir, cosa que no es cierta). El uso de this es para darle de comer aparte (cada vez que te ves obligado a hacer un var self = this, muere un gatito), los operadores son exageradamente casquivanos... cosas muy básicas del diseño de aplicaciones, y especialmente del diseño orientado a objetos.

El feo
Por supuesto, puedes diseñar y programar aplicaciones JavaScript correctamente, incluso orientadas a objetos, siguiendo varias recomendaciones sobre cómo organizar correctamente tu código, y qué características del lenguaje evitar. Hay un montón de libros y artículos sobre el tema, y es verdad que uno de los grandes problemas de Javascript es que durante mucho tiempo se ha considerado un lenguaje "para hacer chapucillas en la web". Y la gente que programaba en Javascript a menudo no eran programadores, lo que ha hecho que se haya extendido mucho código basura.

El malo
Pero también es verdad que el lenguaje no ayuda a estructurar mejor el código, sino todo lo contrario, ni tampoco ayuda a la legibilidad. ¿Por qué tengo que leerme un libro para enterarme de qué cosas del lenguaje debo evitar a toda costa y qué patrones debo seguir no para que mi código sea mejor, sino para que sencillamente no sea pura caca de vaca?. ¿Por qué tengo que leérmelo incluso para entender muchas cosas arcanas que lleva el lenguaje dentro, y que muchos programadores gustan de utilizar para demostrar todo lo que saben?.

En cualquier caso, si ya has superado ese umbral, tienes conocimiento sobre cómo diseñar de una forma legible y mantenible una aplicación JavaScript y, qué demonios, te encuentras cómodo con ello, entonces Dart no será para ti.

Lenguaje limpio, con clases y orientado a objetos 

Hoy por hoy, si quieres hacer una aplicación cliente en la web, en web "pura", se tiene que ejecutar en Javascript, ya que es lo único que los navegadores saben ejecutar. Dadas sus limitaciones, han ido apareciendo otros lenguajes de reemplazo, que compilan a Javascript, como por ejemplo CoffeeScript y TypeScript.

Dart va un paso más allá que estos dos, porque al contrario que ellos, el lenguaje no está basado directamente en Javascript. Se parece en algunas cosas, sí, porque la mayor parte de su "target" es la gente que está trabajando con Javascript, por lo que es conveniente que haya cosas que les resulten familiares.

En contraposición a Javascript, la palabra que mejor define a Dart es que es limpio. Tiene funciones, que también se pueden asignar a variables, pero también tiene clases, lo que facilita que se haga un diseño orientado a objetos. Permite crear librerías, versionarlas e importarlas en otro proyecto con un espacio de nombres propio, para evitar colisiones. Tiene la posibilidad de crear propiedades y métodos privados dentro de una clase.

Además, de esto, el lenguaje tiene mucho "syntantic sugar", es decir, muchas formas sencillas de declarar las cosas en pocos términos. Para que un elemento sea privado, basta con hacer que su nombre empiece por "_". Los getter y setter son opcionales, y se les llama automáticamente al usar la propiedad, de forma que desde el punto de vista del que los llama no sabe si existen o no (sí, sí, encapsulación poco trabajosa). Se pueden declarar métodos sencillos que devuelvan un valor o una operación de forma rápida usando "=>". Se pueden concatenar llamadas consecutivas a un objeto con el operador "..". Se pueden declarar fácilmente funciones anónimas para pasarlas como parámetro. Se pueden declarar datos de colecciones de forma sencilla usando literales [] y {}...

El lenguaje Dart tiene tipado dinámico, aunque a ellos les gusta más llamarlo "tipado opcional". Puedes declarar el tipo de cualquier variable, o no hacerlo. Dart tiene un modo de ejecución llamado "checked mode", que es el que se recomienda para desarrollo, y que es más lento pero da error ante manipulaciones erróneas de variables sin tipo declarado según su valor actual. Por ejemplo este código no daría error en "modo producción", pero sí haría saltar una excepción en el modo checked, al tratar una cadena como si fuera un booleano (lo de los undefined, null, etc. como false dentro de las condiciones no se ha heredado de JS; en las condiciones sólo pueden ir booleanos):

var name = 'Bob';
if (name) {
print
('You have a name!'); // Prints in JavaScript, not in Dart.
}
No sé muy bien por qué, pero el editor de Dart no es tan listo como eso, aunque sí que es listillo. En el código anterior no daría ninguna advertencia, pero si pulsamos autocompletar en "name." sí que nos dará una lista con todos los atributos y métodos públicos de la clase String (lo que me hace pensar que lo anterior también es alcanzable). También es capaz de hacer cambios de nombre en clases, propiedades o métodos propagando el cambio a todos los que lo usen, así como hacer otros tipos de refactorizaciones. Por tanto, los que se encuentren más cómodos con lenguajes estáticos (entre los que me incluyo) podrán mitigar un poco esa incomodidad gracias a esta posibilidad de hacer refactorizaciones y renombrados. Por supuesto, en los sitios en los que las variables o los parámetros sí que los declaremos con su tipo, se detectará cualquier manipulación errónea.

En definitiva, sí, el lenguaje se parece a... muchos otros lenguajes que ya existen. Tiene bastantes puntos en común con Groovy, por ejemplo. Pero tiene una diferencia importante respecto a ellos: se puede ejecutar en el navegador.

"Mundo real": Rendimiento, compatibilidad, conectividad

Todo esto es muy bonito, pero la vida real no vive sólo de lenguajes. Consideremos otros aspectos fundamentales a la hora de tomar una decisión:

    de 11870.com
  • Rendimiento. Este ha sido el objetivo número uno del equipo de Dart una vez definido el lenguaje. Es obvio que el rendimiento es importantísimo, el compilador de Javascript debe generar código que sea suficientemente rápido y pequeño, o si no no merecerá la pena. En cuanto a la velocidad, Google afirma que su lenguaje actualmente puede llegar a ser incluso más rápido que si haces la aplicación directamente en Javascript. Los datos se pueden encontrar en este benchmark. Sea verdad o no (ya se sabe que los benchmarks son como las encuestas de los políticos), la verdad es que los ejemplos que he visto sí me han parecido suficientemente rápidos. En cuanto al tamaño, Dart optimiza la carga de librerías, de forma que sólo se compila a JS el código que realmente se utiliza dentro de la aplicación. Ahora mismo hay alguna librería que al utilizarse sí que aumenta mucho el tamaño, pero teniendo en cuenta que se trata aún de una versión 1.0 de la plataforma y que el rendimiento es el objetivo principal actualmente, es de esperar que se hagan optimizaciones. Habrá que ver hasta dónde se consigue llegar.

  • Compatibilidad. El código generado es compatible con todos los navegadores actuales... excepto con Internet Explorer 8 o inferiores. En Octubre del 2013 el uso de IE8 según StatsCounter es del 9,56%, todavía alto. Por otra parte, las herramientas de Dart no funcionan en Windows XP, sistema operativo que todavía según la misma web tiene un uso del 20,06%. Mi interpretación es que este es uno de los aspectos en los que se nota que Dart sabe que su momento no iba a llegar este año... pero puede que sí el que viene, ya que es de esperar que ambas estadísticas y especialmente la de IE8 desciendan durante los próximos meses.

  • Conectividad. Desde Dart se puede llamar a librerías Javascript sin ningún tipo de problema. El enfoque sugerido es hacer una librería Dart que encapsule las llamadas, y en general es así como se está haciendo. Por otra parte, a pesar de lo pronto que todavía es para Dart, existen ya librerías para acceso a bases de datos como MySQL, PostgreSQL o MongoDB. Por supuesto, el margen de mejora en cuanto a librerías es todavía grande.

Librerías de acceso al navegador

Para acceder al navegador tenemos la librería html, una librería bastante grande que encapsula el acceso al DOM del navegador, HttpRequest, WebSockets, y las múltiples nuevas APIs que están surgiendo en HTML5 (almacenamiento local, sonido, WebRTC, etc.). También tenemos la librería js que permite acceder a JavaScript.

Luego sobre ellas se construyen algunas librerías que facilitan la creación de la web según estándares modernos y modularizables, pensando especialmente en la creación basada en componentes. La librería que se considera "base" para esto es polymer, basada en el proyecto Javascript del mismo nombre, y que permite definir elementos y atributos propios dentro del DOM o extender otros ya existentes, enlazar automáticamente con datos de los objetos Dart, incluso con enlazado doble, si cambia el elemento HTML cambia el objeto Dart y viceversa, y crear estructuras condicionales y bucles.

La cosa no se queda ahí ya que también se está creando un port del cada vez más popular AngularJS, llamado AngularDart. Este proyecto va más allá que polymer, ya que es un framework MVC más completo.

Hay que tener en cuenta que toda la parte del cliente, incluyendo estas librerías, al final se acaba convirtiendo en JavaScript. Es decir, nada de SEO (o sea, fundamentalmente nada de aparecer en buscadores) ni de accesibilidad (me refiero a la accesibilidad tradicional WAI WCAG-AA, que se sigue exigiendo en muchos proyectos de la administración pública, no a la más moderna WAI-ARIA). Por eso dije al principio que Dart es ideal fundamentalmente para aplicaciones web, no para sitios web públicos que queramos que aparezcan al buscar en Google.

Se puede ejecutar en el servidor

La aparición de node.js supuso una pequeña revolución, ya que permitía ejecutar Javascript no sólo en el navegador, sino que también la parte de servidor se hiciera en el mismo lenguaje. Dart ha tomado también este modelo, y desde el principio se ha preparado para poder ejecutarse en un servidor web. La diferencia fundamental entre la parte de cliente y la de servidor es que esta última no se compila a Javascript, sino que se ejecuta directamente en la máquina virtual de Dart. Además no podrá utilizar, obviamente, las librerías de acceso al navegador, y a cambio tendrá las suyas específicas que no podrán usarse en el cliente (muchas otras serán comunes para los dos lados).

La librería principal del servidor es io, que permite acceso al sistema de ficheros, sockets, cliente y servidor web, etc.

Para esta primera versión 1.0 el equipo de desarrollo se ha planteado como prioridad el compilador de Javascript y su rendimiento, y la ejecución en la parte del cliente en general, dejando para más adelante las optimizaciones en el servidor. Esto quiere decir que el soporte de Dart en el servidor, aun siendo completo, sólo lo veo recomendable por el momento para aplicaciones que no tengan muchas complicaciones en el lado del servidor. Un API que acceda casi directamente a una base de datos, por ejemplo. Si no, para valientes.

Algunos ejemplos de incidencias o mejoras que se han pospuesto tienen que ver con la velocidad de la propia máquina virtual y de la concurrencia. Concurrencia que en Dart se ofrece en forma de isolates, elementos de ejecución que no comparten memoria y se comunican entre sí por medio de mensajes, para evitar la existencia de errores chungos al gestionar la sincronización. Aparte del modelo de concurrencia, Dart permite ejecución asíncrona mediante Futures, que no se ejecutan simultáneamente sino uno detrás de otro, pero que permiten establecer el orden de las ejecuciones de una forma un poco particular. Son similares a las promises de Javascript. Siendo interesantes a priori ambas propuestas (aunque no tengo una opinión demasiado firme sobre ninguna de los dos por el momento y ambas me chocan un poco), lo cierto es que les queda trabajillo para facilitar la programación en el servidor, tanto en los isolates como en los futures (los futures se usan también en el cliente habitualmente pero entiendo que ahí no tienen por qué complicarse demasiado y me da que basta con lo que hay). Incluso la mismísima serialización de objetos con JSON, que debería ser importantísima en Dart para la comunicación cliente-servidor, entre isolates o usando sockets, está aún poco trabajada (aunque hay soluciones para crear y leer JSON automáticamente, pero ya las contaré otro día).

Sin embargo, el que tanto el cliente como el servidor se puedan crear en el mismo lenguaje Dart tiene ventajas muy interesantes. Es obvio que podríamos compartir código entre ellos, incluyendo los propios objetos que se serialicen en la comunicación cliente-servidor, pero puede ser aún más interesante si creamos interfaces comunes para determinadas partes que son similares en el cliente y el servidor en cuanto a concepto aunque su implementación sea completamente distinta. Especialmente el cliente HTTP, aunque también posiblemente los sockets / websockets. Esto nos permitiría poder trasladar código de cliente a servidor y viceversa con mucha facilidad, haciendo que por ejemplo podamos trasladar cosas fácilmente de acuerdo al rendimiento, la seguridad o simplemente cambios de requisitos.

Incluso, se me ocurre que potencialmente podríamos hacer un framework en el servidor que reemplazara la librería de acceso al DOM y creara los elementos en memoria usando el mismo Polymer o cualquier otro sistema de plantillas que usemos en el cliente, y devolviera el HTML resultante al cliente para partes de la web que queramos que aparezcan en buscadores. Sí, quizá después de todo el SEO sí que sea posible.

Es de esperar que Google vaya con fuerza a por el servidor después de la versión 1.0, e incluso que le dé soporte en AppEngine, de forma que podamos tener con cierta facilidad un hosting básico gratuito.

Yendo un paso más allá

Al ser Dart un proyecto de Google, se pueden adivinar intenciones que tiene la empresa para el lenguaje. Se puede adivinar que pronto la versión oficial de Google Chrome va a ejecutar Dart de forma nativa (bueno, además es que lo han dicho ellos mismos). Al fin y al cabo, ya existe una máquina virtual que ejecuta Dart directamente, la DartVM, y ya existe una versión de Chromium que también ejecuta Dart de forma nativa, Dartium. No olvidemos que Chrome tiene un uso actualmente del 40,44% (nuevamente StatsCounter, Octubre 2013).

Al hacer esto Google va a conseguir una jugada a varias bandas: por un lado, esta vez seguro que sí, las aplicaciones Dart serán más rápidas que las aplicaciones Javascript. Por otro lado, las aplicaciones Dart serán más rápidas en Chrome que en el resto de navegadores. Y por otro lado, las aplicaciones Dart podrán tener un rendimiento comparable a las de escritorio, por lo que se aumentará la tendencia de aplicaciones hacia las tecnologías web. Triple win para Google.

Además de esto, no olvidemos que HTML5 + JS se está utilizando actualmente en otros ámbitos, no sólo aplicaciones web. Por ejemplo, para hacer aplicaciones móviles multiplataforma, o para videojuegos. Y vaya hombre, Google también está detrás de Android, por lo que es de esperar que finalmente también consigan que las aplicaciones hechas en Dart funcionen mucho mejor en Android que en IOS (y ya van cuatro wins). En este sentido, existen ya librerías específicas para hacer aplicaciones móviles en Dart, especialmente Rikulo.

Conclusiones

¿Y ahora?. ¿Estás ya convencido de que esto no es lo mismo de siempre?. ¿Ves no sólo lo que ya aporta, que es mucho (aunque algunas cosas estén aún un poco verdes), sino en lo que puede llegar a convertirse, en lo que se está convirtiendo?. Te invito a que dejes un comentario con tus impresiones. ¿Piensas como yo que supone un nuevo paso en la propia evolución de la web?.

Pues corre, vete a la página de Dart, ¡y dale duro!.

¡La web te necesita!

de panicposters.com