Articulo de referencia

Dylan (lenguaje de programación)

{{cite web |last1=Stokel-Walker |first1=Chris |title=Julia: The Goldilocks language |url=https://increment.com/programming-languages/goldilocks-language-history-of-julia/ |websi...

Dylan es un lenguaje de programación multiparadigma que incluye soporte para programación funcional y orientada a objetos (POO). Es dinámico y reflexivo , a la vez que proporciona un modelo de programación diseñado para generar código máquina eficiente , con un control preciso sobre comportamientos dinámicos y estáticos. Fue creado a principios de la década de 1990 por un grupo liderado por Apple Computer .

Dylan deriva de Scheme y Common Lisp , y añade un sistema de objetos integrado derivado del Common Lisp Object System (CLOS). En Dylan, todos los valores (incluidos números, caracteres, funciones y clases ) son objetos de primera clase . Dylan admite herencia múltiple , polimorfismo , despacho múltiple , argumentos con palabras clave , introspección de objetos, macros de extensión de sintaxis basadas en patrones y muchas otras características avanzadas. Los programas pueden expresar un control preciso sobre el dinamismo, admitiendo programas que se sitúan en un continuo entre la programación dinámica y estática, y apoyando el desarrollo evolutivo (lo que permite la creación rápida de prototipos seguida de un refinamiento y optimización incrementales).

El principal objetivo de diseño de Dylan es ser un lenguaje dinámico idóneo para el desarrollo de software comercial . Dylan intenta solucionar posibles problemas de rendimiento introduciendo límites "naturales" a la flexibilidad total de los sistemas Lisp , lo que permite al compilador comprender claramente las unidades compilables, como las bibliotecas .

Dylan deriva gran parte de su semántica de Scheme y otros lenguajes Lisp; algunas implementaciones de Dylan se crearon inicialmente dentro de sistemas Lisp ya existentes. Sin embargo, Dylan tiene una sintaxis similar a la de ALGOL en lugar de una sintaxis de prefijos similar a la de Lisp.

Historia

Dylan fue creado a principios de la década de 1990 por un grupo liderado por Apple Computer . En un principio de su desarrollo, se concibió para usarse con la computadora Apple Newton , pero la implementación de Dylan no alcanzó la madurez suficiente a tiempo, y Newton terminó utilizando una combinación de C y NewtonScript, desarrollado por Walter Smith. Apple finalizó el desarrollo de Dylan en 1995, aunque lanzó una versión de prueba (Apple Dylan TR1) que incluía un entorno de desarrollo integrado (IDE) avanzado.

Otros dos grupos contribuyeron al diseño del lenguaje y desarrollaron implementaciones: Harlequin lanzó un IDE comercial para Microsoft Windows y la Universidad Carnegie Mellon lanzó un compilador de código abierto para sistemas Unix llamado Gwydion Dylan. Ambas implementaciones son ahora de código abierto. La implementación de Harlequin se llama ahora Open Dylan y la mantiene un grupo de voluntarios, los Dylan Hackers.

El idioma de Dylan se denominó en clave Ralph. James Joaquín eligió el nombre Dylan por "Lenguaje Dinámico".

Sintaxis

Muchas de las características sintácticas de Dylan provienen de su herencia Lisp. Originalmente, Dylan utilizaba una sintaxis de prefijos similar a la de Lisp, basada en expresiones s . Para cuando se completó el diseño del lenguaje, la sintaxis se modificó a una similar a la de ALGOL, con la expectativa de que resultara más familiar para un público más amplio de programadores. La sintaxis fue diseñada por Michael Kahl y se describe con gran detalle en el Manual de Referencia de Dylan. [ 2 ]

Sintaxis léxica

Dylan no distingue entre mayúsculas y minúsculas . Su sintaxis léxica permite el uso de una convención de nomenclatura donde se utilizan guiones (signos menos) para conectar las partes de identificadores de varias palabras (a veces llamada " lisp-case " o " kebab case "). Esta convención es común en los lenguajes Lisp.

Además de caracteres alfanuméricos y guiones, Dylan permite una variedad de caracteres no alfanuméricos como parte de los identificadores. Los identificadores no pueden consistir únicamente en estos caracteres no alfanuméricos. [ 2 ] Si existe alguna ambigüedad, se utiliza un espacio en blanco.

Código de ejemplo

Una clase simple con varias ranuras:

define class <point> ( <object> ) slot point-x :: <integer> , required-init-keyword: x: ; slot point-y :: <integer> , required-init-keyword: y: ; end class <point> ;

Por convención, las clases se nombran utilizando los signos de menor que y mayor que como corchetes angulares , por ejemplo, la clase nombrada en el ejemplo de código.<point>

En ambos casos , y son opcionales. Esto es cierto para todas las cláusulas. Por ejemplo, se puede escribir o simplemente para finalizar una instrucción.end class <point>class<point>endend ifendif

Para crear una instancia de :<point>

crear ( <punto> , x: 100 , y: 200 )

La misma clase, reescrita de la forma más minimalista posible:

define class <punto> ( <objeto> ) slot punto-x ; slot punto-y ; fin ;

Ahora ambas ranuras están tipadas como . Las ranuras deben inicializarse manualmente:<object>

let p = make ( <punto> ); punto-x ( p ) := 100 ; // o p.punto-x := 100; punto-y ( p ) := 200 ; // o p.punto-y := 200;

Por convención, los nombres de las constantes comienzan con "$":

define constant $pi :: <double-float> = 3.1415927 d0 ;

Una función factorial:

define function factorial ( n :: <entero> ) => ( n! :: <entero> ) case n < 0 => error ( "No se puede calcular el factorial de un entero negativo: %d \n " , n ); n = 0 => 1 ; otherwise => n * factorial ( n - 1 ); end end ;

Aquí, n!y son simplemente identificadores normales.<integer>

No existe una instrucción de retorno explícita . El resultado de un método o función es la última expresión evaluada. Es habitual omitir el punto y coma después de una expresión en posición de retorno.

Módulos frente a espacios de nombres

En muchos lenguajes orientados a objetos, las clases son el principal medio de encapsulación y modularidad; cada clase define un espacio de nombres y controla qué definiciones son visibles externamente. Además, en muchos lenguajes, las clases definen una unidad indivisible que debe usarse como un todo. Por ejemplo, usar una Stringfunción de concatenación requiere importar y compilar con todas las clases String.

Algunos lenguajes, incluido Dylan, también incluyen un sistema de módulos o espacios de nombres separados y explícitos que realizan la encapsulación de una manera más general.

En Dylan, los conceptos de unidad de compilación y unidad de importación están separados, y las clases no tienen relación directa con ninguno de ellos. Una biblioteca define elementos que deben compilarse y gestionarse conjuntamente, mientras que un módulo define un espacio de nombres. Las clases pueden ubicarse juntas en módulos o abarcar varios, según lo desee el programador. A menudo, la definición completa de una clase no reside en un solo módulo, sino que se encuentra distribuida en varios que, opcionalmente, se agrupan. Diferentes programas pueden tener definiciones distintas de la misma clase, incluyendo solo lo que necesitan.

Por ejemplo, consideremos una biblioteca complementaria para el soporte de expresiones regulares en String. En algunos lenguajes, para que la funcionalidad se incluya en cadenas, debe agregarse al Stringespacio de nombres. Tan pronto como esto ocurre, la Stringclase se hace más grande, y las funciones que no necesitan usar expresiones regulares aún deben "pagar" por ello en un mayor tamaño de la biblioteca. Por esta razón, este tipo de complementos generalmente se colocan en sus propios espacios de nombres y objetos. La desventaja de este enfoque es que las nuevas funciones ya no son parte deString ; en cambio, están aisladas en su propio conjunto de funciones que deben llamarse por separado. En lugar de myString.parseWith(myPattern), que sería la organización natural desde un punto de vista de POO, myPattern.parseString(myString)se usa algo como , que efectivamente invierte el orden.

Con Dylan, se pueden definir varias interfaces para el mismo código; por ejemplo, el método de concatenación de cadenas podría ubicarse tanto en la interfaz String como en la interfaz "concat", que agrupa todas las funciones de concatenación de diversas clases. Esto se usa con mayor frecuencia en bibliotecas matemáticas, donde las funciones suelen aplicarse a tipos de objetos muy diferentes.

Un uso más práctico de la estructura de interfaz es crear versiones públicas y privadas de un módulo, algo que otros lenguajes incluyen como una característica añadida que invariablemente causa problemas y agrega sintaxis. En Dylan, cada llamada a función se puede colocar simplemente en la interfaz "Privada" o "Desarrollo", y agrupar las funciones de acceso público en Public. En Java o C++ , la visibilidad de un objeto se define en el código, lo que significa que para admitir un cambio similar, un programador se vería obligado a reescribir las definiciones por completo y no podría tener dos versiones al mismo tiempo.

Clases

En Dylan, las clases describen slotsobjetos (miembros de datos, campos, variables de instancia, etc.) de forma similar a la mayoría de los lenguajes orientados a objetos. El acceso a las ranuras se realiza mediante métodos, como en Smalltalk . Los métodos getter y setter predeterminados se generan automáticamente en función de los nombres de las ranuras. A diferencia de la mayoría de los demás lenguajes orientados a objetos, otros métodos aplicables a la clase suelen definirse fuera de ella, por lo que las definiciones de clase en Dylan normalmente solo incluyen la definición del almacenamiento. Por ejemplo:

define class <window> ( <view> ) slot title :: <string> = "untitled" , init-keyword: title: ; slot position :: <point> , required-init-keyword: position: ; end class ;

En este ejemplo, se define la clase " ". La sintaxis < nombre de clase > es solo una convención para resaltar los nombres de las clases; los corchetes angulares son simplemente parte del nombre de la clase. En contraste, en algunos lenguajes la convención es poner en mayúscula la primera letra del nombre de la clase o anteponerle una C o una T (por ejemplo). hereda de una sola clase, , y contiene dos ranuras, una para el título de la ventana y otra para un punto XY de una esquina de la ventana. En este ejemplo, al título se le ha dado un valor predeterminado, mientras que a la posición no. La sintaxis opcional de palabra clave init permite al programador especificar el valor inicial de la ranura al instanciar un objeto de la clase.<window><window><view>titleposition

En lenguajes como C++ o Java, la clase también definiría su interfaz. En este caso, la definición anterior no tiene instrucciones explícitas, por lo que en ambos lenguajes el acceso a las ranuras y métodos se considera protected, lo que significa que solo pueden ser utilizados por subclases. Para permitir que código no relacionado utilice las instancias de ventana, deben declararse public.

En Dylan, este tipo de reglas de visibilidad no se consideran parte del código, sino del sistema de módulos/interfaces. Esto aporta una flexibilidad considerable. Por ejemplo, una interfaz utilizada durante el desarrollo inicial podría declarar todo como público, mientras que otra utilizada en pruebas y despliegue podría limitar esta visibilidad. En C++ o Java, estos cambios requerirían modificaciones en el código fuente, por lo que no se suelen realizar; en cambio, en Dylan este concepto es totalmente independiente.

Aunque este ejemplo no lo utiliza, Dylan también admite la herencia múltiple .

Métodos y funciones genéricas

En Dylan, los métodos no están intrínsecamente asociados a ninguna clase específica; se puede considerar que existen independientemente de las clases. Al igual que CLOS, Dylan se basa en el despacho múltiple (multimétodos), donde el método específico que se va a llamar se elige en función de los tipos de todos sus argumentos. No es necesario conocer el método en tiempo de compilación, ya que se entiende que la función requerida puede estar disponible o no, según las preferencias del usuario.

En Java, los mismos métodos estarían aislados en una clase específica. Para usar esa funcionalidad, el programador se ve obligado a importar dicha clase y referenciarla explícitamente para llamar al método. Si esa clase no está disponible o se desconoce en tiempo de compilación, la aplicación simplemente no compilará.

En Dylan, el código se aísla del almacenamiento en funciones . Muchas clases tienen métodos que llaman a sus propias funciones, por lo que su apariencia y funcionamiento son similares a los de la mayoría de los demás lenguajes orientados a objetos. Sin embargo, el código también puede ubicarse en funciones genéricas , lo que significa que no está asociado a una clase específica y puede ser llamado de forma nativa por cualquier usuario. La vinculación de una función genérica específica a un método en una clase se realiza de la siguiente manera:

define method turn-blue ( w :: <window> ) w . color := $blue ; end method ;

Esta definición es similar a las de otros lenguajes y probablemente estaría encapsulada dentro de la clase. Nótese la llamada al setter :=, que es azúcar sintáctico para .<window> color-setter($blue, w)

La utilidad de los métodos genéricos se hace evidente al considerar ejemplos más "genéricos". Por ejemplo, una función común en la mayoría de los lenguajes es `window` to-string, que devuelve una representación legible del objeto. Por ejemplo, una ventana podría devolver su título y su posición entre paréntesis, mientras que una cadena se devolvería a sí misma. En Dylan, todos estos métodos podrían agruparse en un único módulo llamado ` to-stringwindow`, eliminando así este código de la definición de la clase. Si un objeto específico no admitiera `window` to-string, podría agregarse fácilmente en el to-stringmódulo.

Extensibilidad

Todo este concepto podría resultar muy extraño para algunos lectores. ¿El código para manejar to-stringuna ventana no está definido en ? Esto podría no tener sentido hasta que se considere cómo Dylan maneja la llamada de . En la mayoría de los lenguajes, cuando se compila el programa, se busca el para y se reemplaza con un puntero (más o menos) al método. En Dylan, esto ocurre cuando el programa se ejecuta por primera vez; el entorno de ejecución crea una tabla de detalles de nombre de método/parámetros y busca métodos dinámicamente a través de esta tabla. Esto significa que una función para un método específico puede estar ubicada en cualquier lugar, no solo en la unidad de tiempo de compilación. Al final, el programador tiene una flexibilidad considerable en cuanto a dónde colocar su código, agrupándolo en líneas de clase cuando sea apropiado y en líneas funcionales cuando no lo sea.<window>to-stringto-string<window>

Esto implica que un programador puede añadir funcionalidad a clases existentes definiendo funciones en un archivo aparte. Por ejemplo, se podría añadir corrección ortográfica a todas las cadenas, lo que en C++ o Java requeriría acceso al código fuente de la clase `string` , y estas clases básicas rara vez se distribuyen en formato de código fuente. En Dylan (y otros lenguajes extensibles), el método de corrección ortográfica podría añadirse en el módulo, definiendo todas las clases a las que se puede aplicar mediante la construcción `function`. En este caso, la funcionalidad real podría definirse en una única función genérica que recibe una cadena y devuelve los errores. Cuando el módulo se compila en un programa, todas las cadenas (y otros objetos) obtendrán la funcionalidad añadida.<string>spell-checkdefine methodspell-check

Apple Dylan

Apple Dylan es la implementación de Dylan producida por Apple Computer . Fue desarrollada originalmente para el producto Apple Newton .

Referencias

  1. Stokel-Walker, Chris. "Julia: El lenguaje de Ricitos de Oro" . Increment . Stripe . Consultado el 23 de agosto de 2020 .
  2. 1 2 Andrew Shalit; David Moon; Orca Starbuck (11 de septiembre de 1996). The Dylan Reference Manual . Apple Press. Addison-Wesley . ISBN 9780201442113.
  • Sitio web oficial , Open Dylan: aloja el compilador Dylan de código abierto y optimizado para Unix/Linux, macOS y Microsoft Windows.
  • Descripción general del idioma
  • Una introducción a Dylan
  • Apple Dylan TR1
  • El intérprete de Marlais Dylan : una implementación de un subconjunto de Dylan, adecuada para el arranque de un compilador.