Self es un lenguaje de programación orientado a objetos basado en el concepto de prototipos . Self comenzó como un dialecto de Smalltalk , se tipeó dinámicamente y usó la compilación justo a tiempo (JIT), así como el enfoque basado en prototipos para los objetos: se usó por primera vez como un sistema de prueba experimental para el diseño de lenguajes en las décadas de 1980 y 1990. . En 2006, Self todavía se estaba desarrollando como parte del proyecto Klein, que era una máquina virtual Self escrita completamente en Self. La última versión es 2017.1 lanzada en mayo de 2017. [2]
![]() | |
Paradigma | orientado a objetos ( basado en prototipos ) |
---|---|
Diseñada por | David Ungar , Randall Smith |
Desarrollador | David Ungar, Randall Smith, Universidad de Stanford , Sun Microsystems |
Apareció por primera vez | 1987 |
Lanzamiento estable | Mandarin 2017.1 / 24 de mayo de 2017 |
Disciplina de mecanografía | dinámico , fuerte |
Licencia | Licencia similar a BSD |
Sitio web | www |
Implementaciones importantes | |
Uno mismo | |
Influenciado por | |
Smalltalk , APL [1] | |
Influenciado | |
NewtonScript , JavaScript , Io , Agora , Squeak , Lua , Factor , REBOL |
Varias técnicas de compilación just-in-time fueron pioneras y mejoradas en la autoinvestigación, ya que eran necesarias para permitir que un lenguaje orientado a objetos de muy alto nivel funcionara hasta la mitad de la velocidad de C. optimizado Gran parte del desarrollo de Self tuvo lugar en Sun Los microsistemas y las técnicas que desarrollaron se implementaron posteriormente para la máquina virtual HotSpot de Java .
En un momento, se implementó una versión de Smalltalk en Self. Debido a que pudo usar el JIT, esto también dio un rendimiento extremadamente bueno. [3]
Historia
Self fue diseñado principalmente por David Ungar y Randall Smith en 1986 mientras trabajaba 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 mudaron a la Universidad de Stanford y continuaron trabajando en el lenguaje, construyendo el primer compilador de Self en funcionamiento en 1987. En ese momento, el enfoque cambió a intentar crear un sistema completo para Self, en lugar de solo el lenguaje.
El primer lanzamiento público fue en 1990 y al año siguiente el equipo se trasladó a Sun Microsystems, donde continuaron trabajando en el lenguaje. Siguieron varios lanzamientos nuevos hasta que cayeron en gran medida inactivos en 1995 con la versión 4.0. La versión 4.3 se lanzó en 2006 y se ejecutó en Mac OS X y Solaris . Un nuevo lanzamiento en 2010, [4] la versión 4.4, ha sido desarrollada por un grupo que comprende parte del equipo original y programadores independientes y está disponible para Mac OS X y Linux , al igual que todas las versiones siguientes. El seguimiento 4.5 se publicó en enero de 2014, [5] y tres años después, la versión 2017.1 se publicó en mayo de 2017.
Self también inspiró varios lenguajes basados en sus conceptos. Los más notables, quizás, fueron NewtonScript para Apple Newton y JavaScript utilizados en todos los navegadores modernos. Otros ejemplos incluyen Io , Lisaac y Agora . El sistema de objetos distribuidos de IBM Tivoli Framework , desarrollado en 1990, era, en el nivel más bajo, un sistema de objetos basado en prototipos inspirado en Self.
Lenguajes de programación basados en prototipos
Los lenguajes OO 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 objeto son manifestaciones particulares de una clase.
Por ejemplo, supongamos que los objetos de la Vehicle
clase tienen un nombre y la capacidad de realizar varias acciones, como conducir al trabajo y entregar materiales de construcción . Bob's car
es un objeto particular (instancia) de la clase Vehicle
, con el nombre "Bob's car". En teoría, uno puede enviar un mensaje a Bob's car
, diciéndole que entregue materiales de construcción .
Este ejemplo muestra uno de los problemas con este enfoque: el automóvil de Bob, que resulta ser un automóvil deportivo, no puede transportar y entregar materiales de construcción (en un sentido significativo), pero esta es una capacidad para la que Vehicle
están diseñados. Un modelo más útil surge del uso de subclases para crear especializaciones de Vehicle
; por ejemplo Sports Car
y Flatbed Truck
. Solo los objetos de la clase Flatbed Truck
necesitan proporcionar un mecanismo para entregar materiales de construcción ; los coches deportivos, que no se adaptan bien a ese tipo de trabajo, sólo necesitan conducir rápido . Sin embargo, este modelo más profundo requiere más conocimiento durante el diseño, conocimiento que solo puede salir a la luz cuando surgen problemas.
Este problema es uno de los factores motivadores de los prototipos . A menos que se pueda predecir con certeza qué cualidades tendrá un conjunto de objetos y clases en un futuro lejano, no se puede diseñar una jerarquía de clases correctamente. Con demasiada frecuencia, el programa eventualmente necesitaría comportamientos adicionales, y las secciones del sistema necesitarían ser rediseñadas (o refactorizadas ) para dividir los objetos de una manera diferente. [ cita requerida ] La experiencia con los primeros lenguajes OO como Smalltalk mostró que este tipo de problemas surgían una y otra vez. Los sistemas tenderían a crecer hasta cierto punto y luego se volverían muy rígidos, ya que las clases básicas muy por debajo del código del programador se volvieron simplemente "incorrectas". Sin alguna forma de cambiar fácilmente la clase original, podrían surgir problemas graves. [ cita requerida ]
Los lenguajes dinámicos como Smalltalk permitieron este tipo de cambio a través de métodos bien conocidos en las clases; al cambiar la clase, los objetos basados en ella cambiarían su comportamiento. Sin embargo, tales cambios debían realizarse con mucho cuidado, ya que otros objetos basados en la misma clase podrían estar esperando este comportamiento "incorrecto": "incorrecto" a menudo depende del contexto. (Esta es una forma del frágil problema de la clase base ). Además, en lenguajes como C ++ , donde las subclases se pueden compilar por separado de las superclases, un cambio a una superclase puede romper los métodos de subclase precompilados. (Esta es otra forma del frágil problema de la clase base, y también una forma del frágil problema de la interfaz binaria ).
En Self y 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 uno hace una copia de un objeto existente y lo cambia. Así Bob's car
se crearía haciendo una copia de un objeto "Vehículo" existente y luego agregando el método de conducción rápida , modelando el hecho de que resulta ser un Porsche 911 . Los objetos básicos que se utilizan 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 utiliza los objetos existentes no se modifica.
Descripción
Los objetos del self son una colección de "ranuras". Las ranuras son métodos de acceso que devuelven valores, y colocar dos puntos después del nombre de una ranura establece el valor. Por ejemplo, para un espacio llamado "nombre",
myPerson nombre
devuelve el valor en nombre, y
myPerson name: 'foo'
lo establece.
Self, como Smalltalk, usa bloques para el control de flujo y otras tareas. Los métodos son objetos que contienen código además de ranuras (que utilizan para argumentos y valores temporales), y se pueden colocar en una ranura Self como cualquier otro objeto: un número, por ejemplo. La sintaxis sigue siendo la misma en ambos casos.
Tenga en cuenta que no hay distinción en Self entre campos y métodos: todo es una ranura. Dado que acceder a las ranuras a través de mensajes forma la mayor parte de la sintaxis en Self, muchos mensajes se envían a "self" y el "self" se puede omitir (de ahí el 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 está presente) y los argumentos pueden ser ellos mismos el resultado de otros mensajes. Seguir un mensaje con un punto significa que Self descartará el valor devuelto. Por ejemplo:
'¡Hola Mundo!' imprimir .
Esta es la versión Self del programa hello world . La '
sintaxis indica un objeto de cadena literal. Otros literales incluyen números, bloques y objetos generales.
La agrupación se puede forzar utilizando paréntesis. En ausencia de una agrupación explícita, se considera que los mensajes unarios tienen la prioridad más alta, seguidos de binarios (agrupando de izquierda a derecha) y las palabras clave que tienen la más baja. El uso de palabras clave para la asignación daría lugar a algunos paréntesis adicionales donde las expresiones también tienen mensajes de palabras clave, por lo que para evitar que Self requiere que la primera parte de un selector de mensajes de palabras clave comience con una letra minúscula y las partes posteriores comiencen con una letra mayúscula.
válido: base inferior entre: ligadura inferior + altura Y: base superior / factor de escala .
se puede analizar sin ambigüedades 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 vería escrita como:
válido : = auto de base inferior entre: auto ligadura parte inferior + auto altura y: auto base de la parte superior / auto escala factor de .
suponiendo base
, ligature
, height
y scale
no eran variables de instancia de self
pero eran, de hecho, los métodos.
Haciendo nuevos objetos
Considere un ejemplo un poco más complejo:
labelWidget copy label: '¡Hola, mundo!' .
hace una copia del objeto "labelWidget" con el mensaje de copia (sin acceso directo esta vez), luego le envía un mensaje para poner "Hello, World" en el espacio llamado "label". Ahora para hacer algo con él:
( ventana activa de escritorio ) dibujar: ( labelWidget copiar etiqueta: '¡Hola, mundo!' ) .
En este caso, (desktop activeWindow)
se realiza primero, devolviendo la ventana activa de la lista de ventanas que conoce el objeto de escritorio. A continuación (lea de interior a exterior, de izquierda a derecha) el código que examinamos antes devuelve el labelWidget. Finalmente, el widget se envía al espacio de dibujo de la ventana activa.
Delegación
En teoría, cada objeto del Ser es una entidad independiente. El yo 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 hicieran. Normalmente, un objeto solo puede comprender los mensajes correspondientes a sus espacios locales, pero al tener uno o más espacios que indiquen objetos principales , un objeto puede delegar cualquier mensaje que no comprenda por sí mismo al objeto principal. Cualquier ranura se puede convertir en un puntero principal agregando un asterisco como sufijo. De esta manera, Self maneja las tareas que usarían la herencia en los lenguajes basados en clases. La delegación también se puede utilizar para implementar características como espacios de nombres y alcance léxico .
Por ejemplo, supongamos que se define un objeto llamado "cuenta bancaria", que se utiliza en una aplicación simple de contabilidad. Por lo general, este objeto se crearía con los métodos dentro, tal vez "depositar" y "retirar", y cualquier espacio de datos que necesiten. Este es un prototipo, que solo es especial en la forma en que se usa, ya que también es una cuenta bancaria completamente funcional.
Rasgos
Hacer un clon de este objeto para la "cuenta de Bob" creará un nuevo objeto que comienza exactamente como el prototipo. En este caso, hemos copiado las ranuras, incluidos los métodos y cualquier dato. Sin embargo, una solución más común es hacer primero un objeto más simple llamado objeto de rasgos que contenga los elementos que normalmente se asociarían con una clase.
En este ejemplo, el objeto "cuenta bancaria" no tendría el método de depósito y retiro, pero tendría como padre un objeto que sí lo tuviera. De esta manera se pueden hacer muchas copias del objeto de la cuenta bancaria, pero aún podemos cambiar el comportamiento de todas cambiando las ranuras en ese objeto raíz.
¿En qué se diferencia esto de una clase tradicional? Bien, considere el significado de:
myObject padres: someOtherObject .
Este extracto cambia la "clase" de myObject en tiempo de ejecución cambiando el valor asociado con la ranura 'padre *' (el asterisco es parte del nombre de la ranura, pero no 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 ranuras
Los objetos en Self se pueden modificar para incluir espacios adicionales. Esto se puede hacer usando el entorno de programación gráfica o con la primitiva '_AddSlots:'. Una primitiva tiene la misma sintaxis que un mensaje de palabra clave normal, pero su nombre comienza con el carácter de subrayado. La primitiva _AddSlots debe evitarse porque es un remanente de implementaciones tempranas. Sin embargo, lo mostraremos en el siguiente ejemplo porque hace que el código sea más corto.
Un ejemplo anterior trataba sobre la refactorización de una clase simple llamada Vehículo para poder diferenciar el comportamiento entre automóviles y camiones. En uno mismo, uno lograría esto con algo como esto:
_ AddSlots: ( | vehículo <- ( | padre * = rasgos clonables | ) | ) .
Dado que el receptor de la primitiva '_AddSlots:' no está indicado, es "self". En el caso de expresiones escritas en el indicador, ese es un objeto llamado "lobby". El argumento de '_AddSlots:' es el objeto cuyas ranuras se copiarán al receptor. En este caso, es un objeto literal con exactamente una ranura. El nombre de la ranura es 'vehículo' y su valor es otro objeto literal. La notación "<-" implica una segunda ranura llamada 'vehículo:' que se puede usar para cambiar el valor de la primera ranura.
El "=" indica una ranura constante, por lo que no hay un 'padre:' correspondiente. El objeto literal que es el valor inicial de 'vehículo' incluye una única ranura para que pueda comprender los mensajes relacionados con la clonación. Un objeto verdaderamente vacío, indicado como (| |) o más simplemente como (), no puede recibir ningún mensaje en absoluto.
vehículo _ AddSlots: ( | nombre <- 'automóvil' | ) .
Aquí, el receptor es el objeto anterior, que ahora incluirá ranuras de 'nombre' y 'nombre:' además de 'padre *'.
_ AddSlots: ( | sportsCar <- copia del vehículo | ) . SPORTSCAR _ AddSlots: ( | driveToWork = ( '' un poco de código, este es un método '' ) | ) .
Aunque anteriormente 'vehículo' y 'sportsCar' eran exactamente iguales, 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 <- SPORTSCAR copia | ) . Nombre de porsche911 : 'Bobs Porsche' .
El nuevo objeto 'porsche911' comenzó exactamente como 'sportsCar', pero el último mensaje cambió el valor de su ranura de 'nombre'. Tenga en cuenta que ambos siguen teniendo exactamente los mismos espacios aunque uno de ellos tiene un valor diferente.
Ambiente
Una característica de Self es que se basa en el mismo tipo de sistema de máquina virtual que usaban los sistemas Smalltalk anteriores. Es decir, los programas no son entidades independientes como lo son en lenguajes como C , sino que necesitan todo su entorno de memoria para ejecutarse. Esto requiere que las aplicaciones se envíen en trozos 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 porque el estado de tiempo de ejecución es más fácil de inspeccionar y modificar. (La diferencia entre el desarrollo basado en fuentes y basado en imágenes es análoga a la diferencia entre la programación basada en clases y la programación orientada a objetos prototípica).
Además, el entorno se adapta al cambio rápido y continuo de los objetos del sistema. Refactorizar un diseño de "clase" es tan simple como arrastrar métodos de los antepasados existentes a otros nuevos. Las tareas simples como los métodos de prueba se pueden manejar haciendo una copia, arrastrando el método a la copia y luego cambiándolo. A diferencia de los sistemas tradicionales, solo el objeto modificado tiene el nuevo código y no es necesario reconstruir nada para probarlo. Si el método funciona, simplemente se puede arrastrar de nuevo al antepasado.
Actuación
Las máquinas virtuales propias lograron un rendimiento de aproximadamente la mitad de la velocidad de C optimizado en algunas pruebas. [6]
Esto se logró mediante técnicas de compilación justo a tiempo que fueron pioneras y mejoradas en la autoinvestigación para hacer que un lenguaje de alto nivel funcione bien.
Recolección de basura
El recolector de basura para Self utiliza la recolección de basura generacional que segrega los objetos por edad. Al utilizar el 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 después de ejecutarse durante algún tiempo puede producirse una recolección de basura completa, lo que lleva un tiempo considerable. [ vago ]
Optimizaciones
El sistema de tiempo de ejecución aplana selectivamente las estructuras de llamadas. Esto proporciona aumentos de velocidad modestos en sí mismo, pero permite un almacenamiento en caché extenso de información de tipo 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 insertar declaraciones de rama 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 de recolección de basura completa. [7]
Ver también
- Cecil (lenguaje de programación)
Referencias
- ^ "Yo". Actas de la tercera conferencia ACM SIGPLAN sobre Historia de los lenguajes de programación (HOPL III) . doi : 10.1145 / 1238844.1238853 .
- ^ "Auto" mandarín "2017.1" . 24 de mayo de 2017. Archivado desde el original el 24 de mayo de 2017 . Consultado el 24 de mayo de 2017 .
- ^ Wolczko, Mario (1996). "yo incluye: Smalltalk". Taller sobre lenguajes basados en prototipos, ECOOP '96, Linz, Austria .
- ^ "Self 4.4 lanzado" . 16 de julio de 2010. Archivado desde el original el 5 de diciembre de 2017 . Consultado el 24 de mayo de 2017 .
- ^ "Self Mallard (4.5.0) lanzado" . 12 de enero de 2014. Archivado desde el original el 6 de diciembre de 2017 . Consultado el 24 de mayo de 2017 .
- ^ Agesen, Ole (marzo de 1997). "Diseño e implementación de Pep, un traductor Just-In-Time de Java" . sun.com . Archivado desde el original el 24 de noviembre de 2006.
- ^ [1] [ enlace muerto ]
Otras lecturas
- Artículos publicados sobre uno mismo
- Chambers, C. (1992), The Design and Implementation of the SELF Compiler, an Optimizing Compiler for Object-Oriented Programming Languages , Stanford University , CiteSeerX 10.1.1.30.1652
- Serie de cuatro artículos "El entorno y el lenguaje de programación Self"
enlaces externos
- Página web oficial
- self en GitHub
- Antigua página de inicio de Self en Sun Microsystems
- Fuente alternativa de artículos sobre Self de UCSB (espejo de la página de artículos de Sun)
- Proyecto Merlin
- Autoportado a Linux (sin muchas optimizaciones)
- Aplicación de refactorización automatizada en sourceforge.net, escrita para y en uno mismo
- La página de Gordon sobre uno mismo
- Sistema de objetos Prometheus en la Wiki de Community Scheme
- Video demostrándose a sí mismo
- dSelf: extensión distribuida a la delegación y lenguaje Self