Gentoo Powered

Powered by Planet Venus!


Planet::Phiuba es un canal que sindica distintos blogs y contenidos de la comunidad de la Facultad de Ingeniería de la Universidad de Buenos Aires.

Si formás parte de la comunidad y creés que tu blog puede aportarle a este espacio; estás invitado a pedir que te agreguemos a la lista de feeds que lo conforman (hacelo acá).

17 October, 2016

From Clojure to ClojureScript

Publicado en Mon 17 Oct, 2016 01:55 por facundoolano en "Facundo Olano"

Ever since I started working on the advenjure engine as a learning project for Clojure, I thought porting it to the browser would make a great excuse to get into ClojureScript as well. I finally took the time to do it a couple of weeks ago and now the engine is fully functional in both environments.

The process of going from zero to having some Clojure code running in the browser, source-maps included, was surprisingly easy. Making a fully functional Clojure project target the browser too was a bit more difficult, especially when dealing with JavaScript’s inherent asynchronous nature and setting up the ClojureScript compiler to bundle the project and its dependencies.

I document here the steps I took, useful reads, issues I found along the way and the sources where I got the solutions. For context, I’m using version 1.9.229 of ClojureScript.

Getting Started

Differences from Clojure was a good place to start getting an idea of what ClojureScript looks like coming from Clojure. After that, the Quick Start tutorial was all it took to get my code running in the browser, in Node.js and even in a ClojureScript REPL. With the boilerplate project from the tutorial I was able to start migrating bits of the advenjure codebase and running them in the browser console.

After testing most of the pure-logic parts of the project and being confortable that they could actually run in the browser, I had to deal with the files that were more or less dependant on Java interop. At this point I needed to review the Reader Conditionals documentation, here and here. The key takeaways where: Clojure specific logic goes in .clj files, ClojureScript in .cljs; shared logic goes in .cljc, using the conditional syntax in the parts that differ from one host to another:

(defn str->int [s]
  #?(:clj  (java.lang.Integer/parseInt s)
     :cljs (js/parseInt s)))

Because of how macros work in ClojureScript, those needed to go either in .clj or .cljc files, and required using the :require-macros option:

(ns example.dialogs
  #?(:clj (:require [advenjure.dialogs :refer [dialog]])
     :cljs (:require-macros [advenjure.dialogs :refer [dialog]])))

With Reader Conditionals, println and js/prompt I had a fairly functional version of the example game running in the browser. The next step was to include jQuery terminal in the web page and use it as the interface for the game.

JavaScript interop and asyncrhonous code

Interop syntax is pretty simple, this article and the CloureScript Cheatsheet covered all I needed: the js/ namespace to access JavaScript globals and built-ins, js-obj for object literals, aget and aset to access them, dot prefix to invoke methods.

Things got a bit more complicated as I started integrating the jQuery terminal: my library was more or less a REPL, designed around the idea of waiting for user input and then processing it, but the terminal, as most JavaScript libraries, relied on callbacks and asynchronous processing. When the user enters a command, a processing function is called, which is detached from the game loop that holds the current game state and knows how to handle that command.

After googling around, core.async seemed like the most suitable tool to emulate the synchronicity that my codebase required. I’ve read about it earlier in the Brave Clojure book; this article was also helpful to get code samples.

My solution was to create an input channel where the jQuery terminal would write the commands:

(ns advenjure.ui.input
  (:require-macros [cljs.core.async.macros :refer [go]])
  (:require [cljs.core.async :refer [<! >! chan]]))

(def input-chan (chan))

(defn process-command
  "Callback passed to jQuery terminal upon initialization"
  (go (>! input-chan command)))

(defn get-input
  "Wait for input to be written in the input channel"
  (go (<! input-chan)))

The main game loop that used to block waiting for user input now was a go-loop that “parked” until data came into the input channel:

  (:require [advenjure.ui.input :refer [get-input exit]]
            #?(:cljs [cljs.core.async :refer [<!]]))
  #?(:cljs (:require-macros [cljs.core.async.macros :refer [go-loop]])))

    (defn run
      [game-state finished?]
      (loop [state game-state]
        (let [input (get-input state)
              new-state (process-input state input)]
          (if-not (finished? new-state)
            (recur new-state)

    (defn run
      [game-state finished?]
      (go-loop [state game-state]
        (let [input (<! (get-input state))
              new-state (process-input state input)]
          (if-not (finished? new-state)
            (recur new-state)

This works well although it requires some amount of duplication between the Clojure and ClojureScript versions of the code. Advenjure dialog trees introduce more sophisticated ways of reading and processing user input, which threatened to leak the core.async logic into other portions of the codebase, thus causing more duplication. I managed to keep that to an acceptable minimum without loosing functionality, but there’s definitely room for improvement, perhaps coming up with some macro that abstracts host-specific differences behind a common syntax.

Reading, evaluating and persisting Clojure code

Some of the features of advenjure, such as dialogs and post/pre conditions, required storing quoted Clojure code in the game state, for later evaluation. I found that some built-ins I used to implement them, like read-string and eval, are not directly available in ClojureScript, but a bit of googling revealed how to bring them back.

Based on this article I came up with the following function to replace the native eval, using the tools in the cljs.js namespace:

(ns advenjure.eval
  (:require [cljs.js]))

(defn eval [form] (cljs.js/eval
                    {:eval cljs.js/js-eval
                     :source-map true
                     :context :expr}

As I learned later on, this snippet comes with one catch: when using Self-hosted ClojureScript (which is what cljs.js enables, evaluating ClojureScript code inside ClojureScript), you can’t use advanced compiler optimizations in your build.

While it’s not a built-in, read-string can be found in cljs.reader/read-string. In the Clojure version of my library, I was able to easily save and restore the game state to a file:

(defn save-game [game-state]
 (spit "" game-state))

(defn load-game []
 (read-string (slurp "")))

I intended to do the same in ClojureScript, using the browser localStorage. This didn’t work right away, though, because the ClojureScript reader doesn’t know how to read records back from the storage. This script gave me the solution:

(require '[cljs.reader :refer [read-string register-tag-parser!]]
         '[advenjure.items :refer [map->Item]]
         '[advenjure.rooms :refer [map->Room]])

(defn save-game [game-state]
 (aset js/localStorage "" (pr-str game-state)))

(register-tag-parser! "advenjure.items.Item" map->Item)
(register-tag-parser! "advenjure.rooms.Room" map->Room)

(defn load-game []
 (read-string (aget js/localStorage "")))

Leiningen cljsbuild plugin

So far I was doing all the work inside the hello-world project from the Quick Start tutorial. Now that most of the engine was working in ClojureScript I had to integrate it back into the Clojure project and fix anything I broke to make sure it targeted both platforms. I’m using Leiningen so I looked into the lein-cljsbuild plugin. Since advenjure is a library intended to be used as a dependency in other projects, it didn’t matter much what configuration I put in there; the example project, though, ended up with the following configuration in its project.clj:

:plugins [[lein-cljsbuild "1.1.4"]]
   {:main {:source-paths ["src"]
           :compiler {:output-to "main.js"
                      :main example.core
                      :optimizations :simple
                      :pretty-print false
                      :optimize-constants true
                      :static-fns true}}

    :dev {:source-paths ["src"]
          :compiler {:output-to "dev.js"
                     :main example.core
                     :optimizations :none
                     :source-map true
                     :pretty-print true}}}}

Then, running lein cljsbuild once would compile development and production versions of the game to be included in the HTML page. Note that, as mentioned, I couldn’t use :optimizations :advanced in the production build, because I was using the cljs.js namespace in my project.

Regular Expressions

Some of the features of advenjure relied on regular expressions. The Clojure related functions are backed by the host implementation of regexes, and JavaScript doesn’t support named capturing groups. To overcome this without changing the original code, I resorted to XRegExp, which fortunately respects the native JavaScript interfaces for regular expressions:

(def regexp #?(:clj re-pattern :cljs js/XRegExp))

(defn match-verb [verb-pattern input]
  (re-find (regexp verb-pattern) input))

Bundling foreign libs

Once everything worked as expected, I needed to figure out how to pack the library so it could be easily included in projects with minimum effort. Particularly, I needed a way to bundle the JavaScript dependencies (jQuery, jQuery terminal, etc.), so the users wouldn’t need to include them manually in their HTML. This topic can get a bit complex in ClojureScript, especially when dealing with advanced optimizations (which I learned along the way I wasn’t going to use). This and this are good references.

The CLJSJS project is an initiative that allows to easily require JavaScript libraries like regular Clojure dependencies. The problem is that the amount of supported libraries is limited, and contributing one of your own is not a trivial process (specifically, it seems to require Boot, and since I was already set up with Leiningen it didn’t look like an option at the moment).

I had to fallback to using the foreign-libs compiler option. For some reason, I couldn’t figure out how to make that work from the cljsbuild settings in my project.clj, so after reviewing this wiki entry I decided to include a deps.cljs file in the root of my source directory:

 [{:file "jquery/jquery-3.1.1.js"
 :file-min "jquery/jquery-3.1.1.min.js"
 :provides ["jquery"]}
 {:file "jquery.terminal/jquery.terminal-0.11.10.js"
 :file-min "jquery.terminal/jquery.terminal-0.11.10.min.js"
 :requires ["jquery"]
 :provides ["jquery.terminal"]}
 {:file "jquery.terminal/jquery.mousewheel.js"
 :file-min "jquery.terminal/jquery.mousewheel.min.js"
 :requires ["jquery"]
 :provides ["jquery.mousewheel"]}
 {:file "xregexp/xregexp-all.js"
 :file-min "xregexp/xregexp-all.min.js"
 :provides ["xregexp"]}]
 :externs ["jquery/externs.js" "jquery.terminal/externs.js" "xregexp/externs.js"]}

Some notes about it:

  • I had to add the files and minified files to the resources folder of the library, to be used in the development and production builds respectively.
  • I needed to define a :provides name and require it in my codebase (no matter if the library exposes a global value that’s actually accesible through js/), in order for the compiler to include the library in the generated build.
  • The :requires is also important to establish dependencies between libraries; without it, the jQuery terminal code can be included before jQuery, which would cause a reference error when running in the browser.
  • The externs aren’t really necessary, since I wasn’t using advanced optimizations, but if I was I found this tool of great help in generating those files, especially for big libraries like jQuery. Smaller ones, like a jQuery plugin, I could create by hand; the CLJSJS packages can be a good reference in that case.

11 October, 2016

Templo de Ranakpur

Publicado en Tue 11 Oct, 2016 19:21 por Knoxville ( en Noticias de Ayer
Llegué al templo de Ranakpur en un día soleado. Por una ruta espectacular inicialmente pero luego,una muy pequeña de un carril que había que parar de tanto en tanto e ir con cuidado en las curvas

Al llegar me detuvieron algunos locales para preguntarme de donde venía y cómo era mi viaje, si era un motociclista profesional (manly tears were shed) y demás pero eso para otro post.

Templo de Ranakpur 

El templo es zarpado, iniciado en los 1400s está tallado a mano en marmol.

Esta foto se la robé a wikipedia porque no tengo la foto acá conmigo que saque pero it's pretty much the same

Now, un poco de por qué me fui un mes a la india a recorrer lo más posible en moto.

No, it's not.

Hace muchos años que fantaseaba con andar en moto en un país exótico. De hecho todo surgió hablando con un irlandes many many years ago cuando viajé al sur y lo contaba en este post (por el final). En su momento el tipo me había contado que había recorrido de punta a punta Vietnam en moto. Se me cayó el orto, ahí nomás.

Entonces empezó la fantasía en mi cabeza y que la guita, y que ir, y que bla y quedó ahí dando vuelta pero más como eso de inalcanzable que otra cosa.

Pero en sí era eso, ir a un país a andar en moto en esas rutas perdidas y nada más. Nada de ir a conectarme con mi chota interior ni a tener una experiencia mística. Quien carajo eran estos hombres que veía por youtube andando a solas y cómo carajo hacían?

Finalmente decidí jugarme y vi que Vietnam había cambiado las leyes y que no permitían a extranjeros alquilar motos mayores de 50cc pero que igual lo hacían y que había que pagar coima pero que no tenias seguro y que encima el clima a veces te cae 15 días de lluvia

Es ahí donde dije, soy un chabón que tiene cero experiencia en viajar en moto largas distancias solo, no conozco el idioma y encima me puede agarrar la lluvia?

Y mirando alternativas elegí India, exótica también, pero 0% de lluvias la mayor parte del año y se habla inglés. Saqué los pasajes y después lo comuniqué a quienes valían la pena y la gran mayoría expresó felicidad pero también preocupación porque ir solo a la India y en moto parecía demasiado far fetched.

Para dar un cierre al post, aun cuando mi objetivo original era sólo andar en moto, lo que me llevo de la india es el poder de la fe, no por el lado de religión, sino creer en algo con tanta convicción que sin importar qué te comprometes y lo llevas adelante.

Como estos tipos en los 1400s que dijeron con un martillo en mano, "aca vamos a hacer un templo de la concha de sus madres, bitches!!" y se les cagaron de risa y probablemente se los culearon. Pero lo hicieron igual y 600 años después un chabon X lo visita y se cae de ogt.

-. Lo sabía, cursilerias de mierda, te odio!



Anyway esto continua y hay más para registrar

<iframe allowfullscreen="allowfullscreen" class="YOUTUBE-iframe-video" data-thumbnail-src="" frameborder="0" height="266" src="" width="320"></iframe>

14 September, 2016

Journaling was key, yet I took it for granted

Publicado en Wed 14 Sep, 2016 04:38 por Knoxville ( en Noticias de Ayer

Recuerdo que me ajusté el casco y salí decidido al desierto, aun cuando sabía que la noche podría encontrarme. Todo aquello parecía una buena oportunidad.

El desierto de Jaisalmer  - India

Había pasado la noche tomando ron con Al Pacino. Not the actual Al Pacino, pero un hindú que se había cambiado el nombre a Al Pacino. Nunca jamás me había subido a la moto ebrio, ni entonado. El hostel quedaba 3 cuadras pero no podía encontrar los cambios, maneje a 2 Km/h y me parecia que iba velocidad de la luz.

Lo mas loco fue lo random del asunto, entre al local preguntando por guantes de moto y terminé yéndome a las 2 de la mañana después de charlar de miles de temas. Pocas veces me sentí tan cómodo y tan "in the flow" como en ese momento.

Su amigo austriaco, me dice "wanna see my tatoo? here's my tatoo". De dónde sale este tipo de gente y por qué no conozco más gente así?

Pero bueno, desierto de Jaisalmer. Fui recibido por una especie de gate keeper que me cobró una entrada y me dió su bendición. Ahora todo esto parece lejano, aunque recordarlo lo acerca.

La vuelta ofreció un buen paisaje

Why was I not journaling this and why am I dont have it in me to journal? Quisiera que exista un libro que se llame "Que me está pasando?" que en vez de explicarme porqué me estaba saliendo pelo abajo de la axila me explique qué me está pasando en este momento.

26 August, 2016


Publicado en Fri 26 Aug, 2016 14:24 por Sebastián Santisi ( en Sebastián Santisi
De un viaje relámpago a Santiago del Estero nos volvimos con un bombo legüero. Particularmente el más barato que pudo conseguirse. Su confección, calidad, cilindrez, etc. en sintonía con el precio... hasta está atado con alambre sobre una grieta.

En Santiago hay todo un gremio de artesanos que se dedican a grabar los bombos. La técnica tradicional es quemar con planchuelas puestas al rojo en un brasero, algo que no puede reproducirse en un departamento. Lo que tenía en casa era un soldador graaaande:

Cabe destacar que la punta corva no es algo cómodo para "dibujar", que cansa mucho el brazo la parte de estar haciendo palanca mientras se hace presión.

El diseño, googlear un poco por imágenes, hacer un collage, rotar un poco las cosas y amoldarlas al diseño apaisado del bombo.

Los primeros trazos, algo tranca para agarrarle la mano al sistema (y ver si el soldador tiraba potencia como para quemar y eso):

Después, un poco más de detalle:

Un receso para deliberar sobre el diseño de la rama izquierda y plasmarlo:

Luego el lado B y los detalles:

Tres manos de barniz satinado:

Y con ustedes el bombo armado:

Acá puede verse el diseño completo:

<video autoplay="autoplay" height="720" loop="loop" muted="muted" width="480">
<source src="" type="video/mp4">
<script> if(!document.createElement('video').canPlayType) { document.write(''); } </script>

26 June, 2016

Real-world RPC with RabbitMQ and Node.JS

Publicado en Sun 26 Jun, 2016 15:54 por facundoolano en "Facundo Olano"

tl;dr: use the direct reply-to feature to implement RPC in RabbitMQ.

I’m currently working on a platform that relies heavily on RPC over RabbitMQ to move incoming requests through a chain of Node.JS worker processes. The high level setup for RPC is well described in RabbitMQ’s documentation; let’s steal their diagram:


We grew our RPC client code based on the JavaScript tutorial, using the amqp.node module. The first —admittedly naive— implementation just created a new connection, channel and queue per request and killed the connection after getting a reply:

const sendRPCMessage = (settings, message, rpcQueue) =>
  amqp.connect(settings.url, settings.socketOptions)
    .then((conn) => conn.createChannel())
    .then((channel) => channel.assertQueue('', settings.queueOptions)
      .then((replyQueue) => new Promise((resolve, reject) => {
        const correlationId = uuid.v4();
        const msgProperties = {
          replyTo: replyQueue.queue

        function consumeAndReply (msg) {
          if (!msg) return reject(Error.create('consumer cancelled by rabbitmq'));

          if ( === correlationId) {

        channel.consume(replyQueue.queue, consumeAndReply, {noAck: true})
        .then(() => channel.sendToQueue(rpcQueue, new Buffer(message), msgProperties));

That got us a long way during development but obviously failed to perform under non-trivial loads. What was more shocking is that it got dramatically worse when running it on a RabbitMQ cluster.

So we needed to refactor our client code. The problem is that most examples show how to send one-off RPC messages, but aren’t that clear on how the approach would be used at scale on a long-lived process. We obviously needed to reuse the connection but how about the channel? Should I create a new callback queue per incoming request or a single one per client?

Using a single reply-to queue per client

Based on the tutorial, I understood that the sensible approach was to reuse the queue from which the client consumed the RPC replies:

In the method presented above we suggest creating a callback queue for every RPC request. That’s pretty inefficient, but fortunately there is a better way – let’s create a single callback queue per client. That raises a new issue, having received a response in that queue it’s not clear to which request the response belongs. That’s when the correlation_id property is used.

We were already checking the correlationId, and just needed to create the reply-to queue in advance:

const createClient = (settings) =>
  amqp.connect(settings.url, settings.socketOptions)
    .then((conn) => conn.createChannel())
    .then((channel) => channel.assertQueue('', settings.queueOptions)
      .then((replyQueue) => {
        channel.replyQueue = replyQueue.queue;
        return channel;

I thought that would be enough to make sure the right consumer got the right message, but in practice I found that each message was always delivered to the first consumer. Therefore, I needed to cancel the consumer after the reply was processed:

const sendRPCMessage = (channel, message, rpcQueue) =>
  new Promise((resolve, reject) => {
    const correlationId = uuid.v4();
    const msgProperties = {
      replyTo: channel.replyQueue

    function consumeAndReply (msg) {
      if (!msg) return reject(Error.create('consumer cancelled by rabbitmq'));

      if ( === correlationId) {
          .then(() => resolve(resolve(msg.content)));

    channel.consume(channel.replyQueue, consumeAndReply, {
      noAck: true,
      // use the correlationId as a consumerTag to cancel the consumer later
      consumerTag: correlationId
    .then(() => channel.sendToQueue(rpcQueue, new Buffer(message), msgProperties));

Enough? Only if the client processed one request at a time. As soon as I added some concurrency I saw that some of the messages were not handled at all. They were picked up by the wrong consumer, which ignored them because of the correlationId check, so they were lost. I needed to do something about unexpected message handling.

Requeuing unexpected messages

I tried using nack when a consumer received a reply to a message with an unexpected correlationId:

function consumeAndReply (msg) {
  if (!msg) return reject(Error.create('consumer cancelled by rabbitmq'));

  if ( === correlationId) {
      .then(() => resolve(resolve(msg.content)));
  } else {

Now the messages seemed to be handled, eventually. Only they weren’t: when I increased the load I saw message loss again. Further inspection revealed that the consumers were entering a weird infinite loop:

Consumer A gets message B; message B requeued
Consumer B gets message C; Message C requeued
Consumer C gets message A; Message A requeued

Repeated ad-infinitum. The same behaviour was reproduced using every possible combination of nack, reject and sendToQueue to send back the message.

Googling the issue, I read about the possibility of using Dead letter exchanges to handle those cases. But having to manually requeue unexpected messages felt weird enough; introducing a new exchange and queue sounded like a lot of effort to handle what should be a pretty standard use case for RPC. Better to take a step back.

Using a new reply-to queue per request

So I went back to a reply-to queue per request. This was marginally better than our initial approach since now at least we were recycling the connection and the channel. What’s more, that appeared to be the standard way to do RPC in RabbitMQ according to the few spots where I found non-tutorial implementation details, so, as Paul Graham would say, we wouldn’t get in trouble for using it.

And it worked well for us as long as we run a single RabbitMQ instance. When we moved to a RabbitMQ cluster the performance was pretty much the same as when we were creating connections like there was no tomorrow.

Using direct reply-to

We were seriously considering dropping the RabbitMQ cluster altogether (which meant turning our broker into a single point of failure), when I came across the link to the direct reply-to documentation. The first interesting thing there was that it confirmed why we were seeing such bad performance when running a RabbitMQ cluster:

The client can declare a single-use queue for each request-response pair. But this is inefficient; even a transient unmirrored queue can be expensive to create and then delete (compared with the cost of sending a message). This is especially true in a cluster as all cluster nodes need to agree that the queue has been created, even if it is unmirrored.

Direct reply-to uses a pseudo-queue instead, avoiding the queue declaration cost. And fortunately it was fairly straightforward to implement:

const createClient = (settings) => amqp.connect(settings.url, settings.socketOptions)

const sendRPCMessage = (client, message, rpcQueue) => conn.createChannel()
  .then((channel) => new Promise((resolve, reject) => {
    const replyToQueue = 'amq.rabbitmq.reply-to';
    const timeout = setTimeout(() => channel.close(), 10000);

    const correlationId = uuid.v4();
    const msgProperties = {
      replyTo: replyToQueue

    function consumeAndReply (msg) {
      if (!msg) return reject(Error.create('consumer cancelled by rabbitmq'));

      if ( === correlationId) {

    channel.consume(replyToQueue, consumeAndReply, {noAck: true})
    .then(() => channel.sendToQueue(rpcQueue, new Buffer(content), msgProperties))

This worked just as we expected, even in the cluster. As the code shows, though, we were still creating a new channel per request and we needed to handle its closing, even when the response never came. Trying to use a single channel resulted in a “reply consumer already set” error, because the queue was always the same.

Creating so many channels didn’t feel right, so I filed an issue asking for advice in the amqp.node repo. The creator confirmed that that was indeed an anti-pattern and suggested not only using a single channel but registering a single consumer (i.e. a single callback function to handle all RPC responses). This meant introducing some structure to be able to route responses back to the promise that was expecting it. Using an EventEmitter turned out to be an elegant way to accomplish it:

const REPLY_QUEUE = 'amq.rabbitmq.reply-to';

const createClient = (settings) => amqp.connect(settings.url, settings.socketOptions)
  .then((conn) => conn.createChannel())
  .then((channel) => {
    // create an event emitter where rpc responses will be published by correlationId
    channel.responseEmitter = new EventEmitter();
      (msg) => channel.responseEmitter.emit(, msg.content),
      {noAck: true});

    return channel;

const sendRPCMessage = (channel, message, rpcQueue) => new Promise((resolve) => {
  const correlationId = uuid.v4();
  // listen for the content emitted on the correlationId event
  channel.responseEmitter.once(correlationId, resolve);
  channel.sendToQueue(rpcQueue, new Buffer(message), { correlationId, replyTo: REPLY_QUEUE })

20 March, 2016

Clojure: the good, the bad and the ugly

Publicado en Sun 20 Mar, 2016 20:25 por facundoolano en "Facundo Olano"

Four years ago I wrote about my first (and last) experience with Common Lisp. I had high expectations and was disappointed; I ended up thinking maybe I should give Scheme or Clojure a try. It took a while, but I finally did it last month: learn Clojure. And it looks like a keeper.

Clojure has all the goodness of Lisp and functional programming, and it feels like a modern language: it addresses most of things that annoyed me about Common Lisp.

I’ve followed the great Clojure for the brave and true book by Daniel Higginbotham and then I’ve tackled a small project to train my skills. Here are my notes.

The good

  • A consistent syntax, operation names and polymorphic functions. No weird illegible names, no type specific versions of the same function (I’m looking at you Common Lisp).
  • Functional! Immutable! Expressive!
    • I don’t miss objects. Structuring programs in small functions; isolated, never changing data; those things just feel right. And I’m not even waving the old “shared state is bad for concurrency” flag; I don’t care —just now— about concurrency. This stuff makes programs simpler to reason about and test, and more fun to write.
    • There’s more: Clojure is so expressive and gives you enough options (I’m thinking loop, doseq, destructuring, etc.) that you don’t necessarily need to incur in “head/tail” recursive processing as much as I found in other functional languages, so the leap is not so rough.
  • Did I say I don’t care about concurrency? I don’t. Mostly. Not at the language level, anyway. Clojure has a lot of cool tools for concurrency (future, delay, promise, pmap, core.async). Too much options, maybe, but I don’t mind about that either. I can just RTFM whenever I do have the need to do things concurrently. And, yes, immutability and pure functions make it simpler.
  • A strong philosophy behind the language, that seems to drive its design. Python has this too and to me it’s its biggest selling point. Languages like C++ and increasingly JavaScript, on the other hand, feel like magic bags where features are added carelessly without consideration of the results. Java does have a strong philosophy: programmers are mostly idiots.
  • Runs on the JVM. Seriously? I personally couldn’t care less about that but JVM languages seem to attract a lot of attention. There are tons of Java devs for sure and some of them seem to have a symbiotic relationship with the JVM: it’s like they aren’t cheating on Java if they keep the deal inside the VM. Why would people learn Groovy instead of Ruby or Python, god only knows, but because of the “Runs in Java” part there’s a better chance of finding a Clojure Job than one using Haskell, Scheme or most other functional languages around. That alone is enough reason for me to stick with Clojure instead of keep trying Lisp dialects, even if some other one may fit my taste better.
  • Haven’t tried it yet, but the mere existence of ClojureScript is good news to me, specially considering how annoyed I am with the direction the JavaScript syntax and ecosystem is taking lately. This talk totally sold it to me.
  • Said it before and say it again: forget about parenthesis. People seem to worry a lot about them beforehand, but as with Python whitespace indentation, once you’ve used it for five minutes it just goes away. Specially if you use the darn awesome Parinfer.
  • Which brings me to: you don’t need Emacs for Lisp programming. Yes, I hear you, once I master Emacs I’ll be a more powerful programmer. But I’m trying to learn a weird language here, don’t make me also learn a weird, counter-intuitive editor at the same time. That would just increase the chances of me dropping the effort altogether. There are decent ports of Paredit for Sublime and Atom, which is good enough. But with Parinfer you just learn one command and forget about it, it just works.
  • REPL driven development. Because of pure functions it’s easy to write a piece of code and test it right away in the REPL. Together with unit tests it pretty much removes the need for debugging.
  • Leiningen looks good, it covers the small needs I had starting out and didn’t get in the way. Clojurians say it does a lot more than that, so great. Much better than the 17 tools you need to set up to have a Node.js project running these days.

The bad

  • Namespace syntax is complicated, there are too many operations and keywords to do it (require, refer, use, alias, import and ns —which can do all of the others with a slightly different notation). It’s flexible but boilerplatish, even when sticking to ns:
  (:require [advenjure.rooms :as rooms]
            [advenjure.utils :as utils]
            [advenjure.verb-map :refer [find-verb verb-map]]))
  • And while there’s no hard rule to keep a one to one relation between files and namespaces, there’s a strong convention to do it, so having to declare the package name in every file seems totally redundant (and Java-ish, let’s be honest).
  • contains? Works in a counter-intuitive way for vectors.

That’s all I got.

And the ugly

Ok, there wasn’t much bad stuff, but there is some maybe not so good or arguably not good things I can think of.

  • The built in operator set doesn’t follow the Unix and Python philosophy of small core and a lot of libraries that I like so much: the functions are way too many to easily remember, and they aren’t entirely orthogonal (some of them do the same thing in slightly different ways). Then again, the Clojure Cheatsheet, the REPL and doc are more than enough to cope with that.
  • Polymorphism is great: sequence and collection functions work as expected in all data structures. The downside is that to do so the results are always coerced to seqs, which may be unexpected, specially for hash maps. In practice, though, I found myself just chaining those functions and rarely caring about the resulting type.
  • Macros are powerful and awesome but the quoting syntax can get very tricky. I definitely need more experience to learn to reason about macro code, but the syntax will remain ugly. I guess that’s the cost you pay for being able to fiddle with how the language processes the code. In the end (much like Python metaclasses), macros are a great tool to keep in the box, but to use sparingly. So far every time I thought about implementing one I got away fine by using closures instead.
  • Java does sneak in quite a bit and that’s a turn off. (spoiler alert: I don’t like Java). OK, Java interop is simple and powerful, probably the most straightforward language interfacing I’ve seen (boy was SWIG a nightmare). That being said, Java code inside Clojure looks like, well, Java code inside Clojure: it reeks. This wouldn’t be so much of a problem if needed only to interact with some third party Java libraries, but in practice I’ve found that there’s basic stuff lacking in the Clojure standard library and it’s either add a dependency or use Java interop. I saw this while solving an exercise from the Brave Clojure book: it asked to list the first results of a google search. The request should be a one liner using the built in slurp function but, wait, you need to set the User-Agent header to request google, so you end up with:
(with-open [inputstream (-> ( url)
                            (doto (.setRequestProperty "User-Agent"
                                                       "Mozilla/5.0 ..."))

The end

Even though it’s not my ideal language and it may be less ideal to me than Python was, it looks like I’ll start to look for excuses to use Clojure as much as possible and it’ll be a while before I jump to study another new language.

06 January, 2016

Capitulo 4: About luck and the value of food

Publicado en Wed 06 Jan, 2016 02:58 por Knoxville ( en Noticias de Ayer

Dios, porque deje el blog por tanto tiempo. Quiero escribir de putas y locuras recurrentes pero no, acá estoy siguiendo la linea cronologica.


Me desperté, desayuné like a best y sali para el campamento italiano

A los 100 metros se me rompe la mochila. Se rompe una de las dos bandas o como mierda se llame que ahora, 10.41 pm no me sale.

Luck was on my side, porque si eso me hubiese pasado a mitad de camino hubiese tenido que arrastrarla por muchos kilometros lo que me hubiese llevado a un estado de fuck this shit y no se que hubiera hecho.

Volvi al campamento y empecé a pedir hilo y aguja y me dieron, asi como también duck tape. All was good.

Yendo al campamento Italiano el clima fue agradable y el camino muy entretenido

Pero por momentos el clima se ponia adverso

<iframe allowfullscreen="allowfullscreen" class="YOUTUBE-iframe-video" data-thumbnail-src="" frameborder="0" height="266" src="" width="320"></iframe>

Finalmente llegue al campamento italiano y tenía una especie de minimercado con todos los productos que uno podría haber soñado.

That was a good night.

Y me importó tres carajos que la carpa se mueva para todos lados, porque estaba comido, bañado y sintiendome como un panqué de canela recien horneado (A la noche se largo tremenda tormenta que gracias a dios la carpa aguantó)

<iframe allowfullscreen="allowfullscreen" class="YOUTUBE-iframe-video" data-thumbnail-src="" frameborder="0" height="266" src="" width="320"></iframe>

Habiendo dormido como un puto bebé salí para el final del recorrido, el Glaciar Grey donde pasaría una noche in a proper bed porque queria sentir un poco el colchon contra mi espalda.

It was the end of the road, at least del trekking. Salí del parque y desde lejos lo contemplé

Quedaba ahora la posta, ir con un Kayak por las pequeñas islas del sur de chile hasta llegar a un glaciar.


Anyway, termino con esto de Chile y vienen post randoms con some crazy shiet.

Suena Lean on, una rola que se te pega en la parte de atrás del cerebro y te deja cantando loquete. LOQUETE I SAY

<iframe allowfullscreen="allowfullscreen" class="YOUTUBE-iframe-video" data-thumbnail-src="" frameborder="0" height="266" src="" width="320"></iframe>


22 December, 2015

Torres del Paine Capitulo 3: La desolación de tu vieja

Publicado en Tue 22 Dec, 2015 01:29 por Knoxville ( en Noticias de Ayer
Dadas dos mujeres haciendo la tijera furiosa entre ellas y sus papos son lo suficiente carnosos, podría darse un efecto de vacio que hiciera que ambas quedaran pegadas momentaneamente, generando un sonido de "POP" al soltarse?

-. No se, yo estoy acá contando de mi viaje a Torres del Paine.

Ah disculpá capo, cha.


Anyway, eran las 5 am de la mañana y debido a la insistencia del guarda parque chileno, que sólo se podía pasar una noche en el campamento, me desperté, guarde todo en la mochila y emprendí mi ascenso a las Torres.

Claramente no me subía la suficienta agua al tanque porque estaba permitido dejar las cosas en la carpa momentaneamente y luego pasar a buscarlas, pero no, hice el ascenso con ventipico de kilos a cuestas, algo de lo que tomé conciencia una vez arriba cuando se me acercó una tana y me dijo "Vas a acampar en las torres?" and I was like

Anyway, aca el video del ascenso (yes it's me gimoteando all over the place) and some pics

<iframe allowfullscreen="" class="YOUTUBE-iframe-video" data-thumbnail-src="" frameborder="0" height="266" src="" width="320"></iframe>

Estaba bastante cansado pero había llegado y con lo justo, porque una vez que amaneció, el clima se vuelve una mierda total, pero TOTAL y se ponen unas nubes del orto que no te dejan ver un choto y te hacen pensar que dios te odia por haberte hecho algo asi.

Pero bueno, hice el descenso, pasé por la proveduría y no tenían una mierda para venderme porque no se que había pasado así que otra vez comiendo un par de barras de manzana y bla. Me relajó un poco ver esto. Yo medio contrariado y esta pareja está llevando a los bepis encima.

Eso me devolvió esa sensación de "grow some fucking balls dude" y fui a paso constante al camping "Los cuernos". Llegué tipo 18 hs, no había donde poner la carpa, no había ya disponibilidad de comprar cena y si no me apuraba no iba a poder bañarme (llevaba 3 días sin bañarme, casi 4). En la proveduría no había un choto para comprar y comer asi que otra vez, me fui a dormir sin comer.

Dormir fue todo un tema porque al no haber lugar en el camping tuve que acampar montaña arriba donde la carpa me había quedado a 20 o 30 grados inclinada (si, a la mañana me desperté comiendo carpa con la boca). Pero, al salir afuera me encontré con esta vista.

Sin dudas que haya sol hacía que todo fuese mejor, sin dudas, porque a mi si no veo el sol por varios días me quiero pegar un tiro en la chota. Motivo por el cual vivir en Canadá no me hubiese resultado muy copado durante los inviernos, más que nada rodeado de una mina un toque inestable.

Pero todo este razonamiento vendría después mucho después.


Anyway, cambié de laburo y es todo un tema porque laburé 10 años en el mismo lugar y empezar de cero con otra cultura es medio dificil.

Suena DukeDumont  - I Got U

<iframe allowfullscreen="" class="YOUTUBE-iframe-video" data-thumbnail-src="" frameborder="0" height="266" src="" width="320"></iframe>
Video que no pega con la canción. Porque la canción la canta una mina que dice que mientras que tenga a su pareja, no le hace falta nada, pero el video muestra a un tipo de color que parece a bit of a shut-in que se compra un casco de realidad virtual para vivir una vida de fantasía. Ni bien comienza, se pone el casco y se hace blanco. Polemico. Bottom line, la canción está buena y te da ganas de tener una vida como la que se ve en el video.

09 December, 2015

Torres del Paine Capitulo 2: The power of the mind and butter (mostly butter)

Publicado en Wed 09 Dec, 2015 04:15 por Knoxville ( en Noticias de Ayer
Me desperté por la mañana 5.30 am y hacia un frío de cagarse del tipo que salis de la carpa y mientras guardás todo, te tiembla todo el puto cuerpo, but I was a man with a mission

-. Mearle la carpa a las yanquees mientras dormían?

Eh no, but to be honest lo pensé bastante

Dejé atrás el camping y mientras deshacía los 12 KM me iba hablando a mi mismo para no pensar en el hambre y el frio sobre cualquier tema.

Me encontraba en un punto en el que me parecía todo tan bizarro que me hacía reir a mi mismo, llegando al punto de estar a los gritos, total no importaba un carajo porque no había nadie por kilómetros y kilómetros. Gritaba toda la ira contenida en los últimos meses y encontraba todo el asunto liberador.

Obvio que pasando las horas me crucé con alguien en pleno grito, un europeo medio stiff up the ass que claramente se quedó asi O.o pero me entré a reir de la situación lo que lo asustó me pareció. But well, I gave zero fucks.

El clima no era tan adverso y hubo sol durante la mayor parte del trayecto.

Finalmente pude ver a los lejos el complejo que había visto en la entrada al parque lo que significaba para mi una sola cosa: comida

Llegué al Hotel Las Torres (how original) y pregunté si tenían comida para gente que no se hospeda. Me dicen que había un menú y antes que me diga que tenía ya estaba sentado. Era tal el hambre que me traen primero cubos de manteca  para untar y me los comí.

Si, comí manteca sola y fue como si me inyectaran cordura en este momento. Venía soñando despierto con una milanesa napolitana y te digo que jamás desee tanto algo como eso, salvo cuando era adolescente y soñaba con entrarle a todas las secretarias del ruso sofovich.

El pan lo metí todo en la sopa y me lo comí. El plato principal duró 2 minutos y el postre fue un flash.

Ahora que pienso en todo lo ocurrido, supongo que debo estar en algun video grabado por los otros que estaban ahí titulado "Dude eats like a fucking animal a whole meal" con una duración de 5 minutos total. Pero yo, en ese momento era el tipo más feliz de la tierra.


Había hecho los 12 Km y con el combustible lleno, I was like

Asi que me fijé en el mapita y me dediqué a ver el paisaje

A medida que se subía en el terreno se podía ver que del otro lado del parque el clima no era tan bueno

A mitad de camino llegué a un refugio y tenía una proveduría, hiper recontra cara pero en este punto pagaba krugerrands por un paquete de Lays

Finalmente llegué al campamento base

El lugar estaba dentro de un bosque por lo que era muy humedo y frio

En este campamento no había ducha y sólo se podía pasar una sola noche, porque la idea es que llegues, duermas y a la madrugada subas a las torres. Tiene que ser a la madrugada el ascenso porque cuando termina de salir el sol el clima se vuelve una mierda con ventanas a la calle. Asi que comi unas galletas y me fui a dormir. Mañana sería el ascenso.


Anyways suena Waiting for love con el video copado de la historia del perro. El video original del viejito que se cruza todo el pais en la silla de ruedas para echarse un talco con la vieja no le llega ni a los talones and quite frankly lo veo demasiado cliché y poco creible.

<iframe allowfullscreen="" class="YOUTUBE-iframe-video" data-thumbnail-src="" frameborder="0" height="266" src="" width="320"></iframe>


04 December, 2015

Torres del Paine Capitulo 1: Preparativos (or lack of it)

Publicado en Fri 04 Dec, 2015 04:26 por Knoxville ( en Noticias de Ayer
Con dos días de anticipación, decidí irme a Torres del Paine, con la idea de hacer el circuito de la O.

El circuito de la O es una caminata o trekking de 93 KM alrededor del Parque Nacional Torres del Paine


Antes había desistido de ir porque había leido en su  momento que lo mejor era hacerlo con guía por la peligrosidad del circuito, la carencia de lugares donde comprar comida (ellos te cocinan y llevan las cosas para hacerlo), evitar llevar una pesada mochila en la espalda los 93.2 KM y bla bla bla.

Todo esto por una suma de 1500 dolares por 6 noches de guía llevándote. O sea te la vendían como que LO NECESITABAS pero yo pensé "meh" y decidí ponerme mi remera preferida

Ahora quería ir de verdad, pero tampoco estaba dispuesto a pagar esa guita por un tipo que me lleve de la mano y me digas "VES NENE ESTO ES UNA PIEDRA, ESTO ES UN RIO Y ESO ES PASTO. NOW, WHERE'S MY MONEY? THANKS. NEXT!"

Asi que decidí comprarme algo de equipo para ir y ya. Primero fui a comprarme una carpa y como iba a ir solo me compre la carpa Doite ZOLO Especial. Yes, modelo Zolo y Especial.

Un par de bastones de trekking, una linterna, una campera y listo. Metí todo dentro de una mochila vieja de toda la vida y salí.

Caigo a Puerto Natales sin reserva ni nada y no había un puto hostel con cama libre me dicen en la terminal.

Me pongo a hablar con dos mexicanas que estaban en la misma y justo el flaco de la terminal nos dice hay una habitación para tres. Listo vamos y cuando llegamos era una habitación para dos con una sola cama matrimonial.

Las minas se tensionaron pero les dije "yo duermo en el piso con la bolsa de dormir, tranqui" (porque no había ni colchon extra) y ahi relajaron las tetas y nos quedamos hablando largo rato antes de dormirnos.

-. Dale Knox vamos a lo importante, te las cogiste o no?

Eh, no. Igual no estaban buenas.


Fine! Bueno, llego al Parque Nacional y lo primero que noto es que el clima era muy adverso y el terreno estaba muy embarrado de una fuerte lluvia que había habido durante la noche.

Y con el clima cambiante que era sol, lluvia, sol, viento, lluvia, llovizna, llovizna, lluvia, sol, a la media hora estaba empadado, tema no menor porque el unico pantalón que tenía era el puesto.

Si si, a ver, me fui con lo puesto porque todo en mi vida me chupaba un verdadero huevo y lo importante era moverme.

Bueh después de estar caminando la mitad del día en ese lodasal me dio hambre y lo unico que tenía era una caja de barras de cereal fort de manzana y la bolsita de granola del avión.

Me estaba cagando de hambre.
Pero seguí con la esperanza de que al llegar al campamento iba a encontrar comida y buen descanso.

Llego tipo 19 hs al camping. Toco a la puerta de la casilla sale el guardaparque y digo "Hola vendés comida?"

-. No, por el tema del barro no vinieron las provisiones asi que no te podemos ofrecer nada.

Yo: Nada?

-. No, tenemos sólo para nosotros.

Yo: (silencio)

-. Vas a pasar la noche? Tenés que pagar

(oh for fuck sakes) Le pago y el tipo me dice "Mirá que se cerró el paso para el circuito de la 'O' por el mal clima y no sabemos cuando va a reabrir. Minimo 2 o 3 noches más seguro."

Yo: ¬¬ 

Me voy a poner la carpa. Empieza a lloviznar.

Me meto medio embarrado y mojado dentro de la carpa y me acuesto. Afuera escuchaba a lo lejos dos minas yanquees insoportables que estaban onda "OU EM YI SHARON AI MIN LAIK IU NOU, LAIK TOURALI LIVING AUER LAIVS TU DE MAX ".


A los 15 minutos de estar adentro mi calor comenzó a condensar la carpa por dentro y se empapó por completo.

Estaba recontracaliente.

Tenia hambre.
Tenia Frio.
Estaba incomodo.
Estaba sucio y encima el paso estaba cerrado para hacer el circuito de la O por 2 o 3 noches más.

Empecé a pensar en miles de cosas, en como mierda había hecho esto, como iba a venir asi de la nada, sin mínimo planificado, que era un pelotudo, que tenia mierda en la bocha y de repente me dije BASTA.

Ok, esta mierda pinta bastante jodido y me lo busqué solo pero rebotando dentro de la carpa no voy a conseguir nada. Había que ir al plan B. Hacer el circuito de la W (76KM).

Agarré el mapa que me habían dado con el pago de la entrada al Parque y evalué lo que significaba. Tenía que deshacer los 12 KM y caminar 10 KM más para llegar al campamento Base. Si, eran 22KM  bastante por caminar en un día pero si me levantaba temprano y le metía pata, tenía chances. Asi que me comi un par de cereal fort más, lo que quedaba del maní y me puse a dormir.

Creo que pude dormir bien y al toque porque además del cansancio, lo clave fue que después de todos lo que estaba viviendo previo del viaje, donde nunca me había amigado conmigo mismo en años, que siempre me daba con un caño por lo que pasaba con mi novia y no poder resolver la situación, por primera vez después después de mucho tiempo me estaba hermanando conmigo mismo y fue como si una puta warm blanket se posara sobre mis fucking hombros.

Hold on, more feels to come in the next posts. Have in mind


Anyway gracias por la buena onda de los comments!!

Ahora suena Avicii vs Nicky Romero - I Could Be The One

<iframe allowfullscreen="" class="YOUTUBE-iframe-video" data-thumbnail-src="" frameborder="0" height="266" src="" width="320"></iframe>

Este video me mata. La mina le tira "Creo que odio mi vida" la otra le sugiere que es porque engordó y le dice "creo que debería comerte esta ensalada" a lo que la mina le responde "Creo que deberías comerte una poronga"

Se va al carajo a Barbados a tocar bultos y darle zero fucks a la vida


Si si, ya se que se la lleva puesta un bus pero me quedo con lo bueno.

02 December, 2015


Publicado en Wed 02 Dec, 2015 05:46 por Knoxville ( en Noticias de Ayer
De las cosas que me arrepiento en los últimos años, no haber seguido con el blog es una de las mayores. 

J Knox Diciembre de 2015.

-. Knox, me gustan mucho pero mucho las pendejas porque más que nada se que son bastante trolas, pero el "holis" me la baja hasta el sótano. Que puedo hacer?

Escuché la pegunta de mi amigo, pero me excusé y me fui a mi casa sin mediar palabra. Las cosas no estaban bien en mi vida. And that was an understatement.

Estaba con una inercia de inactividad, donde vivía con la ilusión de que mi en ese entonces novia, que estaba del otro lado del mundo vuelva y yo esperándola, dado que me prometía cada mes que iba a volver, pero siempre algo surgia.

La situación era bastante obvia, la mina no volvería nunca más, pero yo estaba ciego y le creía cada vez que me decía que estaba pensando en venir.

En busca de respuestas tomé mi libro

Y en base a las elecciones que venía tomando fui a la página correspondiente y pude observar cual sería mi final.

"Esperando como fiel novio, recurres a métodos clásicos par mantener esa fidelidad. Si bien perfectamente aceptable en un comienzo, con el correr de los meses desarrollas un adicción por el porno. Primero te mofas de la clásica pregunta "Tired of looking at porn?" gritando "NOT THIS GUY!", pero al rato la frase cala profundo en tu ser.

Comienzas a sentir que ya lo viste todo y que ya no es lo mismo. Intentas cada vez más llenar el vacío pero no hay caso. En un momento de desesperación, compras un Smart TV 3D para acceder a la siguiente frontera en entretenimiento para adultos.

Tras varios días sin responder el teléfono, tus padres acuden a tu casa solo para encontrarte muerto, petrificado frente a la TV con los anteojos puestos y la pelicula "Backdoor Sluts 9" en loop,

No hay funeral."


Ante este panorama de suicidio en vida, estaba más que claro que necesitaba cambios en mi vida.

En este momento de epifanía, recibí un mail de mi novia diciendo que venía para fin de año para pasar las vacaciones juntos. Abri 20 tabs y continué como siempre.

Finalmente, luego de intercambiar varios mails, planificar las vacaciones y bla, la mina me termina diciendo que no va a venir porque se le complicó.

Se sintió mas o menos así.

"La concha de tu vieja en una patineta comprada por deal extreme que tuviste que ir a retirar de la aduana en Retiro", pensé por dentro, pero tiré un "no hay problema =/".

Esto lo hizo a días de que empezaran mis vacaciones y no sabía donde ir en ese momento y no tenía forma de planificarlo. Fue así entonces que decidí ir, en cuestión de dos días, a Torres del Paine, Chile, a hacer el circuito de la O de más de 90 km a pie.

Pero por cuestiones que luego entendería, decidí no prepararme nada de nada. Solo sacar el pasaje, comprar una carpa para una sola persona y nada más.

When we return, Knox goes to Torres del Paine unprepared and is kinda slapped in the face by this choice.

Anyway, it's good to be back,

Ahora suena the thrill is gone by Kygo

<iframe allowfullscreen="" class="YOUTUBE-iframe-video" data-thumbnail-src="" frameborder="0" height="266" src="" width="320"></iframe>

No heroes, villains, one to blame
While wilted roses filled the stage
And the thrill, the thrill is gone
Our debut was a masterpiece
But in the end for you and me
Oh, the show, it can't go on

We used to have it all
But now is our curtain call
So hold for the applause
And wave out to the crowd
And take our final bow
Oh, it's our time to go
But at least we stole the show
At least we stole the show...

30 October, 2015

Fixing a hole II.

Publicado en Fri 30 Oct, 2015 16:02 por Sebastián Santisi ( en Sebastián Santisi
Dejar las ollas en remojo es algo bueno. Dejarlas en remojo más de una semana no tanto. No sé si la acidez de probablemente un tuco, o si algo que fue evolucionando en el tiempo pero la onda es que la vez pasada quise hacerme unos fideos e inundé la cocina.

Una foto vale más de mil palabras. Este es el hoyo mayor, pero en total eran cuatro:

(Alguien que esté cursando Hidráulica puede estimar el diámetro del agujero en función del chorro...)

¿Cómo emparchar una olla de aluminio?, lo más sensato que se me ocurrió fue remarcharla... digamos que las asas suelen estar remachadas y no se escapa agua por ahí. Mismo material, algo que llena el agujero, una cabeza de cada lado.

Para los que no saben lo que es un remache o menos un remache macizo (de 10 ferreterías/zinguerías donde pregunté salvo el que me los vendió los demás no tenían ni idea), esto es un remache:

Primer paso, perforar un agujero donde entre el remache:

Después lijar del lado de adentro para sacar las rebabas y que la cabeza haga tope bien, así es como calza antes de remacharlo:

Y ahora a remachar, mi reino por un yunque y por un martillo de cabeza redondeada, pero bueno una silla y un cortafierro con un martillo de clavar sirven para improvisar. La última vez que usé un remache que no fuera pop fue en primer año de la secundaria, hace 20 años:

Conste, así le ponen los remaches a una olla en una fábrica de ollas, con esa prensa cualquiera remacha.

Ahora a la prueba hidráulica, no sé si se ve, pero hay una gotita minúscula asomando por debajo, es muy difícil martillar para que el remache se expanda bien sobre lo que estaba martillándolo:

Vuelta al "yunque", el remache quedó horrible y cagué a palos la olla pero al menos no perdió más:

Repetir para los otros 3 remaches:

Y ahora, después de retocarlos un poco, la prueba hidráulica de en serio:

Misión cumplida, por 80 centavos el remache y media horita de trabajo recuperé la olla:


Muchas veces el feedback que recibo a estos posts es el "ay, vos sabés, yo no puedo". Como dije en el texto, llevaba 20 años sin poner un remache, así que si alguna vez dominé el arte (creo que el Cristo de Borja que fue el farol que hice en hojalatería dice que no) eso fue sepultado hace tiempo.

Acá va la evolución de los 4 remaches que puse, la práctica hace al maestro, y a no desanimarse si el primero queda horrible, cualquiera de estas técnicas se domina intentándolo un par de veces. El último es como deberían haber sido los otros 3:

06 July, 2015

Elementary Os (Freya) Primeras Impresiones

Publicado en Mon 06 Jul, 2015 05:31 por aleperno en Aleperno

La última vez que cambié de distribución fue allá por el 2013 cuando instalé Debian Weezy en mi por aquel entonces nueva Dell Inspiron 14R (podés leer más aca). Aquella vez cambié de Ubuntu (10.04 creo) a Debian 7. Hace un par de días compré un disco Samsung SSD, así que pense en tal vez probar otra distribución de Linux. Luego de hablar con un par de amigos, opté por probar Elementary Os.

Elementary es una distribución basada en Ubuntu con una apariencia a OS X. La versión actual se llama Freya (0.3) y está basada en Ubuntu 14.04. Lo que primero llama la atención es el diseño, los colores, ventanas, transparencias e íconos, bien logrados; algo esperable de una distribución que se proclama “una alternativa a OS X”. Es bastante ligero y veloz, considerando que posee pocos programas por defecto (por ejemplo no posee GIMP ni programa de ofimática) y muchos de ellos son programas propios. Por ejemplo el editor de texto por defecto es “scratch” y no el usual “gedit” (aunque son muy parecidos), asimismo el administrador de archivos se llama “pantheon-file”.

Desafortunadamente, estas alternativas no funcionan muy bien. Scratch suele colgare cuando falla la apertura de un archivos, cuando gedit no lo hace. Panteon-file por lo general funciona bien, sin embargo posee algunos comportamientos extraños y características faltantes, los dispositivos de almacenamiento USB no se montan automáticamente, no puedes previsualizar elementos en la papelera, y seleccionar múltiples elementos con Shift + Click Mouse puede ser realmente tedioso (si, no es chiste).

Hay otros problemas, algunos heredados de Ubuntu, tales como fallas en el login gráfico luego de una actualización, desconexión aleatoria del wifi, la pantalla del login no muestra correctamente un segundo monitor, etc. También tengo la sospecha de que la batería dura menos de lo usual (aunque no he podido corroborarlo fehacientemente, al no poseer una prueba estandar).


Es un OS realmente bonito con gran potencial, con un gran equipo de diseñadores detrás de él. No recomendaría instalarlo todavía, pero si hacer un seguimiento cercano ya que no dista mucho de ser una gran distribución, aunque esos pequeños errores que lo apartan de serlo pueden ser razón suficiente para no utilizar este sistema.

27 June, 2015

(English) Machine Learning first Step

Publicado en Sat 27 Jun, 2015 03:54 por aleperno en Aleperno

Disculpa, pero esta entrada está disponible sólo en English.

17 April, 2015

Publicado en Fri 17 Apr, 2015 15:23 por YouTube Help en EyeonPhiuba
Views: 33515708
0 ratings
Time: 03:56 More in Howto & Style

28 February, 2015

Panda vs Zombies: my new Android video game

Publicado en Sat 28 Feb, 2015 00:59 por facundoolano en "Facundo Olano"


Over most of last year I’ve been working along a couple of college friends on an action game for Android and it’s finally out. It’s done using cocos2d-x in C++, which is by no means a language I like, so hopefully I’ll get to write here about the experience I had building it.

In the meantime, here’s the link to install it from the google play store and the trailer of the game, for those who are interested in checking it out.

<iframe allowfullscreen="true" class="youtube-player" height="365" src=";rel=1&amp;fs=1&amp;autohide=2&amp;showsearch=0&amp;showinfo=1&amp;iv_load_policy=1&amp;wmode=transparent" style="border:0;" type="text/html" width="594"></iframe>

27 February, 2015

Foto De Color Cambiante

Publicado en Fri 27 Feb, 2015 18:57 por aleperno en Aleperno

Si no vivis en un termo, seguro que en estos dias te llegó una imagen de un vestido y una discusión, si era blanco-dorado o azul-negro. Junto con esta imagen también comenzó a circular en twitter una imagen con un “efecto” similar.

Como se puede ver en la imagen, tanto las uñas como los labios tienen un color suave. Pero al hacer click en la imagen en twitter se veia algo como esto:

<script async="async" charset="utf-8" src=""></script>

Es decir, en el timeline se ve de color claro y al acceder a la foto se ve de color oscuro. Si bien pareciera magia o alguna rara ilusion optica, no se trata ni mas ni menos de que un simple “truquillo tecnico”.

Al descargar la foto en formato png, era evidente que las areas del labio y uñas poseian cierta transparencia, es decir que permitian que se vea en cierto grado el color del fondo. Si vemos un timeline de twitter, el fondo con el que contrastan los tweets es blanco, pero al abrir una imagen, el fondo es negro. De este modo se logra el efecto. El mismo es evidente al abrir el programa en GIMP (simil Photoshop) y cambiandole los colores del fondo.

Screenshot from 2015-02-27 15:46:30 Screenshot from 2015-02-27 15:46:51

14 February, 2015

Encuestas en Redes Sociales

Publicado en Sat 14 Feb, 2015 17:04 por aleperno en Aleperno

Es bastante común hoy en día encontrarse en alguna red social como Twitter o Facebook alguna “encuesta” que invita a dar su opinión cuyas opciones son FAV o RT en el caso de Twitter y Like o Share en el caso de Facebook. Lo que quizá no nos sea intuitivo en primera instancia, es que esta metodología de encuesta facilita a quien la crea, “incentivar” un resultado por sobre el otro.

No es intuitivo porque uno es el que emite opinión, así que ¿como podría alguien manipular los resultados de la encuesta? Bueno, si bien el creador de la encuesta no puede alterar los resultados propiamente dichos, puede deliberadamente elegir que opción propagar más haciéndola más probable a la elección. Esto se logra aprovechando dos características de estas redes sociales. La primera es el alcance distinto que tienen las opciones fav-rt, like-share y el segundo es el scope  “solidario” que tienen las redes sociales.

Fav vs Rt

Lo primero a analizar es las diferentes opciones de voto y sus diferencias. La diferencia primordial de ambas opciones es la propagación que tienen. En twitter al favear un tweet, esa acción es “privada” y no se publica a nuestros seguidores; en cambio al retwittear un tweet el mismo va a aparecer en el timeline de nuestros seguidores. Lo mismo sucede en Facebook, con la diferencia que el like en determinadas circunstancias le puede aparecer a alguno de nuestros amigos, pero en muchísimo menor grado que si compartimos la “encuesta”.

Veamos esto de manera gráfica

Este gráfico representa una porción de una red social de 16 individuos. Cada punto es una persona y las conexiones representan amistad / seguimiento. Supongamos que el punto central (azul) es el que inicia una encuesta, en principio sólo aquellas personas que sigan o sean amigos de ella van a tener acceso a la encuesta. Veamos el siguiente gráfico

Ahora vemos como todos los puntos con conexión al punto central, se volvieron partícipes de la encuesta y es acá donde radica el punto más interesante de la cuestión. Veamos que sucede si los tres puntos superiores dan rt / share a la encuesta (verde)

Como sucedió con anterioridad, ahora los puntos conectados a ellos también tienen acceso a la encuesta. Acá vemos como los puntos conectados a aquellos que dieron fav / like (los puntos inferiores) no tienen acceso a la encuesta, lo que de por sí es un limitante.

¿Como el alcance puede ‘sugerir’ un resultado?

Pudimos ver como para que la encuesta tenga llegada más allá de la red del punto origen, es necesario que se le dé share / retweet a la encuesta. Y acá es donde entra en juego parte de los algoritmos y esencia de las redes sociales.

  • Uno es más propenso a ser amigo / seguidor de aquellas personas con la que uno tiene un pensamiento afín.
  • Uno es más propenso a replicar comportamiento de nuestros amigos / seguidores.
  • Facebook nos publicará más sucesos de las personas con las que tenemos pensamiento afín que con las que no.

Este último caso quizá no sea tan evidente, pero si analizamos un poco nuestra cuenta de Facebook, nos daremos cuenta que en el newsfeed (página principal) en su mayoría nos aparecerán noticias de con quienes tenemos interacción (likes, mensajes, etc) que con los amigos con los que no tenemos interacción.

Entonces combinando tanto el alcance de compartir vs dar like, con las características arriba descriptas, es evidente como si deseamos que una opción gane por sobre otra, esa opción debe ser la de compartir. Obviamente hay varios factores que inciden en una encuesta de este tipo y lo sugerido no es deterministico, es decir que no es garantía de elección, pero es interesante analizar como puede incidir en un resultado.

28 January, 2015

Lamento Boliviano Argentino

Publicado en Wed 28 Jan, 2015 20:31 por aleperno en Aleperno

Dada, entre otras cosas, mi posición política más que clara me cuesta mucho escuchar una cadena nacional; sin embargo hace dos días decidí (al menos por poco tiempo) escuchar la cadena nacional del día de la fecha. Si bien sabía que la presidente había sufrido cerca de navidad una fractura de tobillo, me sorprendió que apareciese en silla de ruedas.

Si a algo estamos acostumbrados los argentinos, es a la victimización política, donde todos los males del presente fueron heredados de las sucesivas dictaduras del siglo XX, las políticas de Alfonsín, las relaciones anglo carnales de Menem y DeLa Rua o a causas externas como la CIA, Clarín, la crisis de EEUU e incluso podríamos pensar en una bizarra aplicación del efecto mariposa y culpar a un nenito Europeo que decidió comprar una golosina europea en vez de una Arcor.

En parte considero que esto no es propio de la clase dirigente, sino una expresión de la sociedad en general. Nos gusta victimizarnos, lamentarnos, echarle la culpa a terceros, incluso utilizamos estos métodos para obtener beneficios económicos y políticos que simplemente nos degradan a nivel sociedad.

Ahora tomemos otro ejemplo, Alemania, durante el siglo XX sufrió dos Guerras Mundiales y desde el final de la última hasta los 90 que gran parte de su territorio se encontraba en dominio de la URSS. Hoy en día es potencia mundial, practicamente el prestamista de la UE y si bien estoy trazando con brocha gruesa hay que considerar que Alemania estuvo en peores situaciones que la nuestra. Cientos de miles de muertes por las guerras, ciudades destrozadas, hambrunas debido a la depreciación del marco y el bloqueo, sanciones económicas, etc.

Y si bien Argentina y Alemania son países diferentes en contextos diferentes, estos hechos me parecen muy significativos. Otra comparación (quizá mas banal) es la de las dos mandatarias, Cristina Kirchner y Angela Merkel. Durante el gobierno de Cristina hemos visto como se pintaba de demonio a distintos sectores como el Campo, el Grupo Clarin, EEUU, la “puta clase media”, etc. A esto se le suma el drama actoral mediático desde el fallecimiento del ex presidente Nestor Kirchner, pudiéndola ver vestida exclusivamente de negro, haciendo en cuanta oportunidad tenía mención a éste entre un pobre lagrimeo. En esta última cadena nacional se la vió vestida en su totalidad de blanco (casi queriendo hacer una alusión celestial) y en silla de ruedas.

Si bien es cierto que sufrió una fractura de tobillo, para una presentación de estas características tranquilamente se podría haber sentado en un sillón regular. Ahora comparemos con la otra mandataria, que años atrás sufrió una fractura de pelvis (también cerca de navidad) y en los primeros días de Enero del siguiente año se mostró sin demasiados esfuerzos andando en muletas.

Independientemente de quién en un futuro (tanto como individuo como partido político) sea el que gobierne este país, me gustaría que como sociedad tengamos un poco más de conciencia y responsabilidad, no sólo para hacernos responsables de nuestros propios errores, sino para reconocer a quienes no lo hacen.

En 2015, votá a conciencia y no seas pelotudo.

21 January, 2015


Publicado en Wed 21 Jan, 2015 22:55 por Sebastián Santisi ( en Sebastián Santisi
Luego de superado el temor inicial de secar en Buenos Aires surgió la idea de intentar la experiencia grupal de hacer salames. Este post no va a hablar sobre los salames sino de que cuando estaba yendo a comprar la carne para los mismos me tenté y compré una bondiolita para hacer fiambre.

La compra del relleno de los salames la estaba haciendo en La Saladita en Constitución... que me recordó bastante a 7 Cajas. La granja acusaba una dudosa cadena de frío, todos los cortes estaban tirados adentro de un changuito de supermercado, pero por fuera de eso los cortes se notaban frescos y para salar la pieza no tiene que haber pasado por cámara lo que ocurre con las bondiolas que se compran practicamente en cualquier frigorífico. Llevé una bondiola más o menos chica, de 1,8kg.

La receta que usé está tomada de las que habían publicadas en Taringa!, elegí con el criterio de que la parte del salado inicial se pudiera hacer en heladera y el secado al fresco.

Arranqué desgrasando un pelín y salando:

Así como venía, fue a la heladera por 3 días. Largó un poco de agua que fuí sacándole, pero tampoco tanta:

En paralelo a eso, puse en remojo en vino blanco ajos, especias y eso para el marinado:

A los tres días saqué de la heladera, le lavé toda la sal y la puse un poco en remojo en agua para terminar de sacar los granos, no sé si se nota un poco el cambio de color, pero está más pálida que el primer día:

Después del remojo la sequé y la metí a marinar durante un par de horas con el vino, dándola vuelta de vez en cuando para que chupe de todos lados:

Después de eso, a escurrirla bien; la dejé de una noche para la mañana siguiente:

Y bueno, ahora viene la faena de condimentarla, darle la forma y dejarla lista para el secado. Para los condimentos usé pimentón, curry y nuez moscada. La idea es amasarla sobre eso hasta lograr una forma más o menos bondiolesca (con el tiempo en la heladera, el escurrido y eso la forma no era muy regular):

Y de ahí a atarla. Usé papel de manteca, debería haber conseguido film microperforado para fiambres pero no tenía. El laburo del atado es complejo, onda atar un matambre. Hay que apretarla bien fuerte para que conserve la forma y se prense:

De ahí fue a mi fiambrera a secarse:

A los 25 días vi que había reducido mucho en tamaño y que en donde estaba abierto el papel se veía un poco de mufa. De consistencia ya estaba bastante seca pero no me quise arriesgar a cortar el curado ahí. Lo que hice fue abrirla, limpiar con vinagre los hongos y volver a atarla. Se puede ver en la foto cómo los piolines que el primer día estaban tirantes ahora estaban todos flojos por la reducción en el volumen. El atado de esta vez fue casi testimonial porque estaba bastante armada:

Si bien, como ya dije, estaba ya bastante seca, quedó colgada 10 días más. En total, todo el curado fueron 35 días. Esta vez ya tenía tan poca humedad que no volvieron a crecer hongos.

El resultado final, de 1,8kg fresca terminó pesando 1kg:

02 January, 2015

10 December, 2014

Lo que mata es la humedad

Publicado en Wed 10 Dec, 2014 23:21 por Sebastián Santisi ( en Sebastián Santisi
En poco más de una semana tengo agendado preparar una feijoada, y si bien generalmente la hago con un par de faltantes, esta vez vienen 25 a casa y no puedo prescindir de ponerle carne-de-sol.

Ahora bien, en CABA no se consigue ni charqui, menos que menos la tradicional carne-de-sol brasilera, así que intenté experimentar preparándola yo. Encontré un montón de recetas, todas dispares entre sí, y con bastante poca fe arranqué con el ensayo y error.

La mayor parte de las recetas que encontré decían usar cortes de carne magros, sin una gota de grasa, sin tendones, etc. Con una humedad ambiente de más del 80% (o su equivalente en punto de rocío) todo el tiempo habían altas chances de que se echara a perder por lo que fui a por ensayar con un kilo y medio de roast beef de precios cuidados (dicho sea de paso, ya me pasó que la carne al vacío de Dia% me viniera en mal estado, si se me pudría a mí no hubiera sido más grave):

Disclaimer: Intenté ser cuidadoso con el balance de blancos de la cámara; los cambios de color son consecuencia de las distintas etapas del proceso.

El primer paso fue desgrasarla, hasta donde pude, era un corte de mierda:

Acá voy a insertar el comentario, y perdón si esto más que una receta es un no-debería-haberlo-hecho-así, sobre que varios videos que miré después, la mayor parte de las veces lo preparaban con carnaza común, así que la grasa y los tendones importan poco y nada.

Después el salado. Las recetas se dividían entre sal gruesa y sal fina. Usé sal gruesa. En base a los resultados estoy bastante convencido de que hubiera sido mejor sal fina y no salar a lo bestia sino apenas cubrir:

Acá viene la parte que nadie me avisó. Al cabo de un rato, empezó a largar líquido pero líquido en cantidad zarpada y a apestar bastante a sangre, puede verse como la sal se estaba diluyendo en el líquido:

Esto era el sábado a la tardecita, el día estaba cálido y seco, estuve toda la noche drenando el líquido y cuando me fui a dormir preferí meterlo en la heladera inclinado para que escurriera un poco.

A las 6 de la mañana estaba levantando mucho viento y me levanté a cerrar las ventanas. Me encontré con que la cantidad de líquido era demasiada y resolví cambiar de estrategia porque así no se iba a secar ni en pedo. Puede verse como parte de la sal quedó adherida, la mayor parte quedó sanguinolienta en la fuente:

Después del viento se largó zarpada tormenta por lo que bajó la temperatura y subió la humedad. La carne al menos estaba escurriendo pero el ambiente no ayudaba mucho. Necesitaba calor seco y esto fue lo mejor que se me ocurrió:

La tuve todo el día con el caloventor, y para la noche del domingo si bien estaba mojada al tacto abajo, al menos ya no goteaba:

La cocina olía demasiado a sangre, por lo que tiré a la mierda toda la sal y también sacudí de los bifes toda la sal que estuviera desprendida. La próxima vez que prepare voy a usar sólo sal fina, dejar que se adhiera lo que se adhiere cuando la carne está fresca y lo voy a dejar escurrir colgado desde un primer momento. Con el método de la sal gruesa, si bien el secado anda bien y la carne absorbe la sal que tiene que absorber esa sal se llena de sangre y no está bueno:

Otra vez, no quise dejarlo a la noche afuera de la heladera así que hice un pequeño colchón de sal y lo mandé a la heladera otra vez:

A la mañana del lunes, la sal nueva no se había humedecido, pero las piezas seguían apestando a sangre por lo que directamente retiré todos los granos de sal, volví a colgar pero boca abajo para que se secaran mejor las puntas que estaban abajo y donde más había escurrido el día anterior:

Todo el lunes quedó colgado al fresco, sin darle calor. El lunes a la noche si bien todavía le faltaba a las partes más gruesas el secado ya estaba bastante bien. Usé la misma sal limpia que había quedado de la noche anterior y acomodé en la heladera:

Quedó todo el martes en la heladera, y este es el resultado hoy miércoles:

Todavía queda un pelín de humedad en las partes más gruesas, pero el curado está más que bien, el grosor de las piezas se redujo a la mitad. Quedará en la heladera un par de días más para seguir secándose; según como venga lo aguantaré hasta el día de la feijoada o lo mandaré al freezer cuando crea que ya está bien.

27 November, 2014

[Discusión]¿Trabajo Gratis o Trabajo Comunitario?

Publicado en Thu 27 Nov, 2014 05:03 por aleperno en Aleperno

¿Vos programás gratis? Esa fue la interrogante fulminante que me dió una amiga estudiante de diseño gráfico para basar su negativa a entregarme un archivo vectorizado de unos fonts que ella había hecho y a mi me parecían copados. La pregunta me pareció curiosa porque , he programado gratis, he traducido gratis, he arreglado cosas gratis, etc. La otra cosa que me llamó la atención es que no es la primera persona estudiante de diseño que se niega de manera rotunda a hacer un favor, la última persona también tiró una negativa rotunda al preguntarle si le interesaba hacer algúna idea de logo para esta página.

Lo más irónico de todo es que estas mismas dos personas me han pedido favores, mi amiga me pidió fotos de una pantalla de un shield LCD Arduino porque el font estaba copado (cosa que hice sin drama) y la otra persona pidió auxilio al reventarle la PC con su tesis adentro. ¿Acaso ellos son unos hipócritas / forros? ¿Acaso consideran que un favor tiene más peso que otro? No lo creo, simplemente creo que es el contexto en el cuál se desarrollan, un contexto donde no parece haber mucha co-operación entre pares y donde te enseñan que al cliente se le cobra hasta la vuelta del sacapuntas.

Pero existen otras facultades / contextos donde ocurre exactamente lo mismo, por ejemplo hace unos días descubrí el proyecto NerdBanker el cuál es una suerte de híbrido entre Rincon del Vago y MercadoLibre, en donde se comercializa material de estudio y quién los efectua recibe dinero. Como idea para que algún estudiante corto de recursos haga unos pesos o como idea conceptual de negocio me parece interesante, pero en concreto no me gusta. ¿Que pasó con la idea de colaborar? No hablo a un nivel comunista / utópico, sino la simple idea de ayudar al pibe que tenés al lado. ¿Por qué todo tiene que pasar por la guita?

En concreto yo he aportado algunas reformas de código a diversos proyectos libres como FBHT, he hecho traducciones en Launchpad y actualmente estoy traduciendo Invent Your Own Computer Games with Python, he escrito en latex diversos examenes y apuntes disponibles en mi página pública, etc; y no considero que regalo trabajo, sino que colaboro a mejorar herramientas / material que yo he tenido a libre disposición o que si no lo tuve, me gustaría que otros lo tengan. ¿Cuantas veces deseaste que haya un parcial de ejemplo de una materia? ¿Un apunte copado para tener como referencia? ¿Por que se suele ser tan reacio a hacer estas cosas?

Quizá lo que marque la diferencia es que yo veo las bondades de una comunidad de software libre, de ver como con la colaboración de mucha gente un proyecto se lleva adelante; ¿cuantos de nosotros hemos forkeado un proyecto en GitHub para probarlo y nos resultó fantastico? ¿Por qué no podemos expandir esto a otros ámbitos fuera del software, como la facultad?

Por suerte en mi querida FIUBA existe una hermosa comunidad de alumnos que suele ser muy colaborativa, incluso hay una wiki donde se sube contenido actualizado que es de gran ayuda para todos los estudiantes.

La idea de este artículo es que de pié a la discusión (los leeré con atención en los comentarios) para que opinen.


17 October, 2014

ARSAT-1 y la soberanía tecnológica

Publicado en Fri 17 Oct, 2014 02:35 por aleperno en Aleperno

Así como dice la frase que entre dos españoles hay tes opiniones, lo mismo parece estar sucediendo en Argentina; donde existe una gran polaridad entre adherentes y disidentes al Gobierno. Esto se ve en las opiniones que se expresan en las redes sociales, hay quienes aplauden ciegamente cualquier hecho del gobierno y también están quienes igual de ciegamente los critican. En el fondo generalmente hay muy poca objetividad, el objetivo de esta entrada es dar una mirada lo más objetiva posible (dentro de la subjetividad de quién escribe) respecto de la puesta en órbita del Satélite Argentino ARSAT-1.

El día de hoy, me tocó leer gran cantidad de opiniones. Quienes defendían la postura de que éste es un acontecimiento épico en el camino del desarrollo tecnológico nacional y quienes se “burlaban” considerando que se lanzó una heladera comprada en COTO. Ambas posturas (a mi criterio) son erróneas.

COTO Space Program

Una de las principales “quejas” que leí en contra del ARSAT es que en realidad no se trató de un satélite de 100% origen nacional, en parte esto es verdad y corresponde al paradigma de “épica” que últimamente utiliza el gobierno, sin embargo la realidad es que pocos (o ningún país) tiene la capacidad de desarrollar y fabricar todos los componentes de un satélite. Este satélite se compone de elementos desarrollados nacionalmente, fabricados algunos afuera y ensamblados localmente. Esto implica que la idea, la lógica del satélite surgió de la mente nacional, así como todos los productos de Apple son desarrollados en California pero fabricados en China.

El siguiente paso sería desarrollar la industria electrónica nacional para ser capaces de fabricar más componentes, no sólo para la industria aero-espacial, sino para satisfacer demandas industriales y comerciales. Pero esto es algo de lo que hablaré en el próximo segmento.

El satélite blanco de San Martín

Un recurso muy utilizado últimamente en los discursos, es el de constituir la idea de que todo paso del gobierno constituye un episodio en una historia épica, que posiciona a la Argentina en un lugar privilegiado mundialmente y se realizan acciones que quedarán en la Historia. Y es a este “paradigma” que recaen la mayoría de mis objeciones.

Sin nosotros no habría ARSAT-1 (link)

Estando en vísperas de un año de elecciones presidenciales ésta clase de frases abundan, tratando de afianzar la idea de que de suceder otro gobierno (o haber sucedido) estos proyectos no serian posibles. La realidad que éste no es el primer satélite Argentino (si geoestacionario), durante los 90 en la gestión de Carlos Saúl me toco el huevo izquierdo Menem se lanzaron diversos satélites bajo la órbita de CONAE (si…pun intended). Y si bien es real que este gobierno reactivó proyectos necesarios (diferenciar necesario de suficiente) para el desarrollo nacional, el uso político fue por varios órdenes mayor al impacto real de estos proyectos en la “soberanía tecnológica” (haciendo énfasis en que no es insignificante el impacto, sino muy grande el uso político).

Soberanía Tecnológica

Acá es donde a mi criterio hace agua el “relato”. SI, es ultra necesario tener satélites nacionales propios y es positivo que el gobierno financie dichos proyectos; pero la realidad es que dichos proyectos tienen poca correlación con el desarrollo de tecnologías nacionales a un nivel general. Es decir, debemos estar contentos de fabricar satélites propios, pero si luego la industria nacional no es capaz de fabricar (y por fabricar me refiero a investigar, desarrollar y crear) un celular, microondas, lavarropas, algo está mal. Y acá es donde recae una de las mayores dicótomas del gobierno.

Por un lado se financia con dinero estatal “mega”‘-proyectos, pero a la vez se dificulta al individuo o industrias desarrollar independientemente. Como dije en de mi anterior post respecto de la tecnología, Argentina y el Desarrollo Tecnológico, los países que hoy son grandes mecas de la tecnología lo hicieron gracias a la libertad y facilidad de los individuos a desarrollar. Esto NO es un hecho en Argentina, muy por el contrario las trabas a las importaciones y gravámenes impositivos lo dificultan.

Es una gran hipocresía por un lado aseverar la importancia de establecer telecomunicaciones nacionales, y a la vez si yo como radioaficionado deseo comprar una radio para desarrollarme en el área, gracias a los impuestos me saldría el doble respecto del valor comercial en EEUU. Por ejemplo una base Yaesu 2900R en EEUU sale 190 u$d lo que se traduce en $1600 acorde el dolar oficial ($8,5 que es al cual se importa), sin embargo en los mercados locales el precio ronda los $4000. Cuando este no se trata de un bien “suntuoso” como quiza podria ser un televisor LED.

Hace poco en un artículo de La Nación, salió la noticia de que la mini computadora educativa Raspberry Pi llegó a los 3,8 millones de ventas, destacando su rol en la educación de los chicos sobre computación y electrónica. Una Raspberry Pi B⁺ se vende en EEUU a 38 u$d que corresponderian a $330, sin embargo se comercia localmente alrededor de los $1000. Lo mismo sucede con los Arduino, cuyo precio del modelo MEGA 2560 R3 es de 27 u$d ($250) y localmente se comercializa a $900 la versión oficial y $400 la versión China. Y ni hablar de la disponibilidad de elementos adicionales que le dan aún más valor agregado a estos productos.


Es muy positivo que se afronten estos proyectos por parte de instituciones estatales (o con financiamiento estatal), pero la realidad es que poca correlación tienen estos con un desarrollo global de la industria nacional. Desde el discurso se reconoce la necesidad de que más gente se emplee en estos rubros, pero en la realidad las cuestiones burocráticas y/o económicas dificultan esto. Nos ponemos contentos de lanzar un satélite, pero en comercios de electrónica no se consiguen inductancias y/o integrados. 

Cuando se comprenda la idea (y se aplique) de que el vector de desarrollo nacional más que el Estado, debe ser mediante la posibilidad de que los individuos puedan desarrollar sus propias ideas y proyectos, es cuando estaremos bien encaminados hacia el verdadero desarrollo nacional y soberanía tecnológica. Mientras tanto, antes de abrir la boca, fijate cuán grande es el buzón. 

11 October, 2014

Better authentication for (no query strings!)

Publicado en Sat 11 Oct, 2014 17:08 por facundoolano en "Facundo Olano"


This post describes an authentication method for that sends the credentials in a message after connection, rather than including them in the query string as usually done. Note that the implementation is already packed in the socketio-auth module, so you should use that instead of the code below.

The reason to use this approach is that putting credentials in a query string is generally a bad security practice (see this, this and this), and though some of the frequent risks may not apply to the connection request, it should be avoided as there’s no general convention in treating urls as sensitive information. Ideally such data should travel on a header, but that doesn’t seem to be an option for, as not all of the transports it supports (WebSocket being one) allow sending headers.

Needless to say, all of this should be done over HTTPS, otherwise no security level is to be expected.


In order to authenticate connections, most tutorials suggest to do something like:

io.set('authorization', function (handshakeData, callback) {
  var token = handshakeData.query.token;
  //will call callback(null, true) if authorized
  checkAuthToken(token, callback);

Or, with the middleware syntax introduced in 1.0:

io.use(function(socket, next) {
  var token = socket.request.query.token;
  checkAuthToken(token, function(err, authorized){
    if (err || !authorized) {
      next(new Error("not authorized"));

Then the client would connect to the server passing its credentials, which can be an authorization token, user and password or whatever value that can be used for authentication:

socket = io.connect('http://localhost', {
  query: "token=" + myAuthToken

The problem with this approach is that it credentials information in a query string, that is as part of an url. As mentioned, this is not a good idea since urls can be logged and cached and are not generally treated as sensitive information.

My workaround for this was to allow the clients to establish a connection, but force them to send an authentication message before they can actually start emitting and receiving data. Upon connection, the server marks the socket as not authenticated and adds a listener to an ‘authenticate’ event:

var io = require('').listen(app);

io.on('connection', function(socket){
  socket.auth = false;
  socket.on('authenticate', function(data){
    //check the auth data sent by the client
    checkAuthToken(data.token, function(err, success){
      if (!err && success){
        console.log("Authenticated socket ",;
        socket.auth = true;

    //If the socket didn't authenticate, disconnect it
    if (!socket.auth) {
      console.log("Disconnecting socket ",;
  }, 1000);

A timeout is added to disconnect the client if it didn’t authenticate after a second. The client will emit it’s auth data to the ‘authenticate’ event right after connection:

var socket = io.connect('http://localhost');
socket.on('connect', function(){
  socket.emit('authenticate', {token: myAuthToken});

An extra step is required to prevent the client from receiving broadcast messages during that window where it’s connected but not authenticated. Doing that required fiddling a bit with the namespaces code; the socket is removed from the object that tracks the connections to the namespace:

var _ = require('underscore');
var io = require('').listen(app);

_.each(io.nsps, function(nsp){
  nsp.on('connect', function(socket){
    if (!socket.auth) {
      console.log("removing socket from",
      delete nsp.connected[];

Then, when the client does authenticate, we set it back as connected to those namespaces where it was connected:

socket.on('authenticate', function(data){
  //check the auth data sent by the client
  checkAuthToken(data.token, function(err, success){
    if (!err && success){
      console.log("Authenticated socket ",;
      socket.auth = true;

      _.each(io.nsps, function(nsp) {
        if(_.findWhere(nsp.sockets, {id:})) {
          console.log("restoring socket to",;
          nsp.connected[] = socket;


17 September, 2014

Pasantías en Alemania

Publicado en Wed 17 Sep, 2014 22:02 por Cristian Moleres ( en Comisión de Estudiantes de Ingeniería Civil
La Facultad de Ingeniería informa, a través del convenio firmado entre el Ministerio de Educación y el Servicio Alemán de Intercambio (DAAD), la apertura de la convocatoria anual de pasantías académicas y profesionales cofinanciadas, destinada a estudiantes de ingeniería argentinos, cuya duración es de 12 meses, sin posibilidad de prórroga.
La convocatoria, que ofrece hasta diez (10) becas, se estructura a partir de un programa de estudios en Alemania que comprende dos meses de curso alemán intensivo, un semestre en una universidad alemana para la realización de una pasantía académica y entre 4 y 6 meses de práctica supervisada por la universidad en una empresa alemana o, alternativamente, la realización de un trabajo de investigación aplicada en una universidad alemana bajo la supervisión de un investigador.
. Ser ciudadano argentino y residir en el país al momento de solicitar la beca.
. Ser estudiante en una universidad o instituto universitario de gestión estatal.
. Haber aprobado el 60% de las materias del plan de estudios de su universidad al solicitar la beca y adeudar por lo menos dos (2) materias al inicio de la misma (agosto 2015).
. Promedio mínimo de 7 (siete) con aplazos incluidos, en la carrera de grado.
. No superar los 26 (veintiséis) años de edad al momento de viajar.
. Tener conocimientos de inglés y alemán comprobados de acuerdo a lo estipulado en el presente reglamento.
. Comprometerse a realizar las gestiones y averiguaciones necesarias ante las autoridades de su universidad antes de viajar a Alemania con el fin de validar el proyecto de investigación que surgirá de la pasantía académica como tesis de grado o proyecto final y la pasantía profesional en la empresa alemana como Práctica Profesional Supervisada, según el régimen de cada centro de estudios.
. Comprometerse a regresar al país al finalizar el desarrollo de la beca y reintegrarse a sus actividades específicas.
La inscripción, tanto personalmente, por correo o vía internet, estará vigente hasta el 1ro. de octubre de 2014.
Completar formulario de postulación
Para más información sobre documentación solicitada, criterios de evaluación y selección, pautas para la realización del proyecto de investigación, beneficios de las becas y trámites de inscripción, descargar este archivo.

Jornada Técnica Transporte

Publicado en Wed 17 Sep, 2014 22:00 por Cristian Moleres ( en Comisión de Estudiantes de Ingeniería Civil

Jornada Técnica Transporte - 50° Aniversario CADECI

Jornada orientada al análisis de las oportunidades y desafíos que presenta la actividad local.
Miércoles 1° de octubre de 2014

Un sistema adecuado de transporte, en sus diferentes modos, por su gravitación en la calidad de vida de la población, su incidencia en la economía y el desarrollo del país, resultan claves, en particular en un país con la extensión y tipos diferentes de producción como el nuestro. A los factores de índole económica, hoy se suman en forma insoslayable los aspectos de seguridad y las demandas sociales en relación a la calidad de los servicios y su impacto en el ambiente. Durante la jornada se tratarán, en cada modo, los temas de actualidad.

Centro Argentino de Ingenieros, Salón Costantini
Cerrito 1250 – C.A.B.A

       Auspicia esta Jornada

La jornada es de acceso libre y gratuita con previa inscripción, (cupos limitados).

Programa preliminar

Miércoles 1° de octubre de 2014 

Apertura Jornada Transporte
Autoridades Invitadas - Palabras de bienvenida.
- Ing. Nelson Periotti – Administrador General de la DNV 
- Ing. Jorge Rodriguez - Presidente del Consejo Vial Federal
- Ing. Guillermo Cabana - Presidente de la Asociación Argentina de Carreteras

Transporte Vial - Moderador: Ing. Fabián Schvartzer
Bitrenes: Relevancia, principales desafíos.
- Ing. Fernando Abrate, Gerente de Planeamiento, Investigación y Congrol de la DNV
Autopista Ribereña, sus alternativas.
- Ing. Máximo Fioravanti, Consultor

Transporte Aéreo - Moderador: Ing. Guillermo Grimaux
- Ing. José Martinez Cal, ANAC
Nuevas tecnologías.
- Ing Gustavo Fernández Favarón, AA2000


Transporte Marítimo y Fluvial - Moderador: Ing. Demetrio Serman
Nuevo Puerto en Berisso.
- Ing. Rodolfo Rocca, Gerente de Concesiones y Proyectos del Puerto La Plata
Desarrollo portuario.
- Cap. Sergio Fabián Borrelli, Interventor - Dirección General de Puertos SE

Transporte Ferroviario - Moderador: Ing. Luis Girardotti
Obras estructurales para el Transporte Metropolitano.
- Ing. Juan Pablo Martinez, AC&A
Los desafíos de los Ferrocarriles de cargas en América Latina.
- Ing. Jorge Kohon, Consultor

Cierre -  Jornada Transporte
Palabras de Cierre - Autoridades Institucionales 
Autoridad Invitada
- Dr. Alejandro Ramos, Secretario de Transporte de la Nación

                       Descargar PDF                                         b-inscrip

31 August, 2014

Muerde Mi Brillante Trasero Matematico

Publicado en Sun 31 Aug, 2014 17:40 por aleperno en Aleperno

Si entendiste el título no hace falta que te explique la imagen, de lo contrario la imagen pertenece a la serie Futurama, más precisamente al capítulo en el cual Bender (el robot) posee la capacidad de replicarse. El problema es que se está replicando sin control y el brillante profesor Farnsworth logró encontrar una serie que define la capacidad de replicación de Bender. Cuando muestra la serie todos se espantan (excepto Fry que no parece entender nada) al ver que la expresion no es convergente.


Convergente, implica que luego de una cantidad muy grande de tiempo (mejor dicho términos) la serie va a tender a cierto valor. Que sea no convergente implica lo opuesto, que no existe ningún valor al cuál la serie se acerque. Esta serie es una de ellas, veamos porque.

\sum_{n=0}^{\infty} 2^n * \frac{M_0}{2^n (n+1)} = \sum_{n=0}^{\infty} \frac{M_0}{(n+1)}

Veamos que si n es muy grande el +1 se vuelve insignificante. Para no dividir por cero redefino.

\sum_{n=0}^{\infty} \frac{M_0}{(n+1)} = \frac{M_0}{0+1}+\sum_{n=1}^{\infty} \frac{M_0}{(n)}= \frac{M_0}{0+1}+M_0\sum_{n=1}^{\infty} \frac{1}{n}

Acá estaríamos tentados a pensar que si converge, ya que cuando n es suficiente grande el resultado de la división va a tender a 0, para analizar mejor esto escribamos alguno de los términos.

\sum_{n=1}^{\infty} \frac{1}{n}=\frac{1}{1}+\frac{1}{2}+\frac{1}{3}+\frac{1}{4}+\frac{1}{5}+\frac{1}{6}+\frac{1}{7}+\frac{1}{8}+\frac{1}{9}+\frac{1}{10}+\frac{1}{11}+\frac{1}{12}+...

Agrupando algunos términos

\sum_{n=1}^{\infty} \frac{1}{n}=(\frac{1}{1})+(\frac{1}{2})+(\frac{1}{3}+\frac{1}{4})+(\frac{1}{5}+\frac{1}{6}+\frac{1}{7})+(\frac{1}{8}+\frac{1}{9}+\frac{1}{10}+\frac{1}{11}+\frac{1}{12})+...

Cada uno de los términos agrupados entre paréntesis es igual o mayor a 1/2 por lo que si ademas agrupamos de a dos de estos terminos vamos a tener una suma mayor a 1+1+1+... lo cual es evidente que no hay ninguna convergencia.

El barman

Veamos otro ejemplo: entra un grupo grande de personas, el primero pide una cerveza, el segundo pide “la mitad de lo que pidio el primero”, el tercero dice “la mitad de lo que pidió el segundo” y así sucesivamente. El barman sirve dos cervezas. Veamos que forma tiene esto:

\sum_{n=0}^{\infty} \frac{1}{2^n}

Esto es una serie geometrica con termino general menor a uno, por lo cual converge y en particular converge a 2.

\sum_{n=0}^{\infty} \frac{1}{2^n} = \frac{1-\frac{1}{2}^n}{1- \frac{1}{2}} que con n \rightarrow \infty es claro que tiende a 2.

De esto podemos no solo afirmar que Matt Groening tiene un humor bastante nerd, sino también que las series son interesantes de estudiar, no solo por su capacidad de representar de manera ‘sencilla’ ciertos escenarios sino también que a veces su resultado no es tan intuitivo como uno tendería a pensar.

15 August, 2014

Explicit is better than implicit

Publicado en Fri 15 Aug, 2014 15:55 por facundoolano en "Facundo Olano"

A context manager is not the answer.

Think twice before mock.patch.

And, God, whatever that is, don’t put it in

07 August, 2014

9 y 10/09 1ra. Jornada del Departamento de Estabilidad

Publicado en Thu 07 Aug, 2014 17:57 por Cristian Moleres ( en Comisión de Estudiantes de Ingeniería Civil
La Facultad de Ingeniería de la UBA informa que el 9 y 10 de septiembre, de 18.00 a 22.00, en las instalaciones de la sede de Av. Las Heras 2214, CABA, se llevará a cabo la 1ra edición de las Jornadas del Departamento de Estabilidad.
La muestra de trabajos producidos en el departamento entre 2012 y 2014, el favorecimiento del contacto de los docentes entre sí y el acercamiento de nuevos alumnos a los contenidos de las materias optativas y a la oferta de tesis que propone el Departamento de Estabilidad de la FIUBA, son los objetivos principales de este evento, cuyos contenidos incluyen áreas como la mecánica del suelo, la mecánica del sólido, los métodos numéricos y los materiales.
El martes 9 de septiembre se exhibirán los trabajos vinculados con “Mecánica del Continuo” y “Métodos Numéricos”, con la coordinación del Dr. Ing. Raúl Bertero y la Dra. Inga. Rita Toscano, respectivamente; mientras que el miércoles 10 los ejes temáticos a abordar serán “Mecánica del Suelo”, presentado por el Dr. Ing. Alejo Sfriso, y “Materiales”,  a cargo del Dr. Ing. Luis Fernández Luco.
Los interesados deberán inscribirse a través de este formulario
Departamento de Estabilidad
Av. Las Heras 2214, 3er. piso, CABA.
Tel.: (54-11) 4514-3008

Powered by Planet Venus, CSS template written by Sebastián Santisi, based on FISubsilverSH. Design © 2007-2008 Sebastián Santisi
[ Última actualización: Sat 22 Oct, 2016 06:50 UTC ]