Luan - software reaccionario por fschmidt

Scripting: Programación de Alto Nivel para el Siglo XXI

John K. Ousterhout


Tcl Developer Xchange
2593 Coast Ave.
Mountain View, CA 94043

(Este artículo aparece en la revista IEEE Computer, marzo de 1998)
Resumen
Los lenguajes de scripting como Perl y Tcl representan un estilo de programación muy diferente al de los lenguajes de programación de sistemas como C o JavaTM. Los lenguajes de scripting están diseñados para "unir" aplicaciones; utilizan enfoques sin tipos para lograr un nivel más alto de programación y un desarrollo de aplicaciones más rápido que los lenguajes de programación de sistemas. Los aumentos en la velocidad de las computadoras y los cambios en la mezcla de aplicaciones están haciendo que los lenguajes de scripting sean cada vez más importantes para las aplicaciones del futuro.
Palabras clave: marcos de componentes, programación orientada a objetos, scripting, tipado fuerte, programación de sistemas.

1 Introducción

Durante los últimos quince años ha estado ocurriendo un cambio fundamental en la forma en que las personas escriben programas de computadora. El cambio es una transición de lenguajes de programación de sistemas como C o C++ a lenguajes de scripting como Perl o Tcl. Aunque muchas personas están participando en el cambio, pocas personas se dan cuenta de que está ocurriendo y aún menos personas saben por qué está sucediendo. Este artículo es una pieza de opinión que explica por qué los lenguajes de scripting manejarán muchas de las tareas de programación del próximo siglo mejor que los lenguajes de programación de sistemas.

Los lenguajes de scripting están diseñados para tareas diferentes a las de los lenguajes de programación de sistemas, y esto lleva a diferencias fundamentales en los lenguajes. Los lenguajes de programación de sistemas fueron diseñados para construir estructuras de datos y algoritmos desde cero, comenzando desde los elementos más primitivos de la computadora, como las palabras de memoria. En contraste, los lenguajes de scripting están diseñados para unir: asumen la existencia de un conjunto de componentes poderosos y están destinados principalmente a conectar componentes entre sí. Los lenguajes de programación de sistemas son fuertemente tipados para ayudar a manejar la complejidad, mientras que los lenguajes de scripting son sin tipos para simplificar las conexiones entre componentes y proporcionar un desarrollo rápido de aplicaciones.

Los lenguajes de scripting y los lenguajes de programación de sistemas son complementarios, y la mayoría de las plataformas informáticas importantes desde la década de 1960 han proporcionado ambos tipos de lenguajes. Los lenguajes se utilizan típicamente juntos en marcos de componentes, donde los componentes se crean con lenguajes de programación de sistemas y se unen con lenguajes de scripting. Sin embargo, varias tendencias recientes, como máquinas más rápidas, mejores lenguajes de scripting, la creciente importancia de las interfaces gráficas de usuario y las arquitecturas de componentes, y el crecimiento de Internet, han aumentado enormemente la aplicabilidad de los lenguajes de scripting. Estas tendencias continuarán durante la próxima década, con más y más aplicaciones nuevas escritas completamente en lenguajes de scripting y lenguajes de programación de sistemas utilizados principalmente para crear componentes.

2 Lenguajes de programación de sistemas

Para entender las diferencias entre los lenguajes de scripting y los lenguajes de programación de sistemas, es importante entender cómo evolucionaron los lenguajes de programación de sistemas. Los lenguajes de programación de sistemas se introdujeron como una alternativa a los lenguajes ensambladores. En los lenguajes ensambladores, prácticamente todos los aspectos de la máquina se reflejan en el programa. Cada instrucción representa una sola instrucción de máquina y los programadores deben lidiar con detalles de bajo nivel como la asignación de registros y las secuencias de llamadas a procedimientos. Como resultado, es difícil escribir y mantener programas grandes en lenguaje ensamblador.

A finales de la década de 1950 comenzaron a aparecer lenguajes de nivel superior como Lisp, Fortran y Algol. En estos lenguajes, las instrucciones ya no corresponden exactamente a las instrucciones de máquina; un compilador traduce cada instrucción en el programa fuente en una secuencia de instrucciones binarias. Con el tiempo, una serie de lenguajes de programación de sistemas evolucionaron a partir de Algol, incluyendo lenguajes como PL/1, Pascal, C, C++ y Java. Los lenguajes de programación de sistemas son menos eficientes que los lenguajes ensambladores, pero permiten que las aplicaciones se desarrollen mucho más rápidamente. Como resultado, casi han reemplazado por completo a los lenguajes ensambladores para el desarrollo de aplicaciones grandes.

Los lenguajes de programación de sistemas difieren de los lenguajes ensambladores en dos formas: son de nivel superior y están fuertemente tipados. El término "nivel superior" significa que muchos detalles se manejan automáticamente para que los programadores puedan escribir menos código para hacer el mismo trabajo. Por ejemplo:

En promedio, cada línea de código en un lenguaje de programación de sistemas se traduce en unas cinco instrucciones de máquina, en comparación con una instrucción por línea en lenguaje ensamblador (en un análisis informal de ocho archivos C escritos por cinco personas diferentes, encontré que la proporción varió de aproximadamente 3 a 7 instrucciones por línea[7]; en un estudio de numerosos lenguajes, Capers Jones encontró que para una tarea dada, los lenguajes ensambladores requieren aproximadamente de 3 a 6 veces más líneas de código que los lenguajes de programación de sistemas[3]). Los programadores pueden escribir aproximadamente el mismo número de líneas de código por año independientemente del lenguaje[1], por lo que los lenguajes de programación de sistemas permiten que las aplicaciones se escriban mucho más rápidamente que en lenguaje ensamblador.

La segunda diferencia entre el lenguaje ensamblador y los lenguajes de programación de sistemas es el tipado. Utilizo el término "tipado" para referirme al grado en que el significado de la información se especifica antes de su uso. En un lenguaje fuertemente tipado, el programador declara cómo se utilizará cada pieza de información y el lenguaje impide que la información se use de cualquier otra manera. En un lenguaje débilmente tipado no hay restricciones a priori sobre cómo se puede usar la información: el significado de la información se determina únicamente por la forma en que se usa, no por ninguna promesa inicial.1

Las computadoras modernas son fundamentalmente sin tipos: cualquier palabra en memoria puede contener cualquier tipo de valor, como un entero, un número de punto flotante, un puntero o una instrucción. El significado de un valor se determina por cómo se usa: si el contador de programa apunta a una palabra de memoria, entonces se trata como una instrucción; si una palabra es referenciada por una instrucción de suma de enteros, entonces se trata como un entero; y así sucesivamente. La misma palabra puede usarse de diferentes maneras en diferentes momentos.

En contraste, los lenguajes de programación de sistemas de hoy en día están fuertemente tipados. Por ejemplo:

El tipado tiene varias ventajas. Primero, hace que los programas grandes sean más manejables al aclarar cómo se usan las cosas y diferenciar entre cosas que deben tratarse de manera diferente. Segundo, los compiladores pueden usar la información de tipo para detectar ciertos tipos de errores, como un intento de usar un valor de punto flotante como un puntero. Tercero, el tipado mejora el rendimiento al permitir que los compiladores generen código especializado. Por ejemplo, si un compilador sabe que una variable siempre contiene un valor entero, entonces puede generar instrucciones enteras para manipular la variable; si el compilador no conoce el tipo de una variable, entonces debe generar instrucciones adicionales para verificar el tipo de la variable en tiempo de ejecución.

En resumen, los lenguajes de programación de sistemas están diseñados para manejar las mismas tareas que los lenguajes ensambladores, es decir, crear aplicaciones desde cero. Los lenguajes de programación de sistemas son de nivel superior y mucho más fuertemente tipados que los lenguajes ensambladores. Esto permite que las aplicaciones se creen más rápidamente y se gestionen más fácilmente con solo una ligera pérdida de rendimiento. Vea Figura 1 para una comparación gráfica del lenguaje ensamblador y varios lenguajes de programación de sistemas.

3 Lenguajes de scripting

Los lenguajes de scripting como Perl[9], Python[4], Rexx[6], Tcl[8], Visual Basic y los shells de Unix representan un estilo de programación muy diferente al de los lenguajes de programación de sistemas. Los lenguajes de scripting asumen que ya existe una colección de componentes útiles escritos en otros lenguajes. Los lenguajes de scripting no están destinados a escribir aplicaciones desde cero; están destinados principalmente a conectar componentes. Por ejemplo, Tcl y Visual Basic se pueden usar para organizar colecciones de controles de interfaz de usuario en la pantalla, y los scripts de shell de Unix se utilizan para ensamblar programas de filtro en tuberías. Los lenguajes de scripting se utilizan a menudo para extender las características de los componentes, pero rara vez se utilizan para algoritmos complejos y estructuras de datos; características como estas suelen ser proporcionadas por los componentes. Los lenguajes de scripting a veces se denominan lenguajes de pegamento o lenguajes de integración de sistemas.

Para simplificar la tarea de conectar componentes, los lenguajes de scripting tienden a ser sin tipos: todas las cosas se ven y se comportan de la misma manera para que sean intercambiables. Por ejemplo, en Tcl o Visual Basic, una variable puede contener una cadena en un momento y un entero al siguiente. El código y los datos a menudo son intercambiables, de modo que un programa puede escribir otro programa y luego ejecutarlo sobre la marcha. Los lenguajes de scripting a menudo están orientados a cadenas, ya que esto proporciona una representación uniforme para muchas cosas diferentes.

Un lenguaje sin tipos hace que sea mucho más fácil conectar componentes. No hay restricciones a priori sobre cómo se pueden usar las cosas, y todos los componentes y valores se representan de manera uniforme. Por lo tanto, cualquier componente o valor puede usarse en cualquier situación; los componentes diseñados para un propósito pueden usarse para propósitos totalmente diferentes nunca previstos por el diseñador. Por ejemplo, en los shells de Unix, todos los programas de filtro leen un flujo de bytes de una entrada y escriben una cadena de bytes en una salida; cualquier dos programas pueden conectarse juntos adjuntando la salida de un programa a la entrada del otro. El siguiente comando de shell apila tres filtros juntos para contar el número de líneas en la selección que contienen la palabra "scripting":

select | grep scripting | wc El programa select lee el texto que está actualmente seleccionado en la pantalla y lo imprime en su salida; el programa grep lee su entrada e imprime en su salida las líneas que contienen "scripting"; el programa wc cuenta el número de líneas en su entrada. Cada uno de estos programas puede usarse en numerosas otras situaciones para realizar diferentes tareas.

La naturaleza fuertemente tipada de los lenguajes de programación de sistemas desalienta la reutilización. El tipado alienta a los programadores a crear una variedad de interfaces incompatibles ("las interfaces son buenas; más interfaces son mejores"). Cada interfaz requiere objetos de tipos específicos y el compilador impide que cualquier otro tipo de objetos se use con la interfaz, incluso si eso sería útil. Para usar un nuevo objeto con una interfaz existente, debe escribirse código de conversión para traducir entre el tipo del objeto y el tipo esperado por la interfaz. Esto a su vez requiere recompilar parte o toda la aplicación, lo cual no es posible en el caso común donde la aplicación se distribuye en forma binaria.

Para ver las ventajas de un lenguaje sin tipos, considere el siguiente comando Tcl:

button .b -text Hello! -font {Times 16} -command {puts hello} Este comando crea un nuevo control de botón que muestra una cadena de texto en una fuente Times de 16 puntos e imprime un mensaje corto cuando el usuario hace clic en el control. Mezcla seis tipos diferentes de cosas en una sola instrucción: un nombre de comando (button), un control de botón (.b), nombres de propiedades (-text, -font y -command), cadenas simples (Hello! y hello), un nombre de fuente (Times 16) que incluye un nombre de tipo de letra (Times) y un tamaño en puntos (16), y un script Tcl (puts hello). Tcl representa todas estas cosas de manera uniforme con cadenas. En este ejemplo, las propiedades pueden especificarse en cualquier orden y las propiedades no especificadas reciben valores predeterminados; más de 20 propiedades quedaron sin especificar en el ejemplo.

El mismo ejemplo requiere 7 líneas de código en dos métodos cuando implementado en Java. Con C++ y Microsoft Foundation Classes, requiere aproximadamente 25 líneas de código en tres procedimientos (ver [7] para el código de estos ejemplos). Solo establecer la fuente requiere varias líneas de código en Microsoft Foundation Classes:

CFont *fontPtr = new CFont();
fontPtr->CreateFont(16, 0, 0,0,700, 0, 0, 0, ANSI_CHARSET,
OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
DEFAULT_PITCH|FF_DONTCARE, "Times New Roman");
buttonPtr->SetFont(fontPtr);
Gran parte de este código es una consecuencia del tipado fuerte. Para establecer la fuente de un botón, debe invocarse su método SetFont, pero este método debe recibir un puntero a un objeto CFont. Esto a su vez requiere que se declare e inicialice un nuevo objeto. Para inicializar el objeto CFont debe invocarse su método CreateFont, pero CreateFont tiene una interfaz rígida que requiere que se especifiquen 14 argumentos diferentes. En Tcl, las características esenciales de la fuente (tipo de letra Times, tamaño 16 puntos) pueden usarse inmediatamente sin declaraciones ni conversiones. Además, Tcl permite que el comportamiento del botón se incluya directamente en el comando que crea el botón, mientras que C++ y Java requieren que se coloque en un método declarado por separado.

(En la práctica, un ejemplo trivial como este probablemente se manejaría con un entorno de desarrollo gráfico que oculta la complejidad del lenguaje subyacente: el usuario ingresa valores de propiedad en un formulario y el entorno de desarrollo genera el código. Sin embargo, en situaciones más complejas como la asignación condicional de valores de propiedad o interfaces generadas programáticamente, el desarrollador debe escribir código en el lenguaje subyacente.)

Podría parecer que la naturaleza sin tipos de los lenguajes de scripting podría permitir que los errores pasen desapercibidos, pero en la práctica los lenguajes de scripting son tan seguros como los lenguajes de programación de sistemas. Por ejemplo, ocurrirá un error si el tamaño de fuente especificado para el ejemplo del botón anterior es una cadena no entera como xyz. La diferencia es que los lenguajes de scripting realizan su verificación de errores en el último momento posible, cuando se usa un valor. El tipado fuerte permite que los errores se detecten en tiempo de compilación, por lo que se evita el costo de las verificaciones en tiempo de ejecución. Sin embargo, el precio a pagar por esta eficiencia son las restricciones sobre cómo se puede usar la información: esto resulta en más código y programas menos flexibles.

Otra diferencia clave entre los lenguajes de scripting y los lenguajes de programación de sistemas es que los lenguajes de scripting suelen ser interpretados, mientras que los lenguajes de programación de sistemas suelen ser compilados. Los lenguajes interpretados proporcionan una rápida respuesta durante el desarrollo al eliminar los tiempos de compilación. Los intérpretes también hacen que las aplicaciones sean más flexibles al permitir que los usuarios programen las aplicaciones en tiempo de ejecución. Por ejemplo, muchas herramientas de síntesis y análisis para circuitos integrados incluyen un intérprete Tcl; los usuarios de los programas escriben scripts Tcl para especificar sus diseños y controlar el funcionamiento de las herramientas. Los intérpretes también permiten lograr efectos poderosos generando código sobre la marcha. Por ejemplo, un navegador web basado en Tcl puede analizar una página web traduciendo el HTML de la página en un script Tcl usando unas pocas sustituciones de expresiones regulares. Luego ejecuta el script Tcl para renderizar la página en la pantalla.

Los lenguajes de scripting son menos eficientes que los lenguajes de programación de sistemas, en parte porque utilizan intérpretes en lugar de compiladores, pero también porque sus componentes básicos se eligen por su potencia y facilidad de uso en lugar de un mapeo eficiente en el hardware subyacente. Por ejemplo, los lenguajes de scripting a menudo usan cadenas de longitud variable en situaciones donde un lenguaje de programación de sistemas usaría un valor binario que cabe en una sola palabra de máquina, y los lenguajes de scripting a menudo usan tablas hash donde los lenguajes de programación de sistemas usan matrices indexadas.

Afortunadamente, el rendimiento de un lenguaje de scripting no suele ser un problema importante. Las aplicaciones para lenguajes de scripting son generalmente más pequeñas que las aplicaciones para lenguajes de programación de sistemas, y el rendimiento de una aplicación de scripting tiende a estar dominado por el rendimiento de los componentes, que generalmente se implementan en un lenguaje de programación de sistemas.

Los lenguajes de scripting son de nivel más alto que los lenguajes de programación de sistemas, en el sentido de que una sola instrucción hace más trabajo en promedio. Una instrucción típica en un lenguaje de scripting ejecuta cientos o miles de instrucciones de máquina, mientras que una instrucción típica en un lenguaje de programación de sistemas ejecuta unas cinco instrucciones de máquina (ver Figura 1). Parte de esta diferencia se debe a que los lenguajes de scripting usan intérpretes, que son menos eficientes que el código compilado para lenguajes de programación de sistemas. Pero gran parte de la diferencia se debe a que las operaciones primitivas en los lenguajes de scripting tienen mayor funcionalidad. Por ejemplo, en Perl es tan fácil invocar una sustitución de expresión regular como invocar una suma de enteros. En Tcl, una variable puede tener trazas asociadas con ella para que establecer la variable cause efectos secundarios; por ejemplo, una traza podría usarse para mantener el valor de la variable actualizado continuamente en la pantalla.

Debido a las características descritas anteriormente, los lenguajes de scripting permiten un desarrollo muy rápido para aplicaciones que están orientadas a la unión. Tabla 1 proporciona apoyo anecdótico para esta afirmación. Describe varias aplicaciones que se implementaron en un lenguaje de programación de sistemas y luego se reimplementaron en un lenguaje de scripting, o viceversa.

En todos los casos, la versión de scripting requirió menos código y tiempo de desarrollo que la versión de programación de sistemas; la diferencia varió de un factor de 2 a un factor de 60. Los lenguajes de scripting proporcionaron menos beneficio cuando se usaron para la primera implementación; esto sugiere que cualquier reimplementación se beneficia sustancialmente de las experiencias de la primera implementación y que la verdadera diferencia entre scripting y programación de sistemas es más como un factor de 5-10x que los puntos extremos de la tabla. Los beneficios del scripting también dependen de la aplicación. En el último ejemplo de la tabla, la parte GUI de la aplicación está orientada a la unión, pero la parte del simulador no lo está; esto puede explicar por qué la aplicación se benefició menos del scripting que otras aplicaciones.

En resumen, los lenguajes de scripting están diseñados para unir aplicaciones. Proporcionan un nivel más alto de programación que los lenguajes ensambladores o de programación de sistemas, un tipado mucho más débil que los lenguajes de programación de sistemas y un entorno de desarrollo interpretado. Los lenguajes de scripting sacrifican la velocidad de ejecución para mejorar la velocidad de desarrollo.

4 Herramientas diferentes para tareas diferentes

Un lenguaje de scripting no es un reemplazo para un lenguaje de programación de sistemas o viceversa. Cada uno está adaptado a un conjunto diferente de tareas. Para unir e integrar sistemas, las aplicaciones pueden desarrollarse 5-10x más rápido con un lenguaje de scripting; los lenguajes de programación de sistemas requerirán grandes cantidades de código repetitivo y de conversión para conectar las piezas, mientras que esto puede hacerse directamente con un lenguaje de scripting. Para algoritmos complejos y estructuras de datos, el tipado fuerte de un lenguaje de programación de sistemas hace que los programas sean más fáciles de gestionar. Donde la velocidad de ejecución es clave, un lenguaje de programación de sistemas puede ejecutarse 10-20x más rápido que un lenguaje de scripting porque realiza menos verificaciones en tiempo de ejecución.

Al decidir si usar un lenguaje de scripting o un lenguaje de programación de sistemas para una tarea en particular, considere las siguientes preguntas:

Las respuestas "sí" a estas preguntas sugieren que un lenguaje de scripting funcionará bien para la aplicación. Por otro lado, las respuestas "sí" a las siguientes preguntas sugieren que una aplicación es más adecuada para un lenguaje de programación de sistemas:

La mayoría de las plataformas informáticas importantes de los últimos 30 años han proporcionado tanto lenguajes de programación de sistemas como lenguajes de scripting. Por ejemplo, uno de los primeros lenguajes de scripting, aunque crudo, fue JCL (Job Control Language), que se utilizó para secuenciar pasos de trabajo en OS/360. Los pasos de trabajo individuales se escribieron en PL/1, Fortran o lenguaje ensamblador, que eran los lenguajes de programación de sistemas de la época. En las máquinas Unix de la década de 1980, se usaba C para la programación de sistemas y programas de shell como sh y csh para scripting. En el mundo de las PC de la década de 1990, se usaban C y C++ para la programación de sistemas y Visual Basic para scripting. En el mundo de Internet que se está formando ahora, se usa Java para la programación de sistemas y lenguajes como JavaScript, Perl y Tcl para scripting.

El scripting y la programación de sistemas son simbióticos. Usados juntos, producen entornos de programación de poder excepcional: los lenguajes de programación de sistemas se utilizan para crear componentes emocionantes que luego pueden ensamblarse utilizando lenguajes de scripting. Por ejemplo, gran parte de la atracción de Visual Basic es que los programadores de sistemas pueden escribir componentes ActiveX en C y programadores menos sofisticados pueden luego usar los componentes en aplicaciones de Visual Basic. En Unix es fácil escribir scripts de shell que invoquen aplicaciones escritas en C. Una de las razones de la popularidad de Tcl es la capacidad de extender el lenguaje escribiendo código C que implemente nuevos comandos.

5 El scripting está en aumento

Los lenguajes de scripting han existido durante mucho tiempo, pero en los últimos años varios factores se han combinado para aumentar su importancia. El factor más importante es un cambio en la mezcla de aplicaciones hacia aplicaciones de unión. Tres ejemplos de este cambio son las interfaces gráficas de usuario, Internet y los marcos de componentes.

Las interfaces gráficas de usuario (GUIs) comenzaron a aparecer a principios de la década de 1980 y se generalizaron a finales de la década; las GUIs ahora representan la mitad o más del esfuerzo total en muchos proyectos de programación. Las GUIs son aplicaciones fundamentalmente de unión: el objetivo no es crear nueva funcionalidad, sino hacer conexiones entre una colección de controles gráficos y las funciones internas de la aplicación. No estoy al tanto de ningún entorno de desarrollo rápido para GUIs basado en un lenguaje de programación de sistemas. Ya sea que el entorno sea Windows, Macintosh Toolbox o Unix Motif, los kits de herramientas GUI basados en lenguajes como C o C++ han demostrado ser difíciles de aprender, torpes de usar e inflexibles en los resultados que producen. Algunos de estos sistemas tienen herramientas gráficas muy agradables para diseñar diseños de pantalla que ocultan el lenguaje subyacente, pero las cosas se vuelven difíciles tan pronto como el diseñador tiene que escribir código, por ejemplo, para proporcionar los comportamientos para los elementos de la interfaz. Todos los mejores entornos de desarrollo rápido de GUI están basados en lenguajes de scripting: Visual Basic, HyperCard y Tcl/Tk. Por lo tanto, los lenguajes de scripting han aumentado en popularidad a medida que ha aumentado la importancia de las GUIs.

El crecimiento de Internet también ha popularizado los lenguajes de scripting. Internet no es más que una herramienta de unión. No crea nuevos cálculos o datos; simplemente hace que una gran cantidad de cosas existentes sean fácilmente accesibles. El lenguaje ideal para la mayoría de las tareas de programación de Internet es uno que haga posible que todos los componentes conectados trabajen juntos, es decir, un lenguaje de scripting. Por ejemplo, Perl se ha vuelto popular para escribir scripts CGI y JavaScript es popular para scripting en páginas web.

El tercer ejemplo de aplicaciones orientadas al scripting son los marcos de componentes como ActiveX, OpenDoc y JavaBeans. Aunque los lenguajes de programación de sistemas funcionan bien para crear componentes, la tarea de ensamblar componentes en aplicaciones es más adecuada para el scripting. Sin un buen lenguaje de scripting para manipular los componentes, gran parte del poder de un marco de componentes se pierde. Esto puede explicar en parte por qué los marcos de componentes han tenido más éxito en las PC (donde Visual Basic proporciona una herramienta de scripting conveniente) que en otras plataformas como Unix/CORBA donde el scripting no está incluido en el marco de componentes.

Otra razón para la creciente popularidad de los lenguajes de scripting son las mejoras en la tecnología de scripting. Los lenguajes de scripting modernos como Tcl y Perl son muy diferentes de los primeros lenguajes de scripting como JCL. Por ejemplo, JCL ni siquiera proporcionaba iteración básica y los primeros shells de Unix no admitían procedimientos. La tecnología de scripting todavía es relativamente inmadura incluso hoy en día. Por ejemplo, Visual Basic no es realmente un lenguaje de scripting; fue implementado originalmente como un lenguaje de programación de sistemas simple, luego modificado para hacerlo más adecuado para scripting. Los futuros lenguajes de scripting serán aún mejores que los disponibles hoy en día.

La tecnología de scripting también se ha beneficiado de la velocidad cada vez mayor del hardware de las computadoras. Solía ser que la única manera de obtener un rendimiento aceptable en una aplicación de cualquier complejidad era usar un lenguaje de programación de sistemas. En algunos casos, incluso los lenguajes de programación de sistemas no eran lo suficientemente eficientes, por lo que las aplicaciones tenían que escribirse en ensamblador. Sin embargo, las máquinas de hoy son 100-500 veces más rápidas que las máquinas de 1980 y continúan duplicando su rendimiento cada 18 meses. Hoy en día, muchas aplicaciones pueden implementarse en un lenguaje interpretado y aún tener un rendimiento excelente; por ejemplo, un script Tcl puede manipular colecciones con varios miles de objetos y aún proporcionar una buena respuesta interactiva. A medida que las computadoras se vuelven más rápidas, el scripting se volverá atractivo para aplicaciones cada vez más grandes.

Una razón final para el uso creciente de los lenguajes de scripting es un cambio en la comunidad de programadores. Hace veinte años, la mayoría de los programadores eran programadores sofisticados que trabajaban en proyectos grandes. Los programadores de esa época esperaban pasar varios meses para dominar un lenguaje y su entorno de programación, y los lenguajes de programación de sistemas estaban diseñados para tales programadores. Sin embargo, desde la llegada de la computadora personal, más y más programadores ocasionales se han unido a la comunidad de programadores. Para estas personas, la programación no es su función principal de trabajo; es una herramienta que usan ocasionalmente para ayudar con su trabajo principal. Ejemplos de programación ocasional son consultas simples de bases de datos o macros para una hoja de cálculo. Los programadores ocasionales no están dispuestos a pasar meses aprendiendo un lenguaje de programación de sistemas, pero a menudo pueden aprender lo suficiente sobre un lenguaje de scripting en unas pocas horas para escribir programas útiles. Los lenguajes de scripting son más fáciles de aprender porque tienen una sintaxis más simple que los lenguajes de programación de sistemas y porque omiten características complejas como objetos e hilos. Por ejemplo, compare Visual Basic con Visual C++; pocos programadores ocasionales intentarían usar Visual C++, pero muchos han podido construir aplicaciones útiles con Visual Basic.

Incluso hoy en día, el número de aplicaciones escritas en lenguajes de scripting es mucho mayor que el número de aplicaciones escritas en lenguajes de programación de sistemas. En los sistemas Unix hay muchos más scripts de shell que programas en C, y bajo Windows hay muchos más programadores y aplicaciones de Visual Basic que de C o C++. Por supuesto, la mayoría de las aplicaciones más grandes y más utilizadas están escritas en lenguajes de programación de sistemas, por lo que una comparación basada en el total de líneas de código o el número de copias instaladas puede seguir favoreciendo a los lenguajes de programación de sistemas. No obstante, los lenguajes de scripting ya son una fuerza importante en el desarrollo de aplicaciones y su cuota de mercado aumentará en el futuro.

6 El papel de los objetos

Los lenguajes de scripting han sido en su mayoría pasados por alto por los expertos en lenguajes de programación e ingeniería de software. En cambio, han centrado su atención en lenguajes de programación de sistemas orientados a objetos como C++ y Java. Se cree ampliamente que la programación orientada a objetos representa el próximo gran paso en la evolución de los lenguajes de programación. Se afirma a menudo que las características orientadas a objetos como el tipado fuerte y la herencia reducen el tiempo de desarrollo, aumentan la reutilización de software y resuelven muchos otros problemas, incluidos los abordados por los lenguajes de scripting.

¿Cuánto beneficio ha proporcionado realmente la programación orientada a objetos? Desafortunadamente, no he visto suficientes datos cuantitativos para responder a esta pregunta de manera definitiva. En mi opinión, los objetos proporcionan solo un beneficio modesto: tal vez una mejora del 20-30% en la productividad, pero ciertamente no un factor de dos, y mucho menos un factor de 10. C++ ahora parece ser odiado tanto como amado, y algunos expertos en lenguajes están comenzando a hablar en contra de la programación orientada a objetos [2]. El resto de esta sección explica por qué los objetos no mejoran la productividad de manera dramática como lo hace el scripting, y argumenta que los beneficios de la programación orientada a objetos pueden lograrse en lenguajes de scripting.

La razón por la que la programación orientada a objetos no proporciona una gran mejora en la productividad es que no eleva el nivel de programación ni fomenta la reutilización. En un lenguaje orientado a objetos como C++, los programadores todavía trabajan con unidades básicas pequeñas que deben describirse y manipularse en gran detalle. En principio, podrían desarrollarse paquetes de librerías poderosos, y si estas librerías se usaran extensamente, podrían elevar el nivel de programación. Sin embargo, no han surgido muchas de estas librerías. El tipado fuerte de la mayoría de los lenguajes orientados a objetos fomenta paquetes estrechamente definidos que son difíciles de reutilizar. Cada paquete requiere objetos de un tipo específico; si dos paquetes deben trabajar juntos, debe escribirse código de conversión para traducir entre los tipos requeridos por los paquetes.

Otro problema con los lenguajes orientados a objetos es su énfasis en la herencia. La herencia de implementación, donde una clase toma prestado código que fue escrito para otra clase, es una mala idea que hace que el software sea más difícil de gestionar y reutilizar. Vincula las implementaciones de las clases juntas de modo que ninguna clase puede entenderse sin la otra: una subclase no puede entenderse sin saber cómo se implementan los métodos heredados en su superclase, y una superclase no puede entenderse sin saber cómo se heredan sus métodos en las subclases. En una jerarquía de clases compleja, ninguna clase individual puede entenderse sin entender todas las demás clases en la jerarquía. Aún peor, una clase no puede separarse de su jerarquía para su reutilización. La herencia múltiple hace que estos problemas sean aún peores. La herencia de implementación causa el mismo entrelazamiento y fragilidad que se ha observado cuando se abusan de las declaraciones goto. Como resultado, los sistemas orientados a objetos a menudo sufren de complejidad y falta de reutilización.

Los lenguajes de scripting, por otro lado, han generado realmente una reutilización significativa de software. Utilizan un modelo donde los componentes interesantes se construyen en un lenguaje de programación de sistemas y luego se unen en aplicaciones utilizando el lenguaje de scripting. Esta división del trabajo proporciona un marco natural para la reutilización. Los componentes están diseñados para ser reutilizables, y hay interfaces bien definidas entre componentes y scripts que facilitan el uso de componentes. Por ejemplo, en Tcl los componentes son comandos personalizados implementados en C; se ven igual que los comandos integrados, por lo que son fáciles de invocar en scripts Tcl. En Visual Basic, los componentes son extensiones ActiveX, que pueden usarse arrastrándolos desde una paleta a un formulario.

No obstante, la programación orientada a objetos proporciona al menos dos características útiles. La primera es la encapsulación: los objetos combinan datos y código de una manera que oculta los detalles de implementación. Esto facilita la gestión de sistemas grandes. La segunda característica útil es la herencia de interfaces, que se refiere a clases que proporcionan los mismos métodos y APIs aunque tengan implementaciones diferentes. Esto hace que las clases sean intercambiables, lo que fomenta la reutilización.

Afortunadamente, los beneficios de los objetos pueden lograrse tanto en lenguajes de scripting como en lenguajes de programación de sistemas y prácticamente todos los lenguajes de scripting tienen algún soporte para la programación orientada a objetos. Por ejemplo, Python es un lenguaje de scripting orientado a objetos, Perl versión 5 incluye soporte para objetos, Object Rexx es una versión orientada a objetos de Rexx, e Incr Tcl es una extensión orientada a objetos para Tcl. Una diferencia es que los objetos en los lenguajes de scripting tienden a ser sin tipos, mientras que los objetos en los lenguajes de programación de sistemas tienden a ser fuertemente tipados.

7 Otros lenguajes

Este artículo no está destinado a ser una caracterización completa de todos los lenguajes de programación. Hay muchos otros atributos de los lenguajes de programación además de la fuerza del tipado y el nivel de programación, y hay muchos lenguajes interesantes que no pueden caracterizarse limpiamente como un lenguaje de programación de sistemas o un lenguaje de scripting. Por ejemplo, la familia de lenguajes Lisp se encuentra en algún lugar entre el scripting y la programación de sistemas, con algunos de los atributos de cada uno. Lisp fue pionero en conceptos como la interpretación y el tipado dinámico que ahora son comunes en los lenguajes de scripting, así como la gestión automática de almacenamiento y los entornos de desarrollo integrados, que ahora se utilizan tanto en lenguajes de scripting como de programación de sistemas.

8 Conclusión

Los lenguajes de scripting representan un conjunto diferente de compensaciones que los lenguajes de programación de sistemas. Renuncian a la velocidad de ejecución y la fuerza del tipado en comparación con los lenguajes de programación de sistemas, pero proporcionan una productividad de programador significativamente mayor y una reutilización de software. Esta compensación tiene más sentido a medida que las computadoras se vuelven más rápidas y baratas en comparación con los programadores. Los lenguajes de programación de sistemas son adecuados para construir componentes donde la complejidad está en las estructuras de datos y algoritmos, mientras que los lenguajes de scripting son adecuados para unir aplicaciones donde la complejidad está en las conexiones. Las tareas de unión se están volviendo cada vez más prevalentes, por lo que el scripting se convertirá en un paradigma de programación aún más importante en el próximo siglo de lo que es hoy.

Espero que este artículo impacte a la comunidad informática de tres maneras:

9 Agradecimientos

Este artículo se ha beneficiado de los comentarios de muchas personas, incluidos Joel Bartlett, Bill Eldridge, Jeffrey Haemer, Mark Harrison, Paul McJones, David Patterson, Stephen Uhler, Hank Walker, Chris Wright, los referees de IEEE Computer, y docenas de otros que participaron en una acalorada discusión en net-news de un borrador inicial del artículo. Colin Stevens escribió la versión MFC del ejemplo del botón y Stephen Uhler escribió la versión Java.

10 Referencias

[1] B. Boehm, Software Engineering Economics, Prentice-Hall, ISBN 0-138-22122-7, 1981.

[2] S. Johnson, Objecting To Objects, Invited Talk, USENIX Technical Conference, San Francisco, CA, enero de 1994.

[3] C. Jones, "Programming Languages Table, Release 8.2", marzo de 1996, http://www.spr.com/library/0langtbl.htm.

[4] M. Lutz, Programming Python, O'Reilly, ISBN 1-56592-197-6, 1996.

[5] Netscape Inc., "JavaScript in Navigator 3.0", http://home.netscape.com/eng/mozilla/3.0/handbook/javascript/atlas.html#taint_dg.

[6] R. O'Hara y D. Gomberg, Modern Programming Using REXX, Prentice Hall, ISBN 0-13-597329-5, 1988.

[7] J. Ousterhout, Additional Information for Scripting White Paper, http://www.ajubasolutions.com/people/john.ousterhout/scriptextra.html.

[8] J. Ousterhout, Tcl and the Tk Toolkit, Addison-Wesley, ISBN 0-201-63337-X, 1994.

[9] L. Wall, T. Christiansen, y R. Schwartz, Programming Perl, Segunda Edición, O'Reilly and Associates, ISBN 1-56592-149-6, 1996.

Sun y Java son marcas comerciales o marcas registradas de Sun Microsystems, Inc. en los Estados Unidos y otros países.



1 Una caracterización más precisa usaría el término "tipado estático" donde digo "tipado fuerte" y "tipado dinámico con conversión automática" para lenguajes de scripting que describo como débilmente tipados o sin tipos. Utilizo el término "tipado" en un sentido general para describir el grado en que el uso de los datos está restringido de antemano.

Tcl DeveloperXChange
Última actualización: 8 de agosto de 2000

Copiado de aquí. También vea scripting.pdf.