Self es un lenguaje de programación orientado a objetos , de alto nivel y de propósito general , basado en el concepto de prototipos . Self comenzó como un dialecto de Smalltalk , con tipado dinámico y compilación justo a tiempo (JIT), utilizando un enfoque basado en prototipos para los objetos. Se utilizó por primera vez como sistema de prueba experimental para el diseño de lenguajes en las décadas de 1980 y 1990. En 2006, Self aún se desarrollaba como parte del proyecto Klein, una máquina virtual de Self escrita completamente en Self. La última versión, 2024.1, se lanzó en agosto de 2024. [ 2 ]
En la investigación de Self se desarrollaron y perfeccionaron diversas técnicas de compilación justo a tiempo, necesarias para que un lenguaje orientado a objetos de muy alto nivel pudiera funcionar hasta a la mitad de la velocidad de C optimizado . Gran parte del desarrollo de Self tuvo lugar en Sun Microsystems , y las técnicas que desarrollaron se implementaron posteriormente en la máquina virtual HotSpot de Java .
En un momento dado, se implementó una versión de Smalltalk en Self. Gracias a su capacidad para usar el JIT, esto también proporcionó un rendimiento extremadamente bueno. [ 3 ]
Historia
Self fue diseñado principalmente por David Ungar y Randall Smith en 1986 mientras trabajaban en Xerox PARC . Su objetivo era impulsar el estado del arte en la investigación de lenguajes de programación orientados a objetos, una vez que Smalltalk -80 fue lanzado por los laboratorios y comenzó a ser tomado en serio por la industria. Se trasladaron a la Universidad de Stanford y continuaron trabajando en el lenguaje, creando el primer compilador funcional de Self en 1987. Posteriormente, se centraron en desarrollar un sistema completo para Self, en lugar de solo el lenguaje.
La primera versión pública se publicó en 1990, y al año siguiente el equipo se trasladó a Sun Microsystems, donde continuaron trabajando en el lenguaje. Le siguieron varias versiones nuevas hasta que quedó prácticamente inactivo en 1995 con la versión 4.0. En 2006, se publicó la versión 4.3 para Mac OS X y Solaris . En 2010, un grupo formado por algunos miembros del equipo original y programadores independientes desarrolló una nueva versión, la 4.4, [ 4 ] para Mac OS X y Linux , al igual que todas las versiones posteriores. En enero de 2014, se publicó una continuación, la 4.5, [ 5 ] y tres años después, en mayo de 2017, se publicó la versión 2017.1.
El entorno de construcción de interfaz de usuario Morphic fue desarrollado originalmente por Randy Smith y John Maloney para el lenguaje de programación Self. [ 6 ] Morphic ha sido portado a otros lenguajes de programación notables, incluidos Squeak , JavaScript , Python y Objective-C .
Self también inspiró varios lenguajes basados en sus conceptos. Quizás los más notables sean NewtonScript para el Apple Newton y JavaScript, utilizado en todos los navegadores modernos. Otros ejemplos incluyen Io , Lisaac y Agora . El sistema de objetos distribuidos del IBM Tivoli Framework, desarrollado en 1990, era, en su nivel más básico, un sistema de objetos basado en prototipos inspirado en Self.
Lenguajes de programación basados en prototipos
Los lenguajes de programación orientada a objetos tradicionales basados en clases se basan en una dualidad profundamente arraigada:
- Las clases definen las cualidades y comportamientos básicos de los objetos.
- Las instancias de objetos son manifestaciones particulares de una clase.
Por ejemplo, supongamos que los objetos de la Vehicleclase tienen un nombre y la capacidad de realizar varias acciones, como conducir al trabajo y entregar materiales de construcción . Bob's cares un objeto particular (instancia) de la clase Vehicle, con el nombre "Coche de Bob". En teoría, se puede enviar un mensaje a Bob's car, indicándole que entregue materiales de construcción .
Este ejemplo muestra uno de los problemas de este enfoque: el coche de Bob, que resulta ser un deportivo, no puede transportar ni entregar materiales de construcción (en ningún sentido significativo), pero esta es una capacidad que Vehiclese supone que tienen los vehículos. Un modelo más útil surge del uso de subclases para crear especializaciones de vehículos Vehicle; por ejemplo Sports Car, y Flatbed Truck. Solo los objetos de la clase Flatbed Trucknecesitan proporcionar un mecanismo para entregar materiales de construcción ; los deportivos, que no son adecuados para ese tipo de trabajo, solo necesitan conducir rápido . Sin embargo, este modelo más profundo requiere una mayor comprensión durante el diseño, una comprensión que puede que solo salga a la luz cuando surjan problemas.
Este problema es uno de los factores que motivan el desarrollo de prototipos . A menos que se pueda predecir con certeza qué cualidades tendrá un conjunto de objetos y clases en el futuro, no se puede diseñar una jerarquía de clases correctamente. Con demasiada frecuencia, el programa acabaría necesitando añadir comportamientos, y sería necesario rediseñar (o refactorizar ) secciones del sistema para separar los objetos de otra manera. La experiencia con lenguajes de programación orientados a objetos como Smalltalk demostró que este tipo de problema se repetía una y otra vez. Los sistemas tendían a crecer hasta cierto punto y luego se volvían muy rígidos, ya que las clases básicas, situadas en lo profundo del código del programador, resultaban ser simplemente erróneas . Sin alguna forma de modificar fácilmente la clase original, podrían surgir graves problemas.
Los lenguajes dinámicos como Smalltalk permitían este tipo de cambios mediante métodos bien conocidos en las clases; al modificar la clase, los objetos que la utilizaban cambiaban su comportamiento. Sin embargo, estos cambios debían realizarse con sumo cuidado, ya que otros objetos basados en la misma clase podrían esperar un comportamiento erróneo : lo erróneo suele depender del contexto. (Esta es una forma del problema de la clase base frágil ). Además, en lenguajes como C++ , donde las subclases se pueden compilar por separado de las superclases, un cambio en una superclase puede, de hecho, romper los métodos de subclase precompilados. (Esta es otra forma del problema de la clase base frágil, y también una forma del problema de la interfaz binaria frágil ).
En Self, y en otros lenguajes basados en prototipos, se elimina la dualidad entre clases e instancias de objetos.
En lugar de tener una instancia de un objeto que se basa en alguna clase , en Self se crea una copia de un objeto existente y se modifica. Así, Bob's carse crearía haciendo una copia de un objeto Vehicle existente y luego agregando el método drive fast , modelando el hecho de que resulta ser un Porsche 911. Los objetos básicos que se usan principalmente para hacer copias se conocen como prototipos . Se afirma que esta técnica simplifica enormemente el dinamismo. Si un objeto existente (o conjunto de objetos) resulta ser un modelo inadecuado, un programador puede simplemente crear un objeto modificado con el comportamiento correcto y usarlo en su lugar. El código que usa los objetos existentes no se modifica.
Descripción
Los objetos self son una colección de slots . Los slots son métodos de acceso que devuelven valores, y colocar dos puntos después del nombre de un slot establece el valor. Por ejemplo, para un slot llamado "name",
mi nombre de personadevuelve el valor en nombre, y
miNombre de persona : 'foo'lo establece.
Al igual que Smalltalk, Self utiliza bloques para el control de flujo y otras funciones. Los métodos son objetos que contienen código, además de ranuras (que utilizan para argumentos y valores temporales), y pueden colocarse en una ranura de Self como cualquier otro objeto: por ejemplo, un número. La sintaxis es la misma en ambos casos.
Cabe destacar que en Self no existe distinción entre campos y métodos: todo es una ranura. Dado que el acceso a las ranuras mediante mensajes constituye la mayor parte de la sintaxis en Self, muchos mensajes se envían a self , y se puede omitir self (de ahí su nombre).
Sintaxis básica
La sintaxis para acceder a las ranuras es similar a la de Smalltalk. Hay tres tipos de mensajes disponibles:
- unario
receiver slot_name- binario
receiver + argument- palabra clave
receiver keyword: arg1 With: arg2
Todos los mensajes devuelven resultados, por lo que el receptor (si lo hay) y los argumentos pueden ser, a su vez, el resultado de otros mensajes. Si un mensaje va seguido de un punto, Self descartará el valor devuelto. Por ejemplo:
Impresión '¡Hola, mundo!' .Esta es la versión Self del programa "¡Hola, mundo!" . La 'sintaxis indica un objeto de cadena literal. Otros literales incluyen números, bloques y objetos generales.
La agrupación se puede forzar mediante paréntesis. En ausencia de agrupación explícita, los mensajes unarios tienen la mayor precedencia, seguidos de los binarios (agrupación de izquierda a derecha) y, por último, las palabras clave. El uso de palabras clave para la asignación generaría paréntesis adicionales cuando las expresiones también contuvieran mensajes de palabras clave. Para evitarlo, Self requiere que la primera parte de un selector de mensajes de palabras clave comience con una letra minúscula, y las partes subsiguientes con una letra mayúscula.
válido: base inferior entre: ligadura inferior + altura Y: base superior / factor de escala .puede analizarse sin ambigüedad y significa lo mismo que:
válido: (( base inferior ) entre: (( ligadura inferior ) + altura ) Y: (( base superior ) / ( factor de escala ))) .En Smalltalk-80, la misma expresión se escribiría como:
válido := base inferior propia entre: ligadura inferior propia + altura propia y: base superior propia / factor de escala propio .suponiendo que base, ligature, heighty scaleno fueran variables de instancia de selfsino que, de hecho, fueran métodos.
Fabricación de nuevos objetos
Consideremos un ejemplo un poco más complejo:
labelWidget copiar etiqueta: '¡Hola, mundo!' .crea una copia del objeto "labelWidget" con el mensaje de copia (esta vez sin atajo), luego le envía un mensaje para colocar "Hello, World" en la ranura llamada "label". Ahora, hagamos algo con él:
( escritorio activeWindow ) dibujar: ( labelWidget copiar etiqueta: '¡Hola, mundo!' ) .En este caso (desktop activeWindow), primero se ejecuta la operación, devolviendo la ventana activa de la lista de ventanas que conoce el objeto de escritorio. A continuación (de adentro hacia afuera, de izquierda a derecha), el código que examinamos anteriormente devuelve el widget de etiqueta. Finalmente, el widget se envía al área de dibujo de la ventana activa.
Delegación
En teoría, cada objeto Self es una entidad independiente. Self no tiene clases ni metaclases. Los cambios en un objeto en particular no afectan a ningún otro, pero en algunos casos es deseable que lo hagan. Normalmente, un objeto solo puede entender mensajes que correspondan a sus ranuras locales, pero al tener una o más ranuras que indiquen objetos padre , un objeto puede delegar cualquier mensaje que no entienda por sí mismo al objeto padre. Cualquier ranura puede convertirse en un puntero padre añadiendo un asterisco como sufijo. De esta forma, Self gestiona tareas que en lenguajes basados en clases usarían herencia . La delegación también puede usarse para implementar características como espacios de nombres y ámbito léxico .
Por ejemplo, supongamos que se define un objeto llamado "cuenta bancaria", que se utiliza en una aplicación de contabilidad sencilla. Normalmente, este objeto se crearía con los métodos internos, por ejemplo, "depositar" y "retirar", y los campos de datos necesarios para ellos. Este es un prototipo, cuya particularidad reside únicamente en su uso, ya que también se trata de una cuenta bancaria completamente funcional.
Rasgos
Al crear un clon de este objeto para la cuenta de Bob, se generará un nuevo objeto idéntico al prototipo. En este caso, hemos copiado las ranuras, incluyendo los métodos y los datos. Sin embargo, una solución más común consiste en crear primero un objeto más sencillo, denominado objeto de rasgos , que contiene los elementos que normalmente se asocian a una clase.
En este ejemplo, el objeto "cuenta bancaria" no tendría los métodos de depósito y retiro, pero tendría como padre un objeto que sí los tuviera. De esta forma, se pueden crear muchas copias del objeto cuenta bancaria, pero aún podemos modificar el comportamiento de todas ellas cambiando las ranuras en ese objeto raíz.
¿En qué se diferencia esto de una clase tradicional? Bueno, consideremos el significado de:
miObjeto padre: algúnOtroObjeto .Este fragmento modifica la clase de myObject en tiempo de ejecución al cambiar el valor asociado con la ranura 'parent*' (el asterisco forma parte del nombre de la ranura, pero no de los mensajes correspondientes). A diferencia de la herencia o el ámbito léxico, el objeto delegado se puede modificar en tiempo de ejecución.
Agregar espacios
Los objetos en Self se pueden modificar para incluir ranuras adicionales. Esto se puede hacer mediante el entorno de programación gráfica o con la primitiva '_AddSlots:'. Esta primitiva tiene la misma sintaxis que un mensaje de palabra clave normal, pero su nombre comienza con un guion bajo. Se recomienda evitar la primitiva _AddSlots, ya que es un vestigio de implementaciones antiguas. Sin embargo, la mostraremos en el siguiente ejemplo porque simplifica el código.
Un ejemplo anterior trataba sobre la refactorización de una clase simple llamada Vehicle para poder diferenciar el comportamiento entre coches y camiones. En Self, esto se lograría con algo como esto:
_ Agregar ranuras: ( | vehículo <- ( | padre * = rasgos clonables| ) | ) .Dado que no se indica el receptor de la primitiva '_AddSlots:', se asume que es el propio objeto . En el caso de expresiones escritas en la línea de comandos, se trata de un objeto llamado "lobby". El argumento de '_AddSlots:' es el objeto cuyos slots se copiarán al receptor. En este caso, es un objeto literal con un único slot. El nombre del slot es 'vehicle' y su valor es otro objeto literal. La <-notación implica un segundo slot llamado 'vehicle:' que se puede usar para cambiar el valor del primer slot.
El =símbolo indica una ranura constante, por lo que no existe un "padre" correspondiente. El objeto literal que constituye el valor inicial de "vehículo" incluye una única ranura para poder interpretar mensajes relacionados con la clonación. Un objeto verdaderamente vacío, indicado como (| |) o, simplemente, como (), no puede recibir ningún mensaje.
vehículo _ Agregar ranuras: ( | nombre <- 'automóvil' | ) .Aquí el receptor es el objeto anterior, que ahora incluirá ranuras 'name' y 'name:' además de 'parent*'.
_ AddSlots: ( | sportsCar <- copia del vehículo | ) . sportsCar _ AddSlots: ( | driveToWork = ( '' algún código, este es un método '' ) | ) .Aunque antes "vehículo" y "coche deportivo" eran idénticos, ahora este último incluye una nueva ranura con un método que el original no tiene. Los métodos solo se pueden incluir en ranuras constantes.
_ AddSlots: ( | porsche911 <- copia de sportsCar | ) . Nombre de Porsche911 : 'Bobs Porsche' .El nuevo objeto 'porsche911' era originalmente igual que 'sportsCar', pero el último mensaje modificó el valor de su ranura 'name'. Cabe destacar que ambos conservan las mismas ranuras, aunque uno de ellos tenga un valor diferente.
Ambiente
Una característica de Self es que se basa en el mismo tipo de sistema de máquina virtual que utilizaban los sistemas Smalltalk anteriores. Es decir, los programas no son entidades independientes como en lenguajes como C , sino que necesitan todo su entorno de memoria para ejecutarse. Esto requiere que las aplicaciones se distribuyan en fragmentos de memoria guardada, conocidos como instantáneas o imágenes . Una desventaja de este enfoque es que las imágenes a veces son grandes y difíciles de manejar; sin embargo, depurar una imagen suele ser más sencillo que depurar programas tradicionales, ya que el estado de ejecución es más fácil de inspeccionar y modificar. (La diferencia entre el desarrollo basado en código fuente y el basado en imágenes es análoga a la diferencia entre la programación orientada a clases y la programación orientada a objetos prototípica).
Además, el entorno está diseñado para adaptarse al cambio rápido y continuo de los objetos del sistema. Refactorizar el diseño de una clase es tan sencillo como arrastrar métodos de sus ancestros existentes a otros nuevos. Tareas simples como la prueba de métodos se pueden realizar copiando el método a la copia y modificándolo. A diferencia de los sistemas tradicionales, solo el objeto modificado contiene el nuevo código, y no es necesario reconstruir nada para probarlo. Si el método funciona, simplemente se puede volver a arrastrar a su ancestro.
Actuación
Las máquinas virtuales propias lograron un rendimiento de aproximadamente la mitad de la velocidad de C optimizado en algunos benchmarks. [ 7 ]
Esto se logró mediante técnicas de compilación justo a tiempo , que fueron desarrolladas y mejoradas en la investigación de Self para conseguir que un lenguaje de alto nivel tuviera este rendimiento.
Recogida de basura
El recolector de basura de Self utiliza la recolección de basura generacional, que segrega los objetos por antigüedad. Mediante el uso del sistema de gestión de memoria para registrar las escrituras de página, se puede mantener una barrera de escritura. Esta técnica ofrece un rendimiento excelente, aunque tras un tiempo de ejecución puede producirse una recolección de basura completa, lo que consume bastante tiempo.
Optimizaciones
El sistema de tiempo de ejecución simplifica selectivamente las estructuras de llamadas. Esto proporciona modestas mejoras de velocidad en sí mismo, pero permite un amplio almacenamiento en caché de la información de tipos y múltiples versiones de código para diferentes tipos de llamadas. Esto elimina la necesidad de realizar muchas búsquedas de métodos y permite la inserción de sentencias condicionales y llamadas codificadas, lo que a menudo proporciona un rendimiento similar al de C sin pérdida de generalidad a nivel de lenguaje, pero en un sistema con recolección de basura completa. [ 8 ]
Véase también
Referencias
- ↑ Ungar, David; Smith, Randall B. (2007). "Self". Actas de la tercera conferencia ACM SIGPLAN sobre la historia de los lenguajes de programación . doi : 10.1145/1238844.1238853 . ISBN 9781595937667. S2CID 220937663 .
- ↑ "Self "Mandarin" 2017.1" . GitHub . 24 de mayo de 2017. Consultado el 1 de noviembre de 2024 .
- ↑ Wolczko, Mario (1996). self includes: Smalltalk . Workshop on Prototype-Based Languages, ECOOP '96. Linz, Austria.
- ↑ "Self 4.4 lanzado" . 16 de julio de 2010. Archivado del original el 5 de diciembre de 2017. Recuperado el 24 de mayo de 2017 .
- ↑ "Self Mallard (4.5.0) lanzado" . 12 de enero de 2014. Archivado del original el 6 de diciembre de 2017. Recuperado el 24 de mayo de 2017 .
- ↑ Maloney, John H.; Smith, Randall B. (1995). «Directividad y vivacidad en el entorno de construcción de interfaces de usuario mórficas» . Actas del 8.º simposio anual de la ACM sobre interfaz de usuario y tecnología de software . págs. 21-28 . doi : 10.1145/215585.215636 . ISBN 089791709X. S2CID 14479674 . Consultado el 24 de marzo de 2020 .
- ↑ Agesen, Ole (marzo de 1997). "Diseño e implementación de Pep, un traductor Java Just-In-Time" . Theory and Practice of Object Systems . 3 (2): 127– 155. doi : 10.1002/(SICI)1096-9942(1997)3:2 < 127::AID-TAPO4 > 3.0.CO ; 2-S . Archivado del original el 24 de noviembre de 2006.
- ↑ Chambers, Craig (13 de marzo de 1992). El diseño e implementación del compilador SELF, un compilador optimizador para lenguajes de programación orientados a objetos (PDF) (tesis doctoral). Universidad de Stanford .
Lecturas adicionales
- Artículos publicados sobre autoconocimiento
- Chambers, C. (1992), El diseño e implementación del autocompilador, un compilador optimizador para lenguajes de programación orientados a objetos , Universidad de Stanford , CiteSeerX 10.1.1.30.1652
- Serie de cuatro artículos titulada "El entorno y el lenguaje de programación Self"
Enlaces externos
- Sitio web oficial
- yo mismo en GitHub
- Antigua página de inicio personal en Sun Microsystems
- Lenguajes de programación
- Lenguajes de programación dinámicos
- Lenguajes de programación de tipado dinámico
- Lenguajes de programación orientados a objetos
- Lenguajes de programación basados en prototipos
- Software que utiliza la licencia BSD.
- Software libre multiplataforma
- Compiladores libres y de código abierto
- Lenguajes de programación creados en 1987
- Software de 1987
