Thursday, January 25, 2007

Alternativa al C: O'Caml

Cuando se habla de alternativas al C, la mayoría de personas piensa en C++, Objective C o Java. Un candidato mas digno, aunque menos conocido es el lenguaje D. Todas estas opciones tienen algo en común: son lenguajes de computación que sigue el paradigma imperativo.

Los lenguajes imperativos se caracterizan por los siguientes elementos:
  • Son dependientes del estado de la computación: Los resultados de los computos dependen de un conjunto de valores almacenados durante la ejecución del programa y que son mutables
  • El flujo de control debe ser totalmente especificado
  • El orden de ejecución debe ser totalmente especificado
Estas características hacen que los lenguajes imperativos puedan ser traducidos mas directa y eficientemente a lenguaje maquina, pero al costo de forzar en el programador la especificación de la solución a un mayor nivel de detalle. Por eso, cuando se busca velocidad, la mayoría de los programadores están predispuestos a buscar lenguajes imperativos. O al menos así solía ser


Bienvenidos a O'Caml

Los lenguajes funcionales por su parte, ofrecen ciertas ventajas al programador:
  • No dependen del estado: Esto elimina un conjunto de errores y permite que el código sea mas auditable (de hecho el código puede ser demostrado correcto, en el sentido matemático del término)
  • Composición de programas: Pequeños segmentos de programas pueden ser combinados de manera sencilla, permitiendo la generación de código complejo expresado sucintamente
  • El flujo de control y/o el orden de ejecución no necesita ser indicado explícitamente
O'Caml es una interesante encarnación de los lenguajes funcionales. Ofrece todas las ventajas descritas (con la excepción del orden de ejecución, el cual sigue siendo explicito). Además, permite programar en otros paradigmas como el imperativo o el orientado a objetos. Agreguesele a ello su robusto sistemas de tipos, el cual permite detectar muchos errores en tiempo de compilación y generar un código super-optimizado.

Adicionalmente, O'Caml parece haberse trazado una meta poco común para los lenguajes funcionales: competir en el terreno de los lenguajes imperativos. Así, O'Caml ofrece librerías para la programación bajo UNIX, depuradores, profilers...

Esto hace del O'Caml uno de los lenguajes mas interesantes y rápidos del vecindario [1], apto incluso para la programación de bajo nivel. De hecho, mientras escribía la entrada respectiva al lenguaje de programación D, para familiarizarme con el, decidí implementar el programa supervise de Dan Berstein. Pues, para mi sorpresa resultó mas directo hacerlo en O'Caml que en D, debido al mejor soporte de las librerías del sistema (el D es facílmente extensible también; no se trata de una crítica al lenguaje D, sino un reconocimiento a las librerías estándares de O'Caml)


[1] Si, si. Son micro-benchmarks. Si, si. Estas pruebas no representan el uso real de los lenguajes in the wild, y todas las advertencias de costumbre cuando se habla de benchmarks. Pero... mientras no haya una forma científica de comparar los lenguajes, una prueba justa es al menos un indicativo a considerar.

Friday, January 19, 2007

Que viene despues de "C"?: "D"

Habiendo establecido que el lenguaje C es una opción cada vez menos atractiva para la programación de sistemas, cabe preguntar: cual es la alternativa? Existen varias, entre ellas:
  • C++
  • D
  • OCaml
  • SML
La opción mas obvia, el C++ es la menos interesante. Dado que el C++ es una extensión del C cuyo objetivo era mantener la compatibilidad con el mismo a nivel de código fuente, presenta las mismas debilidades. C++ es decepcionante en muchos aspectos. No va lo suficientemente lejos en su intención renovadora. Ofrece programación orientada a objetos y templates, pero no hace ningún esfuerzo por eliminar las debilidades del C. No agrega recolección de basura y su sintaxis es perversamente compleja. Pese a todo ello el C++ conquisto parte del mercado del C.


Por que?

Precisamente debido a su bajo perfil pudo el C++ colarse en el mercado. Casi puedo escuchar el razonamiento de los primeros gerentes en adoptarlo en sus proyectos: "Quieres cambiarlo todo para que nada cambie? Quieres mantenerte a la moda sin arriesgar un ápice? C++ es para ti". Sumele a ello la inercia de la interacción con librerías y código heredado, el temor al reentrenamiento, y tendremos un panorama en el que el el heredero de C debía provenir de su mismo linaje.
Desde luego, también estaban los programadores que deseaban probar el paradigma de la programación orientada a objetos y no conocían nada mejor. Como podrían conocer otra cosa? Quien les hablaría de otros paradigmas, como la programación funcional o implementaciones mas radicales y puras como Smalltalk?


En donde nos deja todo esto?

Existen otros lenguajes no emparentados directamente con el C que permiten la programación de sistemas y ofrecen una velocidad igual o superior. En efecto, el C, el lenguaje mas veloz de la cuadra, el hasta hace poco imbatible líder en rendimiento, se ha visto superado por otros contenedores, los cuales ofrecen además mejoras en otras áreas.


D


Sobre las alternativas en el paradigma funcional (OCaml y SML) hablaré en otra ocasión. Centremonos en el mas clásico D. El lenguaje D es a la programación de sistemas lo que Java es a la programación de aplicaciones generales: la satisfacción a la promesa incumplida por C++ de un mejor C.

D no pretende ser compatible a nivel de código fuente con C. Sin embargo, si ofrece compatibilidad a nivel binario. Esta característica clave ya lo separa del montón. Para que un lenguaje de alto rendimiento aspire tener algún éxito, debe necesariamente poder interoperar con las librerías heredadas. Muchos lenguajes conceptualmente mas interesantes que el C han fracasado por el problema del huevo y la gallina (o es el programador y la librería?): El lenguaje es interesante pero no se usa para el desarrollo porque no tiene suficientes librerías, las cuales no son escritas porque no hay suficientes programadores trabajando en el lenguaje porque... no tiene suficientes librerías.

D ha escapado graciosamente a este circulo vicioso al no reinventar la rueda. Las estructuras de datos entre C y D son compatibles. Además, D usa la misma ABI que C. Esto significa que, dado un binario, no hay forma de distinguir si proviene de código fuente escrito en C o en D.

Pero D ofrece mucho mas que compatibilidad binaria:
  • Recolección de basura con capacidad de ser desactivada, permitiendo el manejo manual de la memoria.
  • Programación orientada a objetos.
  • Eliminación de las arbitrariedades e inconsistencias del C.
  • ...manteniendo una sintaxis semejante.
  • Eliminación del preprocesador de texto.
  • Un poderoso sistema de tipos.
  • Las mas importantes estructuras de datos están predefinidas en el lenguaje.
  • Implementación en software libre.
En definitiva, en la clase de lenguajes imperativos destinados a la programación de sistemas o aplicaciones, ya existe una alternativa viable a C/C++. Solo el miedo irracional a nuevas alternativas mantendrá al C en su posición actual.

Monday, January 15, 2007

Migración a Blogger

He migrado todas las entradas del blog a un nuevo proveedor, http://www2.blogger.com/. Por esta razón todos los post previos a este tienen la misma fecha.

El mal C

No deja de resultar paradójico que las características que hacen que un lenguaje triunfe suelen ser las mismas que ocasionan que este deje de ser una alternativa viable. Esta aparente paradoja se entiende cuando se analizan los cambios en el entorno: mientras un lenguaje sigue siendo básicamente el mismo conforme pasa el tiempo, el poder de computo, la memoria y el tipo de problemas evolucionan continuamente.

En una entrada anterior comente que hizo grande al C. Ahora comentare por que el C es cada vez menos una opción valida en un conjunto cada vez mas grande de problemas.
Las principales características que evidencian la edad del C son:

  • Sin colección de memoria
  • Sistema de tipos débil
  • Muy de bajo nivel
  • Pobre especificación

La falta de un recolector de memoria, la cual es una atractiva característica en la programación de sistemas es una pesadilla en casi cualquier otro ámbito. Con excepciones limitadas a cierto tipo de problemas, el ser humano no es mejor administrando la memoria que el compilador. Esta simple verdad es difícil de aceptar por los miembros de la vieja escuela, incluso cuando ellos mismos confían rutinariamente en el compilador para optimizar su código.

El sistemas de tipo del C es otra de esas características que son una bendición/maldición. En el momento de su diseño, el sistema de tipos del C era elogiable: lo suficientemente simple para ser implementado sucintamente, lo suficientemente poderoso para generar código optimizado, lo suficientemente flexible para competir con el ensamblador. Eso fue entonces. Ahora existen sistemas de tipos mas poderosos que permitir expresar los problemas de manera mas clara y ahorrar lineas de código. Estos mismos sistemas, al ser mas estrictos, permiten hacer mas y mejores optimizaciones, por lo cual el C ya no es el lenguaje mas rápido del oeste (véase el lenguaje D, Ocaml o SML).

Otro aspecto que ha cambiado en el ecosistema, es que con el aumento en la velocidad de los procesadores y su memoria, muchas tareas privilegian el tiempo de programación sobre el tiempo de ejecución. Esto hace que el C sea un lenguaje de muy bajo nivel. La administración de sistemas, la cual se hacia antes con C, se hace ahora con bash, perl o python. El desarrollo de aplicaciones web es dominada por lenguajes interpretados. Incluso el uso de lenguajes mas flexibles ha llegado al escritorio de la mano de java/C#-mono/tcl-tk/python.

El ultimo punto, la pobre especificación del C tiene que ver con el hecho que el lenguaje, en aras de permitirle al implementador obtener la máxima eficiencia en la plataforma de destino, se negó a regular de manera completa el comportamiento del lenguaje. Aunque admito que este seria un problema aun mas grave de no haber sido superado por la incompatibilidad entre librerías en el mundo UNIX…

El buen C

Con mas de treinta años de existencia, el lenguaje C aun es uno de los mas usados para escribir las aplicaciones con las que el usuario final se relaciona. Virtualmente todos los sistemas operativos de uso masivo son escritos en el, al igual que muchas aplicaciones en las que la velocidad es critica.

Si bien las costuras del lenguaje tienen tiempo siendo evidentes, hoy quiero escribir de las características que hicieron al C atractivo durante este periodo. En otro momento escribiré sobre los elementos que lo hacen hoy día, a mi modo de ver, dispensable.

En la época en la que el C fue desarrollado los recursos del sistema eran sumamente escasos para los estándares actuales. Los costosos minicomputadores median su memoria en Kilobytes en vez de gigabytes y la programación en ensamblador aun era común.

Bajo estas condiciones el C hizo lo mejor que pudo:

  • Permitir expresiones aritméticas y lógicas en alto nivel, siguiendo la tradición de los lenguajes imperativos que lo precedieron.
  • Permitir el manejo de la memoria con una flexibilidad comparable al del lenguaje ensamblador.
  • Usar un sistema de tipos que permite la generación de código optimizado.
  • Dejar el manejo de memoria al programador.

Estas características le valieron al C el ser considerado el lenguaje de “alto-bajo nivel”: lo suficientemente alto como para que no fuera tedioso escribir código para el procesamiento de datos y lo suficientemente bajo para sustituir al ensamblador el 99.9% de las veces. Sumesele a ello que fue el lenguaje utilizado para escribir el sistema operativo UNIX y se tiene una combinación ganadora.

La razón por la que el lenguaje C se ha mantenido saludable durante estas décadas (además de la inercia causada por el volumen de librerías a su disposición) es que durante todo este tiempo no ha habido alternativas creíbles e interoperables que permitan hacer programación de sistemas. El controlar ese nicho ha mantenido la preponderancia del C en la escritura de sistemas operativos y librerías de soporte.

Tal situación podría estar por cambiar, aunque eso es motivo para otro post.

Hacia el Sistema Operativo del siglo XXI

Hay muchos conceptos interesantes en el desarrollo de sistemas operativos. Buena parte de ellos son diseños totalmente nuevos. Si bien el desarrollo desde cero ofrece el terreno óptimo para la innovación, experiencias del pasado sugiere (el computador personal, MSDOS, ethernet…) que la evolución de los sistemas actuales tiene mayores probabilidades de éxito.

En este orden de ideas, cuales son las áreas en las que Linux necesita evolucionar para hacerse mas flexible y coherente? He aquí algunas posibilidades:

  • Una nueva jerarquía en el filesystem: La jerarquía de directorios de linux, si bien es mas coherente que la de Windows, aun tiene mucho espacio para mejorar. Otros Unixes ya han comenzado el proceso, desde NeXT, hasta Mac OSX. Un ejemplo interesante dentro del mundo del Linux es Gobolinux.
  • Paquetes para aplicaciones: Los sistemas de paquetes dependen de la jerarquía actual en el sistema de archivos, lo cual tiene sus inconvenientes: esparcen los archivos de una apelación entre varios directorios y tienen dificultades con múltiples versiones de librerías.
  • Un nuevo sistema de archivos: En particular uno que permita asociar propiedades a los archivos. La mas reciente versión de ReiserFS podría permitirlo a través de plugins.
  • Nuevos mecanismos de seguridad: La seguridad de UNIX basada en usuario-grupo-otros ya esta mostrando su edad. Si bien es suficiente para muchos entornos, en la actualidad ya hay varias opciones con distintos grados de utilidad.

Estos cambios son superficiales y pueden implementarse en nuevas distribuciones sin tocar el núcleo. Hay otras mas ambiciosas, las cuales requieren abandonar Linux tal y como lo conocemos. En futuros artículos comentare algunos de esos cambios.

Uno en particular, es abandonar el mantra de UNIX “todo es un archivo”. Es cierto, esa concepción fue una de sus grandes fortalezas, y tal vez la mas influyente. Sin embargo, la vieja conseja nunca fue del todo cierta. Comenzando con la división entre dispositivos de bloque y de carácter. Adicionalmente, las infames llamadas ioctl también conocidas como todo-lo-que-no-cabe-en-la-interface-de-archivos-va-acá. Un paso adelante seria el reconocimiento que todo no es un archivo y adoptar un enfoque mas liberal. En vez de tener una jerarquía de archivos, el sistema seria un espacio de nombres de objetos.

Sustituir “archivos” por “objetos” puede sonar cosmético, pero elimina la necesidad de ioctl, homogeniza los conceptos y permite al SO adaptarse a necesidades cambiantes. Desarrollare el concepto en próximo artículos.


Por que calcular es mejor que esquematizar?

En http://www.cs.kent.ac.uk/people/staff/dat/miranda/wadler87.pdf se puede encontrar el paper de Philip WadlerWhy calculating is better than scheming”. Este paper compara lenguajes de la familia lisp/scheme con lenguajes tipo miranda/haskell en el contexto del excelente libro de Abelson y SussmanStructure and Interpretation of Computer Programs” (SICP).

El paper analiza temas aun no resueltos en el área como:

  • Definición de tipos
  • Notación prefija vs. infija
  • Las virtudes del sistema de macros de lisp

Su conclusión? Que la notación infija, la posibilidad de declarar tipos de datos, el uso de tipos de datos algebraicos y el emparejamiento de patrones hacen que la intención del programador sea expresada de manera mas clara y concisa; que la identidad entre código y datos en lisp puede ser un importante factor de confusión para los programadores noveles (y los no tanto); que la evaluación perezosa ofrece una elegante homogenización de los lenguajes que la poseen.

En definitiva, lisp no sale muy bien parado en el análisis de Wadler. Tal vez ello le condujo al desarrollo de Haskell

Sobre Strong Typed

Strong Typed es un blog dedicado al estudio de la ciencia de la computación, programación y sistemas operativos. Otros temas afines, en lo tocante a matemática también tienen cabida acá.