[Top] [Contents] [Index] [ ? ]

La Venganza de los Nerds

por Paul Graham
http://www.paulgraham.com/icad.html
Mayo 2002

1. Sobre la traducción  
2. Introducción  
3. ¿Son todos los lenguajes equivalentes?  
4. Alcanzando el nivel de las matemáticas  
5. Lo que hizo diferente a Lisp  
6. Donde los lenguajes importan  
7. Fuerzas centrípetas  
8. El precio de ser del promedio  
9. Una receta  
10. Apéndice: Potencia  
11. Notas  
12. Material relacionado  


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

1. Sobre la traducción

Este documento fue traducido a partir del original en Inglés, tomado de http://www.paulgraham.com/icad.html

Los comentarios y correcciones sobre esta traducción son bienvenidos. -- Leonardo Boshell.

Updated: Thu 17 Jul 2003, 11:29.

Copyright del texto original (C) 2002 Paul Graham.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

2. Introducción

Mayo de 2002

(Esta es una versión extendida de la ponencia presentada originalmente en la conferencia del Grupo Internacional de Usuarios de ICAD en Mayo de 2002. En ella se explica cómo un lenguaje desarrollado en 1958 ha llegado a ser uno de los más poderosos aun en la actualidad, el significado de poder y cuándo lo necesita, y por qué los jefes de cabello puntiagudo (idealmente, los jefes de sus competidores) ignoran deliberadamente este asunto.)

Nota: Cuando menciono el término "Lisp" en esta charla, me refiero a la familia de lenguajes Lisp, incluyendo Common Lisp, Scheme, Emacs Lisp, EuLisp, Goo, Arc, etc.

Permítanme comenzar por admitir que no sé mucho sobre ICAD. Sé que está escrito en Lisp, y que de hecho incluye Lisp, en el sentido en que permite a los usuarios crear y ejecutar programas en Lisp.

Es bastante común que programas escritos en Lisp incluyan Lisp. Emacs lo hace, y lo mismo Yahoo Store. Pero si lo piensa un poco, eso es un poco extraño. ¿Cuántos programas escritos en C incluyen C, en el sentido en que el usuario llega a ejecutar el compilador de C mientras usa la aplicación? A mí no se me ocurre ninguno, a menos que contemos a Unix como una aplicación. Hemos avanzado un minuto en esta charla y Lisp ya tiene una apariencia poco usual.

Ahora, probablemente a ninguno de ustedes les sorprende que Lisp parezca inusual.

Créanlo o no, hay un motivo por el que el código en Lisp luce tan extraño. Lisp no luce así por que haya sido diseñado por un montón de académicos de cabezas puntiagudas. Fue diseñado por académicos de cabezas puntiagudas, pero ellos tuvieron poderosas razones de ingeniería para hacer que la sintaxis luciera de esa forma.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

3. ¿Son todos los lenguajes equivalentes?

En el mundo de negocios del software hay una batalla constante entre los académicos de cabezas puntiagudas, y otra fuerza igualmente formidable, ejercida por los jefes de cabello puntiagudo. Todos sabemos quién es este jefe de cabello picudo, ¿no es así? Creo que la mayoría de personas que hacen parte del mundo de la tecnología no solo reconocen a este personaje de las caricaturas, sino que conocen a aquella persona en sus compañías sobre el que éste está modelado.

El jefe de cabello picudo combina milagrosamente dos cualidades que son comunes por sí solas, pero rara vez son vistas juntas: (a) él no sabe nada en absoluto sobre tecnología, y (b) él siempre tiene opiniones imponentes al respecto.

Suponga, por ejemplo, que usted necesita escribir una pieza de software. El jefe de cabello picudo no tiene idea de cómo debe funcionar tal software, y no puede diferenciar un lenguaje de programación de otro, y aun así sabe en qué lenguaje debería ser escrito. Correcto: él piensa que usted debería escribirlo en Java.

¿Por qué piensa esto? Echemos un vistazo al interior del cerebro del jefe en cuestión. Lo que él está pensando es algo como esto. Java es un estándar. Debe serlo, ya que leo sobre él todo el tiempo en la prensa. Puesto que es un estándar, no me meteré en líos por usarlo. Y eso también quiere decir que siempre habrán muchos programadores de Java, de modo que si los programadores que trabajan para mí ahora renuncian, como misteriosamente ocurre siempre con los programadores que trabajan para mí, puedo reemplazarlos fácilmente.

Bueno, eso suena razonable. Pero todo eso está basado en una premisa implícita, y esta premisa resulta ser falsa. El jefe de cabello picudo piensa que, a la larga, todos los lenguajes de programación son iguales. Si esto fuera cierto, él estaría en lo correcto. Si los lenguajes de programación fueras todos iguales, seguro, usemos cualquiera que sea el lenguaje que el resto del mundo está usando.

Pero no todos los lenguajes son equivalentes, y creo que puedo probarlo sin llegar siquiera a mencionar las diferencias entre ellos. Si se le hubiera preguntado al jefe de cabello picudo en 1992 cuál lenguaje debía utilizarse para escribir software, él hubiera respondido con tanta seguridad como lo hace el día de hoy. El software debería ser escrito en C++. Pero, si todos los lenguajes son lo mismo, ¿por qué habría de cambiar la opinión del jefe de cabello picudo después de todo? De hecho, ¿por qué se molestaron los desarrolladores de Java en crear un nuevo lenguaje?

Se puede pensar que si usted crea un nuevo lenguaje, es porque cree que es mejor en alguna forma que lo que la gente ya tiene. Y de hecho, Gosling aclara en el primer documento técnico escrito sobre Java que este lenguaje fue creado para corregir algunos problemas que tenía C++. Así que ahí lo tienen: los lenguajes no son todos iguales. Si decide seguir el razonamiento que corre a través del cerebro del jefe de cabello picudo desde Java, y luego de vuelta a la historia de Java y sus orígenes, terminará sosteniendo una idea que contradice el axioma desde el que partió.

De modo que, ¿quién tiene la razón? ¿James Gosling, o el jefe del cabello chistoso? No debe ser sorpresa: Gosling está en lo correcto. Algunos lenguajes son mejores, para ciertos problemas, que otros. Y ahora que lo pensamos, eso introduce algunas preguntas interesantes. Java fue diseñado para ser mejor, en ciertos problemas, que C++. ¿Qué problemas? ¿Cuándo es Java la mejor opción y cuándo lo es C++? ¿Hay situaciones en las que otro lenguaje distinto es mejor que cualquiera de ellos?

Una vez haya comenzado a estudiar seriamente estas preguntas, notará que está lidiando con un problema complicado. Si el jefe de cabello picudo tuviera que pensar sobre el problema en toda su magnitud, su cerebro podría explotar. Mientras siga creyendo que todos los lenguajes son equivalentes, todo lo que tiene que hacer es elegir aquél que parezca gozar de mayor popularidad, y ya que tal cosa es más un asunto de moda que de tecnología, es posible que incluso él tenga la respuesta correcta. Pero si los lenguajes difieren entre sí, él tiene entonces que resolver dos ecuaciones simultáneas, buscando encontrar un balance óptimo entre dos cosas sobre las que no conoce nada: la conveniencia relativa de los veinte o más lenguajes disponibles para el problema que necesita resolver, y las probabilidades que tiene de encontrar programadores, librerías, etc. para cada uno de ellos. Si eso es a lo que se expone, no es extraño que el jefe de cabello picudo no quiera considerar el problema de tal forma.

La desventaja de pensar que todos los lenguajes de programación son equivalentes es que eso no es cierto. Pero la ventaja es que hace la vida más simple. Y pienso que esa es la razón principal por la que esta idea es tan popular. Es una idea cómoda.

Sabemos que Java debe ser bastante bueno, ya que es el lenguaje de programación nuevo, en boga. ¿O realmente lo es? Si se fija en el mundo de los lenguajes de programación a distancia, pareciera que Java es lo último que se ha visto en nuestra tierra. (Si se aprecia desde suficiente distancia, todo lo que se puede ver es el largo y brillante aviso pagado por Sun.) Pero si aprecia este mundo de cerca, encontrará que hay niveles de popularidad. En medio de la subcultura de los hackers, existe otro lenguaje llamado Perl que es considerado mucho mejor que Java. Slashdot, por ejemplo, es generado por una aplicación creada en Perl. No creo que vaya a ver algún día a estos tipos usando Java Server Pages. Pero hay otro lenguaje aun más reciente, llamado Python, cuyos seguidores tienden a demeritar Perl, y más en espera.

Si aprecia estos lenguajes en orden, Java, Perl, Python, notará un patrón interesante. Al menos lo hará si usted es un hacker de Lisp. Cada uno de estos lenguajes es progresivamente más parecido a Lisp. Python copia incluso características que muchos hackers de Lisp consideran ser fallas. Podría incluso transcribir algunos programas simples en Lisp a Python línea por línea. Es 2002, y los lenguajes de programación están apenas alcanzando 1958.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4. Alcanzando el nivel de las matemáticas

A lo que me refiero es a que Lisp fue descubierto inicialmente por John McCarthy en 1958, y los lenguajes de programación están hasta ahora poniéndose al corriente de las ideas que él desarrolló entonces.

Ahora bien, ¿cómo puede ser posible? ¿Acaso la tecnología no es algo que cambia muy rápidamente? Quiero decir, en 1958, las computadoras eran bestias del tamaño de una nevera y con la capacidad de procesamiento de un reloj de pulso. ¿Cómo podría cualquier tecnología de esa edad ser relevante en la actualidad, mucho menos superior a los últimos desarrollos en el área?

Les diré cómo. Es porque Lisp no fue diseñado en realidad como un lenguaje de programación, al menos no en el sentido que entendemos ahora. Lo que entendemos por lenguaje de programación es algo que usamos para decirle a una computadora qué hacer. McCarthy eventualmente intentó desarrollar un lenguaje de programación en este sentido, pero el Lisp con el que resultamos estaba basado en algo diferente que él hizo puramente como ejercicio teórico --- un esfuerzo para definir una alternativa más conveniente a la Máquina de Turing. Como diría McCarthy luego,

Otra manera de demostrar que Lisp era más elegante que las máquinas de Turing era escribir una función universal en Lisp y demostrar que es más corta y más comprehensible que la descripción de una máquina universal de Turing. Esta era la función de Lisp eval..., la cual computa el valor de una expresión en Lisp... Escribir eval requería inventar una notación que permitiera representar funciones de Lisp como datos, y tal notación fue diseñada para los propósitos del documento técnico sin haber pensado que podría ser utilizada para expresar programas de Lisp en la práctica.

Lo que ocurrió a continuación fue que, a finales de 1958, Steve Rusell, uno de los estudiantes de McCarthy, vió esta definición de eval y se dió cuenta de que si la traducía a lenguaje de máquina, el resultado sería un intérprete de Lisp.

Esto fue una gran sorpresa en su momento. Esto fue lo que dijo McCarthy luego en una entrevista:

Steve Rusell me dijo, mire, porqué no programo este eval..., a lo que yo le respondí, jo, jo, estás confundiendo la teoría con la práctica, este eval fue pensado para propósitos de estudio, no de computación. Pero el insistió y la programó. Es decir, compiló la función eval de mi documento en código para la [IBM] 704, corrigió algunos errores, y luego la promocionó como un intérprete de Lisp, lo que ciertamente era. Así que en tal punto Lisp tenía esencialmente la forma que tiene hoy en día...

De repente, en cuestión de semanas, creo yo, McCarthy encontró que su ejercicio teórico se había transformado en un lenguaje de programación real-- y uno más poderoso de lo que había imaginado.

De modo que la explicación corta del porqué este lenguaje de los '50s no es obsoleto en la actualidad es que este lenguaje no era una tecnología sino un desarrollo matemático, y las matemáticas no se desgastan. Una comparación adecuada no podría hacerse con el hardware de los '50s, sino, por ejemplo, con el algoritmo Quicksort, el cual fue descubierto en 1960 y aun es el método general de ordenamiento más rápido que existe.

Hay otro lenguaje más que ha sobrevivido de los '50s, Fortran, y éste representa la cara opuesta del diseño de lenguajes de programación. Lisp era un fragmento de teoría que inesperadamente se convirtió en un lenguaje de programación. Fortran fue desarrollado intencionalmente como un lenguaje de programación, claro que como uno que ahora consideramos de muy bajo nivel.

Fortran I, el lenguaje que fue desarrollado en 1956, era algo muy diferente al Fortran actual. Fortran I era básicamente un lenguaje ensamblador con matemáticas. En ciertas formas era menos poderoso que lenguajes de ensamblador más recientes; no existían subrutinas, por ejemplo, únicamente saltos a diferentes direcciones. El Fortran tal y como lo conocemos ahora es considerablemente más cercano a Lisp que Fortran I.

Lisp y Fortran eran las ramas de dos árboles en evolución distintos, uno basado en las matemáticas y otro fundamentado en la arquitectura de máquina. Estos dos árboles han estado convergiendo desde entonces. Lisp comenzó enfocándose en ser poderoso, y en los veinte años siguientes se hizo eficiente. Los llamados lenguajes de primera línea comenzaron siendo rápidos, y en los siguientes cuarenta años se hicieron gradualmente más poderosos, hasta ahora en donde los más avanzados de ellos son considerablemente cercanos a Lisp. Están cerca, pero aun carecen de ciertas cosas....


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

5. Lo que hizo diferente a Lisp

Cuando fue desarrollado por primera vez, Lisp albergaba nueve ideas nuevas. Algunas de ellas ahora las consideramos naturales, otras sólo pueden apreciarse en lenguajes más avanzados, y dos de ellas siguen siendo exclusivas de Lisp. Estas nueve ideas son, en orden de su adopción por el mercado,

  1. Condicionales. Una condicional es una construcción del tipo si-entonces-sino. Ahora las tomamos como cosas triviales, pero Fortran I no las tenía. Únicamente tenía un goto condicional basado fuertemente en la instrucción de máquina correspondiente.

  2. Un tipo de dato "función". En Lisp, las funciones son un tipo de dato como lo son los enteros o las cadenas. Tienen una representación literal, pueden almacenarse en variables, pueden ser pasadas como argumentos, etc.

  3. Recursión. Lisp fue el primer lenguaje de programación en soportarla.

  4. Tipos dinámicos. En Lisp, todas las variables son en realidad apuntadores. Los valores son los que tienen tipos, no las variables, y el trabajo de asignar o asociar variables quiere decir copiar sus apuntadores, no el contenido al que apuntan.

  5. Recolección de basura.

  6. Programas compuestos de expresiones. Los programas en Lisp son árboles de expresiones, cada uno de las cuales retorna un valor. Esto en contraste a lo que sucede en Fortran y la mayoría de lenguajes posteriores, los cuales distinguen entre expresiones y declaraciones.

    Era normal tener esta distinción en Fortran I ya que las declaraciones no podían ser anidadas. De modo que aunque se requirieran expresiones para que funcionaran las operaciones matemáticas, no había sentido en permitir que cualquier otra cosa retornara un valor, porque no podía haber nada esperando tal valor de respuesta.

    Esta limitación desapareció con el advenimiento de los lenguajes estructurados en bloques, pero para ese entonces ya era muy tarde. Esta diferenciación entre expresiones y declaraciones ya estaba muy arraigada. Se extendió desde Fortran hacia Algol, y luego hacia los descendientes de ambos.

  7. Un tipo para los símbolos. Los símbolos son en realidad apuntadores a cadenas almacenadas en una tabla asociativa. De modo que usted puede probar la equivalencia comparando un apuntador, en lugar de comparar cada letra.

  8. Una notación para el código que utiliza árboles de símbolos y constantes.

  9. El lenguaje completo está disponible todo el tiempo. En realidad no hay diferencia entre tiempo de lectura, de compilación y de ejecución. Es posible compilar o ejecutar código durante la fase de lectura, leer o ejecutar código mientras se compila, así como leer o compilar código en tiempo de ejecución.

    Ejecutar código en tiempo de lectura le permite a los usuarios reprogramar la sintaxis de Lisp; ejecutar código en tiempo de compilación es la base de las macros; compilar en tiempo de ejecución es a su vez la base del uso de Lisp como un lenguaje de extensión en programas como Emacs; y la lectura en tiempo de ejecución le permite a los programas comunicarse usando expresiones s, una idea reinventada recientemente con el nombre de XML.

Cuando Lisp apareció por primera vez, estas ideas estaban lejos de ser adoptadas en las prácticas de programación ordinarias, las cuales eran influenciadas principalmente por el hardware disponible a finales de los 50's. Con el tiempo, el lenguaje por defecto, compuesto de una sucesión de lenguajes populares, ha evolucionado gradualmente hacia Lisp. Las ideas 1 a 5 son ahora comunes. La número 6 está comenzando a aparecer en escena. Python posee cierta forma de la idea 7, aunque aparentemente no existe sintaxis alguna para ella.

Respecto a la número 8, esta podría considerarse la más interesante de todas. Las ideas 8 y 9 se hicieron parte de Lisp por accidente, ya que Steve Rusell implementó algo que McCarthy nunca pensó en implementar. Y aun así estas ideas resultan ser las responsables tanto de la extraña apariencia de Lisp como de sus características más distintivas. Lisp luce de forma extraña no tanto porque tenga una sintaxis extraña, como que de hecho no tiene sintaxis; usted expresa los programas directamente en los árboles de procesamiento que son construidos tras bambalinas cuando otros lenguajes diferentes son analizados sintácticamente, y estos árboles están compuestos de listas, que son estructuras de datos en Lisp.

El hecho de expresar el lenguaje en sus propias estructuras de datos resulta ser una característica bastante poderosa. Las ideas 8 y 9 juntas significan que es posible escribir programas que escriban programas. Eso puede sonar como una idea exótica, pero es algo utilizado diariamente en el mundo de Lisp. La forma más común de hacerlo es mediante el uso de algo llamado macro.

El término "macro" no tiene el mismo significado en Lisp que en otros lenguajes. Una macro en Lisp puede ser cualquier cosa desde una abreviación hasta el compilador de un nuevo lenguaje. Si realmente desea entender Lisp, o simplemente expandir sus horizontes de programación, le recomendaría aprender más sobre las macros.

Las macros (en el sentido que tienen en Lisp) son aun, hasta donde tengo entendido, únicas a Lisp. Esto se debe en parte a que para poder disponer de macros, usted probablemente tendrá que hacer que su lenguaje luzca tan extraño como Lisp. También puede ser porque si añade ese incremento de poder, ya no puede anunciar que ha inventado un nuevo lenguaje, más bien únicamente un nuevo dialecto de Lisp.

Digo esto más que todo como una broma, pero es bastante cierto. Si define un nuevo lenguaje que tenga car, cdr, cons, quote, cond, atom, eq, y una notación para expresar las funciones como listas, entonces podrá construir el resto de Lisp a partir de allí. De hecho, esa es la calidad que define a Lisp: fue con la intención de permitir esto que McCarthy le dio a Lisp la forma que tiene.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

6. Donde los lenguajes importan

Así que suponga que Lisp ciertamente representa un tipo de límite al que los lenguajes populares se acercan asintóticamente--¿quiere decir eso que debería usarlo en la práctica para escribir software? ¿Cuánto se pierde por usar un lenguaje menos potente? ¿No es más sabio, en ocasiones, no vivir en el borde de la innovación? ¿Y no es acaso la popularidad en cierta medida su propia justificación? ¿No tiene razón acaso el jefe de pelo picudo, por ejemplo, al querer usar un lenguaje para el que sea fácil contratar programadores?

En realidad hay, por supuesto, proyectos en donde la selección de un lenguaje de programación no importa mucho. Como regla, entre más requiera la aplicación, más ventaja se recibe al usar un lenguaje potente. Pero muchísimos proyectos no son muy exigentes después de todo. La mayoría de la programación probablemente consiste en escribir pequeños programas que aten cosas, y para estos pequeños programas "de pegamento" usted puede usar cualquier lenguaje con el que ya se encuentre famaliarizado y que tenga buenas librerías para realizar aquello que necesita hacer. Si tan solo necesita alimentar datos desde una aplicación de windows a otra, seguro, use visual basic.

Puede escribir pequeños programas-pegamento en Lisp también (yo lo uso como calculadora de escritorio), pero la mayor ganancia proveniente de lenguajes como Lisp se encuentra al otro lado del espectro, en donde usted necesita escribir programas sofisticados para resolver problemas difíciles haciendo frente a la ruda competencia. Un buen ejemplo es el programa de búsqueda de pasajes de aerolínea que ITA Software le licencia a Orbitz. Estos tipos entraron a un mercado ya dominado por dos grandes, y atrincherados competidores, Travelocity y Expedia, y parecen haberlos humillado tecnológicamente.

El núcleo de la aplicación de ITA es un programa de 200,000 líneas de Common Lisp que realiza búsquedas sobre posibilidades muchos órdenes de magnitud más amplias que sus competidores, quienes aparentemente se encuentran usando aun técnicas de programación de la era de los mainframes. (Auque ITA también se encuentra usando un lenguaje de programación de la era de los mainframes, en cierto sentido.) Nunca he visto código alguno de ITA, pero de acuerdo a uno de sus hackers de punta, ellos usan bastantes macros, y no me sorprende saberlo.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

7. Fuerzas centrípetas

No estoy diciendo que no existe un precio por usar tecnologías poco comunes. El jefe de pelo chistoso no está completamente equivocado al preocuparse por esto. Pero debido a que él no entiende los riesgos, tiende a maximizarlos.

Se me ocurren tres problemas que pueden emerger al usar lenguajes menos populares. Sus programas quizás no funcionen bien con programas escritos en otros lenguajes. Puede que tenga menos librerías a su disposición. Y puede tener problemas contratando programadores.

¿Qué tan relevante es cada uno de estos problemas? La importancia del primero de ellos varía dependiendiendo de si usted tiene el control sobre todo el sistema. Si está escribiendo software que debe correr sobre la máquina de un usuario remoto, que opera sobre un sistema operativo cerrado y con tendencia a fallar (no menciono nombres), pueden haber ventajas en el hecho de escribir su aplicación en el mismo lenguaje que el sistema operativo. Pero si usted controla todo el sistema y dispone del código fuente de toda las partes, como es posiblemente el caso de ITA, usted puede usar cualquier lenguaje que desee. Si cualquier incompatibilidad aparece, usted puede corregirla por sí mismo.

En aplicaciones de servidor puede salirse con la suya usando las tecnologías más avanzadas, y pienso que esa es la principal causa de lo que Jonathan Erickson llama el "renacimiento de los lenguajes de programación." Esta es la razón por la que incluso oímos hablar de lenguajes nuevos como Perl y Python. No escuchamos de estos lenguajes porque la gente los esté usando para escribir aplicaciones windows, sino porque la gente los usa en los servidores. Y en la medida en que el software se translade fuera del escritorio hacia los servidores (un futuro al que incluso microsoft parece haberse resignado), habrá cada vez menos y menos presión para usar tecnologías intermedias.

Respecto a las librerías, su importancia también depende del tipo de aplicación. Para problemas menos exigentes, la disponibilidad de librerías puede ser más importante que el poder intrínseco del lenguaje. ¿En dónde se encuentra el punto de equilibrio? Es difícil decirlo con exactitud, pero dondequiera que se encuentre, se queda corto en aquello que usted reconocería como una aplicación. Si una compañía se considera parte de la industria del software, y se encuentra desarrollando una aplicación que será uno de sus productos, entonces probablemente este desarrollo involucre a varios hackers y la aplicación durará por lo menos seis meses siendo escrita. En un proyecto de ese tamaño, los lenguajes potentes probablemente empiezan a adquirir más importancia que la conveniencia de las librerías pre-existentes.

La tercera preocupación del jefe de pelo picudo, la dificultad para contratar programadores, pienso que es una cortina de humo. ¿Cuántos hackers necesita contratar, después de todo? Ciertamente en este momento ya sabemos que el software se desarrolla más convenientemente en equipos de menos de diez personas. Y no deberían haber problemas para contratar hackers en esa escala para cualquier lenguaje del que cualquier persona haya escuchado jamás. Si no puede encontrar diez programadores de Lisp, entonces su compañía probablemente esté ubicada en la ciudad equivocada para desarrollar software.

De hecho, elegir un lenguaje más poderoso probablemente disminuya el tamaño del equipo que necesita, ya que (a) si usa un lenguaje más poderoso probablemente no requerirá de tantos hackers, y (b) aquellos hackers que trabajan en lenguajes más avanzados tienden a ser más intelegentes.

No estoy diciendo que no recibirá una cantidad de presión para usar aquellas tecnologías que se perciben como "estándar". En Viaweb (ahora Yahoo Store), causamos que se levantaran varias cejas entre empresarios y compradores potenciales al haber usado Lisp. Pero de igual forma levantamos cejas también al usar equipos Intel genéricos como servidores en lugar de servidores "de potencia industrial" como Suns, por usar una variante de Unix open-source en ese entonces oscura llamada FreeBSD en lugar de usar un sistema operativo comercial como windows nt, por ignorar un supuesto estándar comercial llamado SET del que nadie se acuerda ya siquiera, y demás.

No puede permitir que los trajes tomen las decisiones técnicas por usted. ¿Alarmó a algunos compradores potenciales el hecho de que usamos Lisp? Algo, ligeramente, pero si no hubiéramos usado Lisp, no hubiésemos sido capaces de escribir el software que produjo que quisieran comprarnos. Lo que les parecía una anomalía era en realidad causa y efecto.

Si usted decide emprender su propia iniciativa, no diseñe su producto para complacer a empresarios o compradores potenciales. Diseñe su producto para complacer a los usuarios. Si usted se gana a los usuarios, todo lo demás fluirá normalmente. Y si no lo hace, a nadie le importará qué tan cómodamente ortodoxas fueron sus decisiones tecnológicas.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

8. El precio de ser del promedio

¿Cuánto se pierde por usar un lenguaje menos potente? De hecho, hay cierta información al respecto allí fuera.

La medida más conveniente de potencia es probablemente el tamaño del código. La idea de los lenguajes de alto nivel es ofrecerle mayores abstracciones--ladrillos más grandes, poniéndolo de otra forma, para que no necesite tantos a la hora de construir una pared de cierto tamaño. De modo que entre más potencia tenga el lenguaje, los programas serán más cortos (no simplemente en caracteres, por supuesto, sino también en otros elementos diferentes).

¿Cómo le permite un lenguaje más poderoso escribir programas más cortos? Una técnica que puede usar, si el lenguaje se lo permite, es algo llamado programación de abajo hacia arriba. En lugar de simplemente escribir su aplicación en el lenguaje base, usted construye sobre ese lenguaje base un lenguaje para escribir programas como el suyo, y luego escribe su programa en ese nuevo lenguaje. El código combinado de este proceso puede ser mucho menor que si lo hubiera escrito completamente en el lenguaje base--de hecho, esta es la forma en que funcionan muchos algoritmos de compresión. Un programa de abajo hacia arriba debería ser más fácil de modificar también, ya que en muchos casos la capa del lenguaje no necesitará cambiar en absoluto.

El tamaño del código es importante, ya que el tiempo que toma escribir una aplicación depende en gran parte del tamaño del mismo. Si su programa sería tres veces más largo en otro lenguaje, escribirlo tomará tres veces el tiempo--y no es posible evitar este inconveniente contratando más gente, ya que mas allá de cierto tamaño, las nuevas contrataciones constituyen en la realidad una pérdida neta. Fred Brooks describió este fenómeno en su famoso libro The Mythical Man-Month, y todo lo que he visto tiende a confirmar lo que él ha dicho.

Así que ¿en qué medida resultan más cortos sus programas si los escribe en Lisp? La mayoría de números que he escuchado sobre Lisp vs. C, por ejemplo, han estado entre el rango 7-10x. Pero un artículo reciente sobre ITA en la revista New Architect decía que "una línea de Lisp puede reemplazar 20 líneas de C", y debido a que este artículo estaba lleno de citas del presidente de ITA, asumo que obtuvieron estos números de ITA. Si es así entonces podemos tener cierta fé en esta apreciación; el software de ITA incluye una gran cantidad de C y C++ así como de Lisp, así que ellos hablan por experiencia.

Mi impresión es que estos múltiplos no son constantes ni siquiera. Pienso que se incrementan cuando se enfrentan problemas más difíciles y también cuando se cuenta con programadores más inteligentes. Un buen hacker de verdad puede obtener más provecho de mejores herramientas.

Hablando en términos de datos en la curva, en cualquier etapa del proceso, si usted fuera a competir contra ITA y eligiera escribir su software en C, ellos serían capaces de desarrollar software veinte veces más rápido que usted. Si usted invierte un año implementando una nueva característica, ellos serían capaces de duplicarla en menos de tres semanas. Mientras que si ellos dedican tan solo tres meses desarrollando algo nuevo, pasarían cinco años antes de que usted lo tuviera también.

¿Y sabe algo? Eso es contando el mejor escenario posible. Cuando se habla de correlaciones entre tamaño y código, se asume implícitamente que se es posible escribir el software en el lenguaje más débil. Pero de hecho existen límites en aquello que los programadores pueden lograr. Si intenta resolver un problema difícil con un lenguaje que es de un nivel muy bajo, usted llega a un punto en el que sencillamente hay demasiado que debe tener en su cabeza en un momento dado.

De modo que cuando digo que le tomaría cinco años al competidor imaginario de ITA duplicar algo que ITA podría escribir en Lisp en tres meses, quiero decir cinco años si nada malo ocurre. De hecho, considerando el modo en que trabajan muchas compañías, cualquier proyecto de desarrollo que pueda tomar cinco años sufre la tendencia a no ser completado nunca.

Admito que esto es un caso extremo. Los hackers de ITA parecen ser inusualmente inteligentes, y C es un lenguaje de muy bajo nivel. Pero en un mercado competitivo, incluso una diferencia de dos o tres a uno sería suficiente para garantizar que usted siempre estaría detrás de sus competidores.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

9. Una receta

Este es el tipo de posibilidades sobre las que el jefe de pelo picudo nisiquiera quiere pensar. De modo que la mayoría no lo hace. Por que, sabe, cuando se observa con detenimiento, al jefe de pelo picudo no le importa que su compañía sea arrollada por la competencia, siempre y cuando nadie puede probar que ha sido su culpa. El plan más seguro para él personalmente es mantenerse cerca al centro del rebaño.

Al interior de las organizaciones grandes, la frase usada para describir esta estrategia es "la mejor práctica de la industria". Su propósito es escudar al jefe de pelo picudo de toda responsabilidad: si él elige algo que es "la mejor práctica de la industria", y la compañía pierde, él no puede ser culpado. El no fue quien eligió, fue la industria.

Pienso que este término fue usado originalmente para describir métodos de contabilidad y cosas por el estilo. Lo que quiere decir, en términos generales, es no haga nada raro. Y en contabilidad esa es probablemente una buena idea. Los términos "lo último en el mercado" y "contabilidad" no suenan bien juntos. Pero cuando usted introduce este criterio a las desiciones sobre tecnología, comienza a recibir las respuestas equivocadas.

La tecnología debería ser frecuentemente lo último en el mercado. En los lenguajes de programación, como lo ha dicho Erann Gat, lo que le dicta la "mejor práctica de la industria" no es en realidad la mejor opción, tan sólo una respuesta promedio. Cuando una decisión le causa desarrollar software a una fracción del ritmo de los competidores más agresivos, "mejor práctica" es un nombre errado.

Así que aquí tiene un par de datos que creo que resultan valiosos. De hecho, lo sé por experiencia. Número 1, los lenguajes varían en potencia. Número 2, la mayoría de administradores ignoran esto deliberadamente. Entre ellos, estos dos hechos son literalmente una receta para hacer dinero. ITA es un ejemplo de esta receta en acción. Si desea ganar en un negocio de software, tan solo tome el problema más difícil que pueda encontrar, use el lenguaje más poderoso que pueda conseguir, y espere a que los jefes de pelo picudo de sus competidores den el paso atrás hacia los recursos promedio.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

10. Apéndice: Potencia

Con el propósito de ilustrar aquello a lo que hago referencia cuando hablo de potencia de los lenguajes de programación, considere el siguiente problema. Queremos escribir una función que genere acumuladores--una función que toma un número n, y retorna una función que toma otro número i y retorna n incrementado en i.

(Note que digo incrementado en, no más. Un acumulador tiene que acumular.)

En Common Lisp esto sería

 
(defun foo (n)
  (lambda (i) (incf n i)))

y en Perl 5,

 
sub foo {
  my ($n) = @_;
  sub {$n += shift}
}

versión que tiene más elementos que la de Lisp debido a que en Perl hay que extraer los parámetros manualmente.

En Smalltalk el código el ligeramente más largo que en Lisp

 
foo: n
  |s|
  s := n.
  ^[:i| s := s+i. ]

ya que aunque en general las variables lexicales funcionan, no es posible realizar una asignación a un parámetro, de modo que hay que crear una nueva variable s.

En Javascript el ejemplo es, nuevamente, un poco más largo, ya que Javascript conserva la distinción entre sentencias y expresiones, así que hay que usar sentencias return explícitas para devolver valores:

 
function foo(n) {
  return function (i) {
           return n += i } }

(para ser justos, Perl también conserva esta distinción, pero lidia con ella en un modo típico del lenguaje al permitirle omitir los returns.)

Si intenta traducir el código de Lisp/Perl/Smalltalk/Javascript a Python se encotrará con algunas limitaciones. Debido a que Python no soporta por completo las variables lexicales, usted tiene que crear una estructura de datos para almacenar el valor de n. Y aunque Python tiene un tipo de datos "función", no existe una representación literal para referirse a una (a menos que el cuerpo sea tan solo una expresión simple) así que tiene que crear una función con nombre para devolverla. Esto es con lo que resulta:

 
def foo(n):
  s = [n]
  def bar(i):
    s[0] += i
    return s[0]
  return bar

Los usuarios de Python pueden preguntar con todo derecho, por qué no escribir simplemente

 
def foo(n):
  return lambda i: return n += i

o incluso

 
def foo(n):
  lambda i: n += i 

y mi opinión es que ellos probablemente podrán hacerlo, algún día. (Pero si no desean esperar a que Python termine su evolución hacia Lisp, ellos podrían simplemente...)

En los lenguajes orientados a objetos es posible, hasta cierto punto, simular una "clausura" (una función que hace referencia a variables definidas en contextos delimitados) por medio de la definición de una clase con un método y un campo para reemplazar cada variable de un contexto recubridor. Esto hace que el programador realice el tipo de análisis de código que sería hecho por el compilador en un lenguaje con soporte completo para contextos lexicales, y no funcionará si más de una función hace referencia a la misma variable, pero es suficiente para casos simples como este.

Los expertos en Python parecen coincidir en que esta es la forma más apropiada de resolver el problema en Python, escribiendo ya sea

 
def foo(n):
  class acc:
    def __init__(self, s):
        self.s = s
    def inc(self, i):
        self.s += i
        return self.s
  return acc(n).inc 

o

 
class foo:
  def __init__(self, n):
      self.n = n
  def __call__(self, i):
      self.n += i
      return self.n

Incluyo estas versiones ya que no quisiera que los entusiastas de Python digan que estaba malinterpretando el lenguaje, pero ambas me parecen más complejas que la versión original. Se está haciendo lo mismo, definiendo un lugar separado para almacenar el acumulador; simplemente es un campo de un objeto en lugar de la cabeza de una lista. Y el uso de estos nombres de campo reservados especiales, particularmente __call__, lucen un poco como un hack.

En medio de la rivalidad de Perl y Python, la premisa de los hackers de Python parece ser que Python es una alternativa más elegante a Perl, pero lo que este caso muestra es que la potencia es la elegancia suprema: el programa en Perl es más simple (tiene menos elementos), aun cuando la sintaxis sea un poco desagradable.

¿Qué hay de los otros lenguajes? En los otros lenguajes mencionados en esta charla--Fortran, C, C++, Java y visual basic--no es claro si en realidad puede resolverse este problema. Ken Anderson dice que el siguiente código es lo más cercano a una respuesta que puede obtenerse en Java:

 
public interface Inttoint {
  public int call(int i);
}

public static Inttoint foo(final int n) {
  return new Inttoint() {
    int s = n;
    public int call(int i) {
    s = s + i;
    return s;
    }};
}

Esta solución se queda corta ya que sólo funciona con enteros. Después de extensos intercambios con hackers de Java, diría que escribir una versión apropiadamente polimórfica que se comporte como los ejemplos previos se ubica entre lo magníficamente enredado y lo imposible. Si alguien desea escribir este ejercicio yo estaría muy interesado en verlo, pero personalmente ya me he rendido en el intento.

No es completamente cierto que no pueda resolverse este problema en otros lenguajes, por supuesto. El hecho de que todos estos lenguajes sean equivalentes al nivel Turing quiere decir que, estrictamente hablando, usted puede escribir cualquier programa en cualquiera de esos lenguajes. ¿Así que cómo lo haría? En un caso extremo, escribiendo un intérprete de Lisp en estos lenguajes menos potentes.

Eso suena como una broma, pero ocurre en diferentes niveles con tanta frecuencia en proyectos de programación grandes que hay un nombre para este fenómeno, la Décima Regla de Greenspun:

Cualquier programa en C o Fortran lo suficientemente complejo contiene una implementación pragmática, informalmente especificada, abrumada por bugs y lenta de la mitad de Common Lisp.

Si usted intenta resolver un problema difícil, la pregunta no es si usará un lenguaje lo suficientemente potente, sino si usted (a) usará un lenguaje potente, (b) escribirá un intérprete propio de alguno, o (c) usted mismo se convertirá en un compilador humano de uno. Vemos que esto ya empieza a ocurrir en el ejemplo de Python, donde en efecto estamos simulando el código que un compilador generaría para implementar una variable lexical.

Esta práctica no solo es común, sino institucionalizada. Por ejemplo, en el mundo de la orientación a objetos usted oye hablar mucho sobre los "patrones". Me pregunto si estos patrones no son en algunos casos la evidencia del caso (c), el compilador humano, en la práctica. Cuando veo patrones en mis programas, lo considero una señal de problemas. La forma de un programa debería reflejar únicamente el problema que intenta resolver. Cualquier otra irregularidad en el código es una señal, al menos para mí, de que estoy usando abstracciones que no son lo suficientemente potentes--con frecuencia quiere decir que estoy generando a mano las expansiones de alguna macro que necesito escribir.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

11. Notas


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

12. Material relacionado

Muchas personas han respondido a esta charla, así que he creado una página adicional para atender aquellas inquietudes que ha generado: Re: La venganza de los nerds.

También inició una extensa y con frecuencia útil discusión en la lista de correo de LL1. Observe particularmente el mensaje de Anton van Straaten sobre compresión semántica.

Parte del correo en LL1 me ha llevado a hacer el intento de profundizar en el asunto de la potencia de los lenguajes en la página La brevedad es Poder.

Un conjunto mayor de implementaciones de la prueba del generador de acumuladores se encuentra recopilado en su página propia.

Traducción al Japonés.


[Top] [Contents] [Index] [ ? ]


[Top] [Contents] [Index] [ ? ]

1. Sobre la traducción
2. Introducción
3. ¿Son todos los lenguajes equivalentes?
4. Alcanzando el nivel de las matemáticas
5. Lo que hizo diferente a Lisp
6. Donde los lenguajes importan
7. Fuerzas centrípetas
8. El precio de ser del promedio
9. Una receta
10. Apéndice: Potencia
11. Notas
12. Material relacionado

[Top] [Contents] [Index] [ ? ]

This document was generated by Leonardo Boshell on July, 17 2003 using texi2html

The buttons in the navigation panels have the following meaning:

Button Name Go to From 1.2.3 go to
[ < ] Back previous section in reading order 1.2.2
[ > ] Forward next section in reading order 1.2.4
[ << ] FastBack previous or up-and-previous section 1.1
[ Up ] Up up section 1.2
[ >> ] FastForward next or up-and-next section 1.3
[Top] Top cover (top) of document  
[Contents] Contents table of contents  
[Index] Index concept index  
[ ? ] About this page  

where the Example assumes that the current position is at Subsubsection One-Two-Three of a document of the following structure:

This document was generated by Leonardo Boshell on July, 17 2003 using texi2html