Dylan (lenguaje de programación)
Dylan es un lenguaje de programación multiparadigma que incluye soporte para programación funcional y orientada a objetos (OOP), y es dinámico y reflexivo mientras proporciona un modelo de programación diseñado para soportar la generación de código de máquina eficiente, incluido el control detallado sobre los comportamientos dinámicos y estáticos. Fue creado a principios de la década de 1990 por un grupo liderado por Apple Computer.
Dylan se deriva de Scheme y Common Lisp y agrega 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, envío múltiple, argumentos de palabras clave, introspección de objetos, macros de extensión de sintaxis basadas en patrones y muchas otras funciones avanzadas. Los programas pueden expresar un control detallado sobre el dinamismo, admitiendo programas que ocupan un continuo entre la programación dinámica y estática y apoyando el desarrollo evolutivo (permitiendo la creación rápida de prototipos seguida de optimización y refinamiento incrementales).
El principal objetivo de diseño de Dylan es ser un lenguaje dinámico adecuado para desarrollar software comercial. Dylan intenta abordar posibles problemas de rendimiento introduciendo "natural" limita 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 Lisps; algunas implementaciones de Dylan se construyeron inicialmente dentro de los sistemas Lisp existentes. Sin embargo, Dylan tiene una sintaxis similar a ALGOL en lugar de una sintaxis de prefijo similar a Lisp.
Historia
Dylan fue creado a principios de la década de 1990 por un grupo liderado por Apple Computer. En un momento de su desarrollo, estaba destinado a usarse con la computadora Apple Newton, pero la implementación de Dylan no alcanzó la madurez suficiente a tiempo y, en cambio, Newton usó una combinación de C y NewtonScript desarrollado por Walter Smith. Apple terminó su esfuerzo de desarrollo de Dylan en 1995, aunque hicieron un "lanzamiento de tecnología" versión disponible (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 ahora son de código abierto. La implementación de Harlequin ahora se llama Open Dylan y es mantenida por un grupo de voluntarios, los Dylan Hackers.
El nombre en clave del lenguaje Dylan era Ralph. James Joaquín eligió el nombre de Dylan para "LENGUAJE DINÁMICO."
Sintaxis
Muchas de las funciones de sintaxis de Dylan provienen de su herencia Lisp. Originalmente, Dylan usó una sintaxis de prefijo similar a Lisp, que se basaba en expresiones s. Cuando se completó el diseño del lenguaje, la sintaxis se cambió a una sintaxis similar a ALGOL, con la expectativa de que fuera más familiar para una audiencia más amplia de programadores. La sintaxis fue diseñada por Michael Kahl. Se describe con gran detalle en el Manual de referencia de Dylan.
Sintaxis léxica
Dylan no distingue entre mayúsculas y minúsculas. La sintaxis léxica de Dylan permite el uso de una convención de nomenclatura en la que se utilizan signos de guión (menos) para conectar las partes de identificadores de varias palabras (a veces llamados "lisp-case" o "kebab caso"). Esta convención es común en los lenguajes Lisp.
Además de los caracteres alfanuméricos y los signos de guión-menos, 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. Si hay alguna ambigüedad, se utilizan espacios en blanco.
Código de ejemplo
Una clase simple con varios espacios:
definir clase ▪puntito ()▪ Objeción) ranura punto-x :: Identificado, requiere-init-keyword: x:; ranura punto-y :: Identificado, requiere-init-keyword: Y:;final clase ▪puntito;
Por convención, las clases se nombran con signos de menor que y mayor que usados como paréntesis angulares, p. la clase denominada <point>
en el ejemplo de código.
En clase final <punto>
tanto clase
como <punto>
son opcionales. Esto es cierto para todas las cláusulas end
. Por ejemplo, puede escribir end if
o simplemente end
para terminar una instrucción if
.
Para crear una instancia de <point>
:
hacer()▪puntito, x: 100, Y: 200)
La misma clase, reescrita de la forma más mínima posible:
definir clase ▪puntito ()▪ Objeción) ranura punto-x; ranura punto-y;final;
Las ranuras ahora se escriben como <object>
. Las ranuras deben inicializarse manualmente:
Deja p = hacer()▪puntito);punto-x()p) := 100; // o p.point-x:= 100;punto-y()p) := 200; // o p.point-y:= 200;
Por convención, los nombres constantes comienzan con "$":
definir constante $pi :: Identificado = 3.1415927♪;
Una función factorial:
definir función factorial ()n :: Identificado) = ()¡N! :: Identificado) Caso n . 0 = error()"No se puede tomar factorial del entero negativo: %dn", n); n = 0 = 1; de otra manera = n * factorial()n - 1); finalfinal;
Aquí, n!
y <integer>
son solo identificadores normales.
No hay una declaración de devolución explícita. El resultado de un método o función es la última expresión evaluada. Es un estilo común omitir el punto y coma después de una expresión en la posición de retorno.
Módulos frente a espacio 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, las clases en muchos idiomas definen una unidad indivisible que debe usarse como un todo. Por ejemplo, usar una función de concatenación de String
requiere importar y compilar contra todo String
.
Algunos lenguajes, incluido Dylan, también incluyen un espacio de nombres explícito e independiente o un sistema de módulos que realiza 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 nada que ver específicamente con ninguno de los dos. Una biblioteca define elementos que deben compilarse y manejarse juntos, mientras que un módulo define un espacio de nombres. Las clases pueden colocarse juntas en módulos, o cortarse entre ellos, según lo desee el programador. A menudo, la definición completa de una clase no existe en un solo módulo, sino que se distribuye en varios que se recopilan opcionalmente. Diferentes programas pueden tener diferentes definiciones de la misma clase, incluyendo solo lo que necesitan.
Por ejemplo, considere una biblioteca adicional para admitir expresiones regulares en String
. En algunos idiomas, para que la funcionalidad se incluya en cadenas, la funcionalidad debe agregarse al espacio de nombres String
. Tan pronto como esto ocurra, la clase String
se vuelve más grande y las funciones que no necesitan usar expresiones regulares aún deben "pagar" para ello en mayor tamaño de la biblioteca. Por esta razón, este tipo de complementos normalmente se colocan en sus propios espacios de nombres y objetos. La desventaja de este enfoque es que las nuevas funciones ya no son parte de String
; en cambio, está aislado 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 el punto de vista de OO, se usa algo como myPattern.parseString(myString)
, que efectivamente invierte el orden.
Con Dylan, se pueden definir muchas interfaces para el mismo código, por ejemplo, el método de concatenación de cadenas podría colocarse tanto en la interfaz de cadenas como en la interfaz "concat" interfaz que recopila todas las diferentes funciones de concatenación de varias clases. Esto se usa más comúnmente en bibliotecas matemáticas, donde las funciones tienden a ser aplicables a tipos de objetos muy diferentes.
Un uso más práctico de la construcción de la interfaz es construir versiones públicas y privadas de un módulo, algo que otros lenguajes incluyen como una característica atornillada que invariablemente causa problemas y agrega sintaxis. Bajo Dylan, cada llamada de función se puede colocar simplemente en el directorio "Privado" o "Desarrollo" y recopile 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
Las clases en Dylan describen ranuras
(miembros de datos, campos, ivars, etc.) de objetos de forma similar a la mayoría de los lenguajes OO. Todo el acceso a las tragamonedas es a través de 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 otros lenguajes OO, otros métodos aplicables a la clase a menudo se definen fuera de la clase y, por lo tanto, las definiciones de clase en Dylan generalmente incluyen solo la definición del almacenamiento. Por ejemplo:
definir clase ■window ()Vista previa) ranura Título :: Identificado = "Sin título", init-keyword: Título:; ranura posición :: ▪puntito, requiere-init-keyword: posición:;final clase;
En este ejemplo, la clase "<ventana>
" se define. El <nombre de la clase> la sintaxis es solo una convención, para que los nombres de las clases se destaquen; los corchetes angulares son simplemente parte del nombre de la clase. Por el contrario, en algunos idiomas la convención es poner en mayúscula la primera letra del nombre de la clase o anteponer al nombre una C o T (por ejemplo). <window>
hereda de una única clase, <view>
, y contiene dos ranuras, title
que contiene una cadena para el título de la ventana, y position
sosteniendo un punto X-Y para 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 init-keyword permite al programador especificar el valor inicial de la ranura al instanciar un objeto de la clase.
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 los espacios y métodos se considera protegido
, lo que significa que solo pueden ser utilizados por subclases. Para permitir que el código no relacionado use las instancias de la ventana, deben declararse public
.
En Dylan, este tipo de reglas de visibilidad no se consideran parte del código, sino del módulo/sistema de interfaz. Esto añade una flexibilidad considerable. Por ejemplo, una interfaz utilizada durante el desarrollo inicial podría declarar todo público, mientras que una utilizada en las pruebas y la implementación podría limitar esto. Con C++ o Java, estos cambios requerirían cambios en el código fuente, por lo que la gente no lo hará, mientras que en Dylan este es un concepto totalmente ajeno.
Aunque este ejemplo no lo usa, 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 con ninguna clase específica; los métodos pueden considerarse como existentes fuera de las clases. Al igual que CLOS, Dylan se basa en el envío múltiple (multimétodos), donde el método específico a llamar se elige en función de los tipos de todos sus argumentos. No es necesario conocer el método en el momento de la compilación, entendiendo que la función requerida puede estar disponible o no, según las preferencias del usuario.
Bajo Java, los mismos métodos estarían aislados en una clase específica. Para usar esa funcionalidad, el programador se ve obligado a importar esa clase y referirse a ella explícitamente para llamar al método. Si esa clase no está disponible o es desconocida en el momento de la compilación, la aplicación simplemente no se compilará.
En Dylan, el código está aislado del almacenamiento en funciones. Muchas clases tienen métodos que llaman a sus propias funciones, por lo que se ven y se sienten como la mayoría de los otros lenguajes orientados a objetos. Sin embargo, el código también puede estar ubicado en funciones genéricas, lo que significa que no están asociadas a una clase específica y cualquier persona puede llamarlas de forma nativa. Vincular una función genérica específica a un método en una clase se logra así:
definir método turn-blue ()w :: ■window) w.color := $blue;final método;
Esta definición es similar a las de otros idiomas, y probablemente estaría encapsulada dentro de la clase <window>
. Tenga en cuenta la llamada:= setter, que es azúcar sintáctica para color-setter($blue, w)
.
La utilidad de los métodos genéricos se manifiesta cuando se consideran métodos más "genéricos" ejemplos Por ejemplo, una función común en la mayoría de los idiomas es to-string
, que devuelve alguna forma legible por humanos para el 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 recopilarse en un solo módulo llamado "to-string
", eliminando así este código de la definición de la clase misma. Si un objeto específico no admite un to-string
, podría agregarse fácilmente en el módulo to-string
.
Extensibilidad
Todo este concepto puede parecer muy extraño a algunos lectores. ¿El código para manejar to-string
para una ventana no está definido en <window>
? Esto podría no tener ningún sentido hasta que consideres cómo maneja Dylan la llamada de to-string
. En la mayoría de los idiomas, cuando se compila el programa, to-string
para <window>
se busca 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 tiempo 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. Eso significa que una función para un método específico se puede ubicar en cualquier lugar, no solo en la unidad de tiempo de compilación. Al final, al programador se le da una flexibilidad considerable en términos de dónde colocar su código, recopilándolo a lo largo de las líneas de clase cuando sea apropiado y líneas funcionales donde no lo sea.
La implicación aquí es que un programador puede agregar funcionalidad a clases existentes definiendo funciones en un archivo separado. Por ejemplo, es posible que desee agregar un corrector ortográfico a todos los <string>
, que en C++ o Java requerirían acceso al código fuente de la clase de cadena, y estas clases básicas rara vez se dan en forma de fuente. En Dylan (y otros "lenguajes extensibles"), el método de revisión ortográfica podría agregarse en el módulo spell-check
, definiendo todas las clases en las que se puede aplicar a través de < código>definir método construcción. En este caso, la funcionalidad real podría definirse en una sola función genérica, que toma una cadena y devuelve los errores. Cuando el módulo spell-check
se compila en su programa, todas las cadenas (y otros objetos) obtendrán la funcionalidad añadida.
Dylan de manzana
Apple Dylan es la implementación de Dylan producida por Apple Computer. Fue desarrollado originalmente para el producto Apple Newton.
Contenido relacionado
Disco duro
Jon postel
IBM 8514