Eiffel es un lenguaje de programación orientado a objetos diseñado por Bertrand Meyer (defensor de la orientación a objetos y autor de *Object-Oriented Software Construction *) y Eiffel Software. Meyer concibió el lenguaje en 1985 con el objetivo de aumentar la fiabilidad del desarrollo de software comercial. [ 4 ] La primera versión se publicó en 1986. En 2005, la Organización Internacional de Normalización (ISO) publicó una norma técnica para Eiffel.
El diseño del lenguaje está estrechamente relacionado con el método de programación Eiffel. Ambos se basan en un conjunto de principios, entre los que se incluyen el diseño por contrato , la separación entre comandos y consultas , el principio de acceso uniforme , el principio de elección única , el principio de abierto-cerrado y la separación entre opciones y operandos .
Muchos conceptos introducidos inicialmente por Eiffel se añadieron posteriormente a Java , C# y otros lenguajes. [ 5 ] Nuevas ideas de diseño de lenguajes, en particular a través del proceso de estandarización Ecma / ISO , siguen incorporándose al lenguaje Eiffel.
Características
Las características clave del idioma Eiffel incluyen:
- Estructura de programa orientada a objetos en la que una clase sirve como unidad básica de descomposición. [ 6 ]
- Diseño por contrato estrechamente integrado con otras construcciones del lenguaje.
- Gestión automática de memoria, generalmente implementada mediante recolección de basura . [ 7 ]
- La herencia , incluyendo la herencia múltiple , el cambio de nombre , la redefinición , la "selección", la herencia no conforme y otros mecanismos destinados a hacer que la herencia sea segura.
- Programación genérica con y sin restricciones [ 8 ]
- Un sistema de tipos uniforme que maneja tanto la semántica de valor como la de referencia, en el que todos los tipos, incluidos los tipos básicos como INTEGER, están basados en clases.
- Tipado estático
- Seguridad contra valores nulos , o protección estática contra llamadas a referencias nulas, mediante el mecanismo de tipos adjuntos.
- Los agentes, u objetos que encapsulan cálculos, están estrechamente relacionados con los cierres y el cálculo lambda .
- Rutinas de una sola vez , o rutinas evaluadas solo una vez, para compartir objetos e inicialización descentralizada.
- Sintaxis basada en palabras clave, al estilo de ALGOL / Pascal , pero sin separadores, ya que los puntos y comas son opcionales, con sintaxis de operadores disponible para las rutinas.
- Insensibilidad a mayúsculas y minúsculas
- La programación orientada a objetos concurrente simple ( SCOOP ) facilita la creación de múltiples vehículos de ejecución activos simultáneamente a un nivel de abstracción superior a los detalles específicos de estos vehículos (por ejemplo, múltiples subprocesos sin una gestión específica de mutex).
Objetivos de diseño
Eiffel hace hincapié en las declaraciones sobre el código procedimental e intenta eliminar la necesidad de instrucciones contables.
Eiffel evita los trucos o técnicas de codificación que pretenden ser sugerencias de optimización para el compilador . Su objetivo es hacer el código más legible y facilitar la concentración en los aspectos importantes del programa sin perderse en detalles de implementación. La simplicidad de Eiffel busca promover soluciones sencillas, extensibles, reutilizables y fiables a los problemas informáticos. Los compiladores para programas escritos en Eiffel ofrecen técnicas de optimización avanzadas, como la expansión automática en línea (inlining), que eliminan parte de la carga de optimización.
Fondo
Eiffel fue desarrollado originalmente por Eiffel Software, una empresa fundada por Bertrand Meyer . El libro "Construcción de software orientado a objetos" contiene un análisis detallado de los conceptos y la teoría de la tecnología de objetos que condujo al diseño de Eiffel. [ 9 ]
El objetivo de diseño del lenguaje, las bibliotecas y los métodos de programación Eiffel es permitir a los programadores crear módulos de software fiables y reutilizables. Eiffel admite herencia múltiple , genericidad , polimorfismo , encapsulación , conversiones con seguridad de tipos y covarianza de parámetros . La contribución más importante de Eiffel a la ingeniería de software es el diseño por contrato (DbC), en el que se emplean aserciones , precondiciones , postcondiciones e invariantes de clase para garantizar la corrección del programa sin sacrificar la eficiencia.
El diseño de Eiffel se basa en la teoría de la programación orientada a objetos, con escasa influencia de otros paradigmas de programación y poca atención a la compatibilidad con código heredado. Eiffel admite formalmente tipos de datos abstractos . Según el diseño de Eiffel, un texto de software debería poder reproducir su documentación de diseño a partir del propio texto, utilizando una implementación formalizada del "Tipo de Dato Abstracto".
Implementaciones y entornos
EiffelStudio es un entorno de desarrollo integrado disponible bajo una licencia de código abierto o comercial. Ofrece un entorno orientado a objetos para la ingeniería de software , compilando a C o .NET. EiffelEnvision es un complemento para Microsoft Visual Studio que permite a los usuarios editar, compilar y depurar proyectos Eiffel desde el IDE de Microsoft Visual Studio. Hay otras cinco implementaciones de código abierto disponibles: "The Eiffel Compiler" tecomp; Gobo Eiffel; SmartEiffel , la implementación GNU, basada en una versión anterior del lenguaje; LibertyEiffel , basada en el compilador SmartEiffel; y Visual Eiffel .
Otros lenguajes de programación incorporan elementos introducidos originalmente en Eiffel. Sather , por ejemplo, se basó inicialmente en Eiffel, pero desde entonces ha evolucionado y ahora incluye varias características de programación funcional . El lenguaje de enseñanza interactiva Blue, precursor de BlueJ , también se basa en Eiffel. La herramienta Apple Media Tool incluye un lenguaje Apple Media basado en Eiffel.
Especificaciones y normas
La definición del lenguaje Eiffel es un estándar internacional de la ISO . El estándar fue desarrollado por ECMA International , que lo aprobó por primera vez el 21 de junio de 2005 como el estándar ECMA-367, Eiffel: lenguaje de análisis, diseño y programación. En junio de 2006, ECMA e ISO adoptaron la segunda versión. En noviembre de 2006, ISO publicó por primera vez dicha versión. El estándar se puede encontrar y utilizar gratuitamente en el sitio web de ECMA. [ 10 ] La versión ISO [ 11 ] es idéntica en todos los aspectos, excepto en el formato.
Eiffel Software, el compilador Eiffel tecomp y el desarrollador de la biblioteca Eiffel, Gobo, se han comprometido a implementar el estándar. EiffelStudio 6.1 de Eiffel Software y el compilador Eiffel tecomp implementan algunos de los principales mecanismos nuevos, en particular, agentes en línea, comandos de asignación, notación de corchetes, herencia no conforme y tipos adjuntos. El equipo de SmartEiffel se ha alejado de este estándar para crear su propia versión del lenguaje, que consideran más cercana al estilo original de Eiffel. Object Tools no ha revelado si las futuras versiones de su compilador Eiffel cumplirán con el estándar. LibertyEiffel implementa un dialecto intermedio entre el lenguaje SmartEiffel y el estándar.
La norma cita las siguientes especificaciones predecesoras en lenguaje Eiffel:
- Bertrand Meyer: Eiffel: El lenguaje , Prentice Hall, segunda edición, 1992 (primera edición: 1991)
- Bertrand Meyer: Eiffel estándar (revisión de la entrada anterior), en curso, 1997-presente, en la página ETL3 de Bertrand Meyer, y
- Bertrand Meyer: Construcción de software orientado a objetos , Prentice Hall: primera edición, 1988; segunda edición, 1997.
- Bertrand Meyer: Un toque de distinción: Aprender a programar bien con objetos y contratos , Springer-Verlag , 2009 ISBN 978-3-540-92144-8lxiv + 876 páginas Impresión a todo color, numerosas fotografías a color
La versión actual del estándar, de junio de 2006, contiene algunas inconsistencias (por ejemplo, redefiniciones de covariantes) . El comité de la ECMA aún no ha anunciado ningún cronograma ni directriz sobre cómo resolver dichas inconsistencias.
Sintaxis y semántica
Estructura general
Un "sistema" o "programa" Eiffel es una colección de clases . Por encima del nivel de clases, Eiffel define clústeres , que es esencialmente un grupo de clases, y posiblemente de subclústeres (clústeres anidados). Los clústeres no son una construcción sintáctica del lenguaje , sino más bien una convención organizativa estándar. Normalmente, un programa Eiffel se organiza con cada clase en un archivo separado, y cada clúster en un directorio que contiene archivos de clase. En esta organización, los subclústeres son subdirectorios. Por ejemplo, bajo las convenciones organizativas y de mayúsculas estándar, x.epodría ser el nombre de un archivo que define una clase llamada X.
Una clase contiene características , similares a las "rutinas", "miembros", "atributos" o "métodos" de otros lenguajes de programación orientados a objetos. Una clase también define sus invariantes y contiene otras propiedades, como una sección de "notas" para documentación y metadatos. Los tipos de datos estándar de Eiffel, como INTEGER, STRINGy ARRAY, son todos clases.
Todo sistema debe tener una clase denominada "raíz", cuyo procedimiento de creación se denomina "procedimiento raíz". La ejecución de un sistema consiste en crear una instancia de la clase raíz y ejecutar su procedimiento raíz. Generalmente, esto crea nuevos objetos, llama a nuevas funcionalidades, etc.
Eiffel cuenta con cinco instrucciones ejecutables básicas: asignación, creación de objetos, llamada a rutinas, condición e iteración. Las estructuras de control de Eiffel son estrictas en cuanto a la aplicación de la programación estructurada : cada bloque tiene exactamente una entrada y una salida.
Alcance
A diferencia de muchos lenguajes orientados a objetos, pero al igual que Smalltalk , Eiffel no permite la asignación de valores a los atributos de los objetos, excepto dentro de las características de un objeto, lo que constituye la aplicación práctica del principio de ocultación de información o abstracción de datos, que requiere interfaces formales para la modificación de datos. En otras palabras, en Eiffel, todos los atributos están "protegidos" y se necesitan métodos de asignación (setters) para que los objetos cliente modifiquen sus valores. Esto implica que los métodos de asignación pueden implementar, y normalmente lo hacen, las invariantes para las que Eiffel proporciona sintaxis.
Si bien Eiffel no permite el acceso directo a las características de una clase por parte de un cliente de la clase, sí permite la definición de un "comando de asignación", como por ejemplo:
algún_atributo : ALGÚN_TIPO asignar establecer_algún_atributoestablecer_algún_atributo ( v : TIPO_VALOR )-- Establecer el valor de some_attribute a `v'.haceralgún_atributo := vfinSi bien permitir algo que parezca un acceso directo (por ejemplo, infringiendo el principio de ocultación de información) supone una pequeña concesión a la comunidad de desarrolladores, esta práctica es peligrosa, ya que oculta u oscurece la realidad del uso de un método setter. En la práctica, es mejor redirigir la llamada a un método setter en lugar de implicar un acceso directo a una funcionalidad, como some_attributeen el ejemplo de código anterior.
A diferencia de otros lenguajes, que tienen nociones de "público", "protegido", "privado", etc., Eiffel utiliza una tecnología de exportación para controlar con mayor precisión el alcance entre las clases cliente y proveedor. La visibilidad de las características se comprueba estáticamente en tiempo de compilación. Por ejemplo, (abajo), {NONE}es similar a "protegido" en otros lenguajes. El alcance aplicado de esta manera a un "conjunto de características" (por ejemplo, todo lo que está debajo de la palabra clave 'feature' hasta la siguiente palabra clave de conjunto de características o el final de la clase) se puede cambiar en las clases descendientes usando la palabra clave "exportar".
característica { NINGUNA } -- Inicializacióncreación_predeterminada-- Inicializa una nueva instancia decimal de tipo `cero`.hacerhacer_cerofinAlternativamente, la falta de una {x}declaración de exportación implica {ANY}y es similar al alcance "público" de otros idiomas.
Característica -- ConstantesFinalmente, el alcance se puede controlar de forma selectiva y precisa para cualquier clase en el universo del proyecto Eiffel, como por ejemplo:
característica { DECIMAL , DCM_MA_DECIMAL_PARSER , DCM_MA_DECIMAL_HANDLER } -- AccesoAquí, el compilador permitirá que solo las clases enumeradas entre llaves accedan a las características dentro del grupo de características (por ejemplo , DECIMAL, DCM_MA_DECIMAL_PARSER, DCM_MA_DECIMAL_HANDLER ).
"¡Hola Mundo!"
La apariencia y la sensación de un lenguaje de programación a menudo se transmiten mediante un programa "¡Hola, mundo!" . Un programa de este tipo escrito en Eiffel podría ser:
clase HELLO_WORLD crear hacer característica hacer hacer imprimir ( "Hola, mundo!%N" ) fin finEste programa contiene la clase HELLO_WORLD. El constructor (rutina de creación) de la clase, llamado make, invoca la printrutina de la biblioteca del sistema para escribir un¡Hola Mundo!mensaje a la salida.
Diseño por contrato
El concepto de Diseño por Contrato es fundamental para Eiffel. Los contratos establecen qué debe cumplirse antes de la ejecución de una rutina (precondición) y qué debe cumplirse después de su finalización (postcondición). Los contratos de invariantes de clase definen qué afirmaciones deben cumplirse tanto antes como después de acceder a cualquier característica de una clase (tanto rutinas como atributos). Además, los contratos codifican en código ejecutable las suposiciones de desarrolladores y diseñadores sobre el entorno operativo de las características de una clase o de la clase en su conjunto mediante el invariante.
El compilador Eiffel está diseñado para incluir los contratos de características y clases en distintos niveles. EiffelStudio, por ejemplo, ejecuta todos los contratos de características y clases durante la ejecución en el modo "Entorno de trabajo". Al crear un ejecutable, el compilador recibe instrucciones a través del archivo de configuración del proyecto (por ejemplo, un archivo ECF) para incluir o excluir cualquier conjunto de contratos. De este modo, un archivo ejecutable puede compilarse para incluir o excluir cualquier nivel de contrato, lo que permite realizar pruebas unitarias y de integración continuas . Además, los contratos pueden ejecutarse de forma continua y metódica mediante la función de Auto-Prueba de EiffelStudio.
Los mecanismos de Diseño por Contrato están estrechamente integrados con el lenguaje y guían la redefinición de características en la herencia:
- Requisito previo de rutina: El requisito previo solo puede debilitarse por herencia; cualquier llamada que cumpla con los requisitos del antepasado cumple con los del descendiente.
- Postcondición rutinaria: La postcondición solo puede reforzarse mediante herencia; cualquier resultado garantizado por el antepasado sigue estando garantizado por el descendiente.
- Invariante de clase: Condiciones que deben cumplirse tras la creación del objeto y tras cualquier llamada a una rutina de clase exportada. Debido a que el invariante se comprueba con tanta frecuencia, se convierte simultáneamente en la forma de condición o contrato más costosa y más potente.
Además, el lenguaje admite una "instrucción de verificación" (una especie de "aserción"), invariantes de bucle y variantes de bucle (que garantizan la terminación del bucle).
Capacidad de seguridad contra el vacío
La capacidad de protegerse contra valores void, al igual que el tipado estático, es otra herramienta para mejorar la calidad del software. El software protegido contra valores void está a salvo de errores en tiempo de ejecución causados por llamadas a referencias void y, por lo tanto, será más fiable que el software en el que se pueden producir llamadas a destinos void. La analogía con el tipado estático resulta útil. La capacidad de protegerse contra valores void puede considerarse una extensión del sistema de tipos, o un paso más allá del tipado estático, ya que el mecanismo para garantizar la protección contra valores void está integrado en el sistema de tipos.
La protección contra llamadas a destinos vacíos se puede apreciar mediante la noción de conexión y (por extensión) desconexión (por ejemplo, la palabra clave `detachable`). La funcionalidad segura frente a valores vacíos se puede observar en una breve modificación del código de ejemplo utilizado anteriormente:
algún_atributo : desmontable ALGÚN_TIPO usar_algún_atributo -- Establecer el valor de algún_atributo a `v'. hacer si adjunto algún_atributo como l_atributo entonces hacer_algo ( l_atributo ) fin fin hacer_algo ( a_valor : ALGÚN_TIPO ) -- Hacer algo con `a_valor'. hacer ... haciendo algo con ` a_valor ' ... finEl ejemplo de código anterior muestra cómo el compilador puede abordar estáticamente la fiabilidad de si some_attributese adjuntará o desvinculará en el punto en que se utilice. En particular, la attachedpalabra clave permite una "localización adjunta" (por ejemplo, l_attribute), cuyo ámbito se limita al bloque de código encerrado por la construcción de la instrucción if. Por lo tanto, dentro de este pequeño bloque de código, se puede garantizar estáticamente que la variable local (por ejemplo l_attribute) no sea void (es decir, segura para void).
Funcionalidades: comandos y consultas
La característica principal de una clase es que define un conjunto de características: como una clase representa un conjunto de objetos en tiempo de ejecución, o "instancias", una característica es una operación sobre estos objetos. Existen dos tipos de características: consultas y comandos. Una consulta proporciona información sobre una instancia. Un comando modifica una instancia.
La distinción entre comando y consulta es importante para el método Eiffel. En particular:
- Principio de acceso uniforme : desde la perspectiva de un cliente de software que llama a una funcionalidad de una clase, no debería haber diferencia alguna entre una consulta a un atributo (valor de campo) o a una función (valor calculado). Por ejemplo,
a_vehicle.speedpodría ser un atributo del objetoa_vehicleo podría ser calculado por una función que divide la distancia entre el tiempo. La notación es la misma en ambos casos, lo que facilita modificar la implementación de la clase sin afectar al software cliente. - Principio de separación entre comandos y consultas : Las consultas no deben modificar la instancia. Esto no es una regla del lenguaje, sino un principio metodológico. Así, al estilo Eiffel, no se encuentran funciones "get" que modifiquen algo y devuelvan un resultado; en su lugar, existen comandos (procedimientos) para modificar objetos y consultas para obtener información sobre el objeto, como resultado de modificaciones previas.
Sobrecarga
Eiffel no permite la sobrecarga de argumentos . Cada nombre de característica dentro de una clase siempre se corresponde con una característica específica dentro de la misma clase. Un nombre, dentro de una clase, significa una sola cosa. Esta decisión de diseño facilita la legibilidad de las clases, al evitar una causa de ambigüedad sobre qué rutina se invocará mediante una llamada. También simplifica el mecanismo del lenguaje; en particular, esto es lo que posibilita el mecanismo de herencia múltiple de Eiffel. [ 12 ]
Por supuesto, los nombres pueden reutilizarse en diferentes clases. Por ejemplo, la característica plus (junto con su alias infijo "+" ) está definida en varias clases: INTEGER , REAL , STRING , etc.
genericidad
Una clase genérica es una clase que varía según el tipo (por ejemplo LIST [PHONE], una lista de números de teléfono; ACCOUNT [G->ACCOUNT_TYPE], que permite ACCOUNT [SAVINGS]y ACCOUNT [CHECKING], etc.). Las clases pueden ser genéricas para indicar que están parametrizadas por tipos. Los parámetros genéricos aparecen entre corchetes:
clase LISTA [ G ] ...G se conoce como un "parámetro genérico formal". (Eiffel reserva "argumento" para rutinas y usa "parámetro" solo para clases genéricas). Con dicha declaración, G representa dentro de la clase un tipo arbitrario; por lo tanto, una función puede devolver un valor de tipo G , y una rutina puede tomar un argumento de ese tipo:
elemento : G hacer ... fin poner ( x : G ) hacer ... finLos LIST [INTEGER]y LIST [WORD]son "derivaciones genéricas" de esta clase. Las combinaciones permitidas (con n: INTEGER, w: WORD, il: LIST [INTEGER], wl: LIST [WORD]) son:
n := il . item wl . put ( w )INTEGERy WORDson los "parámetros genéricos reales" en estas derivaciones genéricas.
También es posible tener parámetros formales "restringidos", para los cuales el parámetro real debe heredar de una clase dada, la "restricción". Por ejemplo, en
clase HASH_TABLE [ G , CLAVE -> HASHABLE ]Una derivación HASH_TABLE [INTEGER, STRING]es válida solo si STRINGhereda de HASHABLE(como de hecho ocurre en las bibliotecas típicas de Eiffel). Dentro de la clase, tener KEYrestringido por HASHABLEsignifica que para x: KEYes posible aplicar a xtodas las características de HASHABLE, como en x.hash_code.
Conceptos básicos de la herencia
Para heredar de una o más clases, una clase incluirá una inheritcláusula al principio:
La clase C hereda de A B.-- ... Resto de la declaración de clase ...La clase puede redefinir (sobrescribir) algunas o todas las características heredadas. Esto debe anunciarse explícitamente al comienzo de la clase a través de una redefinesubcláusula de la cláusula de herencia, como en
clase C hereda A redefine f , g , h end B redefine u , v endConsulte [ 13 ] para un análisis completo de la herencia Eiffel.
Clases y características diferidas
Las clases pueden definirse con deferred classen lugar de classpara indicar que la clase no puede instanciarse directamente. Las clases no instanciables se denominan clases abstractas en otros lenguajes de programación orientada a objetos. En la terminología de Eiffel, solo una clase "efectiva" puede instanciarse (puede ser descendiente de una clase diferida). Una característica también puede diferirse utilizando la deferredpalabra clave en lugar de una docláusula. Si una clase tiene características diferidas, debe declararse como diferida; sin embargo, una clase sin características diferidas puede diferirse también.
Las clases diferidas desempeñan un papel similar al de las interfaces en lenguajes como Java, aunque muchos teóricos de la programación orientada a objetos creen que las interfaces son en gran medida una respuesta a la falta de herencia múltiple de Java (que Eiffel sí tiene). [ 14 ] [ 15 ]
Cambiar el nombre
Una clase que hereda de una o más clases hereda todas sus características, por defecto, con sus nombres originales. Sin embargo, puede cambiar sus nombres mediante renamecláusulas. Esto es necesario en el caso de herencia múltiple si existen conflictos de nombres entre las características heredadas; sin renombrarlas, la clase resultante violaría el principio de no sobrecarga mencionado anteriormente y, por lo tanto, sería inválida.
Tuplas
Los tipos de tuplas pueden considerarse una forma simple de clase, que proporciona únicamente atributos y el procedimiento "setter" correspondiente. Un tipo de tupla típico se lee:
TUPLE [ nombre : CADENA ; peso : REAL ; fecha : FECHA ]y podría usarse para describir una noción simple de registro de nacimiento si no se necesita una clase. Una instancia de dicha tupla es simplemente una secuencia de valores con los tipos dados, dados entre corchetes, como por ejemplo:
[ "Brigitte" , 3.5 , Anoche ]Se puede acceder a los componentes de dicha tupla como si las etiquetas de la tupla fueran atributos de una clase; por ejemplo, si tse le ha asignado la tupla anterior, entonces t.weighttiene el valor 3.5.
Gracias a la noción de comando de asignación (ver más abajo), la notación de punto también se puede utilizar para asignar componentes de dicha tupla, como en
t.peso : = t.peso + 0,5Las etiquetas de tupla son opcionales, por lo que también es posible escribir un tipo de tupla como TUPLE [STRING, REAL, DATE]. (En algunos compiladores, esta es la única forma de tupla, ya que las etiquetas se introdujeron con el estándar ECMA).
La especificación precisa de eg TUPLE [A, B, C]es que describe secuencias de al menos tres elementos, siendo los tres primeros de tipos A, B, Crespectivamente. Como resultado, TUPLE [A, B, C]se ajusta a (puede asignarse a) TUPLE [A, B], a TUPLE [A]y a TUPLE(sin parámetros), el tipo de tupla superior al que se ajustan todos los tipos de tupla.
Agentes
El mecanismo de "agentes" de Eiffel encapsula las operaciones en objetos. Este mecanismo se puede utilizar para la iteración, la programación orientada a eventos y otros contextos en los que resulta útil pasar operaciones a través de la estructura del programa. Otros lenguajes de programación, especialmente aquellos que enfatizan la programación funcional , permiten un patrón similar mediante continuaciones , cierres o generadores ; los agentes de Eiffel enfatizan el paradigma orientado a objetos del lenguaje y utilizan una sintaxis y semántica similares a los bloques de código en Smalltalk y Ruby .
Por ejemplo, para ejecutar el my_actionbloque para cada elemento de my_list, se escribiría:
mi_lista.hacer_todo ( agente mi_acción )Para ejecutar my_actionsolo en elementos que satisfagan my_condition, se puede agregar una limitación/filtro:
mi_lista.hacer_si ( agente mi_acción , agente mi_condición )En estos ejemplos, my_actiony my_conditionson rutinas. Anteponerles agentproduce un objeto que representa la rutina correspondiente con todas sus propiedades, en particular la capacidad de ser llamada con los argumentos apropiados. Entonces, si arepresenta ese objeto (por ejemplo, porque aes el argumento de do_all), la instrucción
a.llamar ( [ x ] )llamará a la rutina original con el argumento x, como si hubiéramos llamado directamente a la rutina original: my_action (x). Los argumentos a callse pasan como una tupla, aquí [x].
Es posible mantener algunos argumentos de un agente abiertos y cerrar otros . Los argumentos abiertos se pasan como argumentos a call: se proporcionan en el momento del uso del agente . Los argumentos cerrados se proporcionan en el momento de la definición del agente . Por ejemplo, si action2tiene dos argumentos, la iteración
mi_lista . hacer_todo ( agente acción2 ( ? , y ))itera action2 (x, y)para valores sucesivos de x, donde el segundo argumento permanece establecido en y. El signo de interrogación ?indica un argumento abierto; yes un argumento cerrado del agente. Nótese que la sintaxis básica agent fes una abreviatura de con todos los argumentos abiertos. También es posible hacer que el objetivo de un agente sea abierto mediante la notación donde es el tipo del objetivo.agent f (?, ?, ...){T}?T
La distinción entre operandos abiertos y cerrados (operandos = argumentos + objetivo) corresponde a la distinción entre variables ligadas y libres en el cálculo lambda . Una expresión de agente como action2 (?, y)con algunos operandos cerrados y otros abiertos corresponde a una versión de la operación original currificada sobre los operandos cerrados.
El mecanismo de agentes también permite definir un agente sin referencia a una rutina existente (como my_action, my_condition, action2), a través de agentes en línea como en
mi_lista . hacer_todo ( agente ( s : STRING ) requerir not_void : s /= Void hacer s . agregar_caracter ( ',' ) asegurar agregado : s . contar = antiguo s . contar + 1 fin )El agente en línea que se pasa aquí puede tener todas las características de una rutina normal, incluyendo precondición, postcondición, cláusula de rescate (no utilizada aquí) y una firma completa. Esto evita definir rutinas cuando lo único que se necesita es un cálculo que se encapsule en un agente. Esto resulta especialmente útil para contratos, como en una cláusula invariante que expresa que todos los elementos de una lista son positivos.
mi_lista . para_todos ( agente ( x : ENTERO ): BOOLEANO hacer Resultado := ( x > 0 ) fin )El mecanismo actual del agente deja abierta la posibilidad de un error de tipo en tiempo de ejecución (si se pasa una rutina con n argumentos a un agente que espera m argumentos con m < n ). Esto se puede evitar mediante una comprobación en tiempo de ejecución a través de la precondición valid_argumentsde call. Existen varias propuestas para una corrección puramente estática de este problema, incluida una propuesta de cambio de lenguaje de Ribet et al. [ 16 ].
Una vez rutinas
El resultado de una rutina se puede almacenar en caché usando la oncepalabra clave en lugar de do. Las llamadas posteriores a una rutina no requieren cálculos adicionales ni asignación de recursos, sino que simplemente devuelven un resultado calculado previamente. Un patrón común para las "funciones de una sola vez" es proporcionar objetos compartidos; la primera llamada creará el objeto, y las subsiguientes devolverán la referencia a ese objeto. El esquema típico es:
shared_object : SOME_TYPE una vez crear Result.make ( args ) -- Esto crea el objeto y devuelve una referencia a él a través de `Result'.endEl objeto devuelto Result—en el ejemplo— puede ser mutable, pero su referencia permanece invariable.
Con frecuencia, las rutinas de "una sola vez" realizan una inicialización necesaria: varias llamadas a una biblioteca pueden incluir una llamada al procedimiento de inicialización, pero solo la primera de estas llamadas realizará las acciones requeridas. Mediante este patrón, la inicialización puede descentralizarse, evitando la necesidad de un módulo de inicialización específico. Las rutinas de "una sola vez" son similares en propósito y efecto al patrón Singleton en muchos lenguajes de programación, y al patrón Borg utilizado en Python.
Por defecto, una "rutina única" se llama una vez por hilo . La semántica se puede ajustar a una vez por proceso o una vez por objeto calificándola con una "clave única", por ejemplo once ("PROCESS").
Conversiones
Eiffel proporciona un mecanismo que permite conversiones entre distintos tipos. Este mecanismo coexiste con la herencia y la complementa. Para evitar cualquier confusión entre ambos mecanismos, el diseño impone el siguiente principio:
- (Principio de conversión) Un tipo no puede ser conforme y a la vez convertirse a otro.
Por ejemplo, NEWSPAPERpuede ajustarse a PUBLICATION, pero INTEGERse convierte en REAL(y no hereda de él).
El mecanismo de conversión simplemente generaliza las reglas de conversión ad hoc (como, por ejemplo, entre INTEGERy REAL) que existen en la mayoría de los lenguajes de programación, haciéndolas aplicables a cualquier tipo siempre que se observe el principio anterior. Por ejemplo, DATEse puede declarar una clase para convertir a STRING; esto permite crear una cadena a partir de una fecha simplemente mediante
mi_cadena := mi_fechacomo atajo para usar una creación de objeto explícita con un procedimiento de conversión:
crear mi_cadena.crear_desde_fecha ( mi_fecha )Para que la primera forma sea posible como sinónimo de la segunda, basta con enumerar el procedimiento de creación (constructor) make_from_dateen una convertcláusula al principio de la clase.
Como otro ejemplo, si existe un procedimiento de conversión de este tipo listado desde TUPLE [day: INTEGER; month: STRING; year: INTEGER], entonces se puede asignar directamente una tupla a una fecha, provocando la conversión adecuada, como en
Día de la Bastilla := [ 14 , "Julio" , 1789 ]Manejo de excepciones
En Eiffel, el manejo de excepciones se basa en los principios del diseño por contrato. Por ejemplo, se produce una excepción cuando quien llama a una rutina no cumple una precondición o cuando una rutina no puede garantizar una postcondición prometida. En Eiffel, el manejo de excepciones no se utiliza para el control del flujo ni para corregir errores en la entrada de datos.
Un manejador de excepciones Eiffel se define mediante la palabra clave `rescue` . Dentro de la sección `rescue` , la palabra clave `retry` ejecuta la rutina nuevamente. Por ejemplo, la siguiente rutina registra el número de intentos de ejecución y solo reintenta un número determinado de veces:
connect_to_server ( servidor : SOCKET ) -- Conectarse a un servidor o desistir después de 10 intentos. require server /= Void and then server . address /= Void local attempts : INTEGER do server . connect ensure connected : server . is_connected rescue if attempts < 10 then attempts := attempts + 1 retry end endEste ejemplo resulta defectuoso para cualquier programa que no sea extremadamente sencillo, ya que es de esperar que se produzca un fallo de conexión. Para la mayoría de los programas, sería mejor usar una rutina con un nombre como ` attempt_connecting_to_server` , y la condición posterior no garantizaría la conexión, dejando que sea el programa que la llama quien deba tomar las medidas adecuadas si la conexión no se establece.
Concurrencia
Existen varias bibliotecas de redes y subprocesos, como EiffelNet y EiffelThreads. Un modelo de concurrencia para Eiffel, basado en los conceptos de diseño por contrato, es SCOOP ( Simple Concurrent Object-Oriented Programming ), que aún no forma parte de la definición oficial del lenguaje, pero está disponible en EiffelStudio . CAMEO [ 17 ] es una variación (no implementada) de SCOOP para Eiffel. La concurrencia también interactúa con las excepciones. Las excepciones asíncronas pueden ser problemáticas (cuando una rutina genera una excepción después de que quien la llama haya terminado). [ 18 ]
Sintaxis de operadores y corchetes, comandos de asignación
La visión de Eiffel sobre la computación es completamente orientada a objetos en el sentido de que cada operación es relativa a un objeto, el "objetivo". Por ejemplo, una suma como
a + bse entiende conceptualmente como si fuera la llamada al método
a . más ( b )con objetivo a, característica plusy argumento b.
Por supuesto, la primera es la sintaxis convencional y generalmente la preferida. La sintaxis de operadores permite usar cualquiera de las dos formas declarando la característica (por ejemplo, en INTEGER, pero esto se aplica a otras clases básicas y se puede usar en cualquier otra para la que dicho operador sea apropiado):
El abanico de operadores que pueden utilizarse como alias es bastante amplio; incluye operadores predefinidos como "+" pero también operadores libres compuestos por símbolos no alfanuméricos. Esto permite diseñar notaciones infijas y prefijas especiales, por ejemplo, en aplicaciones matemáticas y físicas.
Cada clase puede tener además una función con alias "[]", el operador de corchetes, lo que permite la notación a [i, ...]como sinónimo de a.f (i, ...)donde fes la función elegida. Esto es particularmente útil para estructuras de contenedores como matrices, tablas hash , listas, etc. Por ejemplo, el acceso a un elemento de una tabla hash con claves de cadena se puede escribir
número := agenda_telefónica [ "JILL SMITH" ]Los "comandos de asignación" son un mecanismo complementario diseñado con el mismo espíritu de permitir una notación bien establecida y conveniente reinterpretada en el marco de la programación orientada a objetos. Los comandos de asignación permiten que la sintaxis similar a la de las asignaciones llame a procedimientos "setter". Una asignación propiamente dicha nunca puede tener la forma ya que esto viola la ocultación de información; se debe usar un comando setter (procedimiento). Por ejemplo, la clase de tabla hash puede tener la función y el procedimientoa.x := v
-- Alias y comandos del asignadoralias de elemento "[]" ( clave : STRING ): ELEMENTO -- ☝︎1-- El elemento de clave `key'.-- (Consulta "Getter")hacer...finponer ( e : ELEMENTO ; clave : CADENA )-- Inserta el elemento `e', asociándolo con la clave `key'.-- (Comando "Setter")hacer...finPara insertar un elemento, debe utilizar una llamada explícita al comando setter:
-- usando put() directamente phone_book.put ( New_person , " JILL SMITH" ) -- ☝︎2Es posible escribir esto de forma equivalente como
-- usando el alias phone_book [ "JILL SMITH" ] := New_person -- ☝︎3(de la misma manera que phone_book ["JILL SMITH"]es sinónimo de ), siempre que la declaración de ahora comience (reemplazo para ☝︎ 1 ) connumber := phone_book.item ("JILL SMITH")item
alias de elemento "[]" ( clave : STRING ): ELEMENTO asignar ponerEsto declara putcomo el comando de asignación asociado con itemy, combinado con el alias de corchete, hace que ☝︎ 3 sea legal y equivalente a ☝︎ 2 . (También podría escribirse, sin aprovechar el corchete, como .phone_book.item ("JILL SMITH") := New_person
Nota: La lista de argumentos del asignador de a está restringida a: (el tipo de retorno de a; toda la lista de argumentos de a...)
Propiedades léxicas y sintácticas
Eiffel no distingue entre mayúsculas y minúsculas. Los tokens make, maKey MAKEdenotan el mismo identificador. Sin embargo, consulte las "reglas de estilo" a continuación.
Los comentarios se introducen mediante --(dos guiones consecutivos) y se extienden hasta el final de la línea.
El punto y coma, como separador de instrucciones, es opcional. Generalmente se omite, excepto para separar varias instrucciones en una misma línea. Esto reduce el desorden en la página del programa.
No existe anidamiento de declaraciones de características y clases. Como resultado, la estructura de una clase Eiffel es simple: algunas cláusulas a nivel de clase (herencia, invariante) y una sucesión de declaraciones de características, todas al mismo nivel.
Es habitual agrupar las características en "cláusulas de características" separadas para una mayor legibilidad, con un conjunto estándar de etiquetas de características básicas que aparecen en un orden estándar, por ejemplo:
clase HASH_TABLE [ ELEMENTO , CLAVE -> HASHABLE ] hereda TABLA [ ELEMENTO ]Característica -- Inicialización -- ... Declaraciones de comandos de inicialización (procedimientos/constructores de creación) ...Característica -- Acceso -- ... Declaraciones de consultas no booleanas sobre el estado del objeto, por ejemplo, elemento ...Característica -- Informe de estado -- ... Declaraciones de consultas booleanas sobre el estado del objeto, por ejemplo, is_empty ...Característica -- Cambio de elemento -- ... Declaraciones de comandos que modifican la estructura, por ejemplo, put ...-- etc. finA diferencia de la mayoría de los lenguajes de programación que utilizan llaves , Eiffel distingue claramente entre expresiones e instrucciones. Esto concuerda con el principio de separación de comandos y consultas del método Eiffel.
convenciones de estilo
Gran parte de la documentación de Eiffel utiliza convenciones de estilo distintivas, diseñadas para garantizar una apariencia uniforme. Algunas de estas convenciones se aplican al formato del código en sí, y otras a la representación tipográfica estándar del código Eiffel en formatos y publicaciones donde dichas convenciones son posibles.
Aunque el lenguaje no distingue entre mayúsculas y minúsculas, las normas de estilo prescriben el uso de mayúsculas para los nombres de clase ( LIST), minúsculas para los nombres de características ( make) y mayúscula inicial para las constantes ( Avogadro). El estilo recomendado también sugiere el uso de guiones bajos para separar los componentes de un identificador de varias palabras, como en average_temperature.
La especificación de Eiffel incluye directrices para mostrar textos de software en formatos tipográficos: las palabras clave se muestran en bold, los identificadores y constantes definidos por el usuario se muestran en italics, los comentarios, operadores y signos de puntuación en Roman, con el texto del programa en bluecomo en el presente artículo para distinguirlo del texto explicativo. Por ejemplo, el programa "¡ Hola, mundo! " que se muestra arriba se representaría como se indica a continuación en la documentación de Eiffel:
clase HELLO_WORLD crear hacer característica hacer hacer imprimir (" ¡Hola, mundo! ") fin finInterfaces con otras herramientas y lenguajes
Eiffel es un lenguaje puramente orientado a objetos, pero proporciona una arquitectura abierta para interactuar con software "externo" en cualquier otro lenguaje de programación.
Por ejemplo, es posible programar operaciones a nivel de máquina y de sistema operativo en C. Eiffel proporciona una interfaz sencilla para rutinas de C, incluyendo soporte para "C en línea" (escribir el cuerpo de una rutina de Eiffel en C, normalmente para operaciones cortas a nivel de máquina).
No existe una conexión directa entre Eiffel y C. Sin embargo, muchos compiladores Eiffel ( Visual Eiffel es una excepción) generan código fuente C como lenguaje intermedio , para enviarlo a un compilador C, para su optimización o portabilidad . Por lo tanto, son ejemplos de transcompiladores . El compilador Eiffel tecomp puede ejecutar código Eiffel directamente (como un intérprete) sin pasar por un código C intermedio o generar código C que se pasará a un compilador C para obtener código nativo optimizado. En .NET , el compilador EiffelStudio genera directamente código Common Intermediate Language (CIL). El compilador SmartEiffel también puede generar bytecode Java .
Referencias
- ↑ "Eiffel en pocas palabras" . Eiffel Software . 1985–2012 . Consultado el 29 de noviembre de 2024 .
- ↑ "¡EiffelStudio 25.12 ya está disponible! Una plataforma backend moderna diseñada para la fiabilidad" . Eiffel.org . 26 de enero de 2026. Consultado el 6 de abril de 2026 .
- ↑ Cooper, Peter (2009). Beginning Ruby: From Novice to Professional . Beginning from Novice to Professional (2.ª ed.). Berkeley: APress. p. 101. ISBN 978-1-4302-2363-4
En menor medida, Python, LISP, Eiffel, Ada y C++ también han influido en Ruby
. - ↑ "Eiffel – el lenguaje" . berenddeboer.net . Consultado el 6 de julio de 2016 .
- ↑ Meyer, Bertrand (28 de agosto de 2009). Touch of Class: Learning to Program Well with Objects and Contracts . Springer Science & Business Media. ISBN 978-3-540-92144-8.
- ↑ "Lenguajes de programación - Eiffel" (PDF) . Departamento de Ciencias de la Computación, Virginia Tech . Consultado el 25 de marzo de 2023 .
- ↑ Carl Friess. "Guía de sintaxis Eiffel" . Guía de sintaxis Eiffel . Consultado el 25 de marzo de 2023 .
- ↑ Claus Brabrand. "El lenguaje de programación EIFFEL" (PDF) . Universidad de TI de Copenhague . Consultado el 25 de marzo de 2023 .
- ↑ Meyer, Bertrand (1997). Construcción de software orientado a objetos (2.ª ed.). Prentice Hall . ISBN 0-13-629155-4.
- ↑ ECMA International: Norma ECMA-367 – Eiffel: Análisis, diseño y lenguaje de programación, 2.ª edición (junio de 2006); disponible en línea en https://ecma-international.org/publications-and-standards/standards/ecma-367/
- ↑ Organización Internacional de Normalización: Norma ISO/IEC DIS 25436, disponible en línea en
- ↑ Bertrand Meyer: Sobrecarga frente a tecnología de objetos, en Journal of Object-Oriented Programming (JOOP), vol. 14, n.º 4, octubre-noviembre de 2001, disponible en línea.
- ↑ "9 HERENCIA" . Archive.eiffel.com. 23 de marzo de 1997. Consultado el 8 de julio de 2013 .
- ↑ "Herencia múltiple e interfaces" . Artima.com. 16 de diciembre de 2002. Consultado el 8 de julio de 2013 .
- ↑ "La herencia múltiple no es mala" . C2.com. 28 de abril de 2007. Consultado el 8 de julio de 2013 .
- ↑ Philippe Ribet, Cyril Adrian, Olivier Zendra, Dominique Colnet: Conformidad de agentes en el lenguaje Eiffel , en Journal of Object Technology , vol. 3, n.º 4, abril de 2004, número especial: TOOLS USA 2003, pp. 125-143. Disponible en línea en la página del artículo de JOT.
- ↑ Brooke, Phillip; Richard Paige (2008). "Cameo: Un modelo alternativo de concurrencia para Eiffel" (PDF) . Aspectos formales de la computación . 21 (4). Springer: 363–391 . doi : 10.1007/s00165-008-0096-1 . S2CID 18336088 .
- ↑ Brooke, Phillip; Richard Paige (2007). "Excepciones en Eiffel concurrente" . Journal of Object Technology . 6 (10): 111– 126. doi : 10.5381/jot.2007.6.10.a4 .
Enlaces externos
- El sitio web de Eiffel Software , la empresa que lanzó Eiffel, era Interactive Software Engineering (ISE).
- Sitio web LibertyEiffel del sistema de desarrollo libre GNU Eiffel.
- Lenguajes de programación basados en clases
- lenguajes de especificación formal
- Lenguajes de programación de alta integridad
- Lenguajes de programación orientados a objetos
- Lenguajes de programación creados en 1986
- Lenguajes de programación con un estándar ISO
- Lenguajes de programación de tipado estático
