stringtranslate.com

Eiffel (lenguaje de programación)

Eiffel es un lenguaje de programación orientado a objetos diseñado por Bertrand Meyer (un 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 estuvo disponible en 1986. En 2005, Eiffel se convirtió en un lenguaje estandarizado por la ISO .

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 apertura y cierre y la separación entre opciones y operandos .

Muchos conceptos introducidos inicialmente por Eiffel encontraron luego su camino hacia Java , C# y otros lenguajes. [5] Nuevas ideas de diseño de lenguajes, particularmente a través del proceso de estandarización Ecma / ISO , continúan incorporándose al lenguaje Eiffel.

Características

Las características clave del lenguaje Eiffel incluyen:

Objetivos de diseño

Eiffel enfatiza las declaraciones declarativas sobre el código de procedimiento e intenta eliminar la necesidad de instrucciones de contabilidad.

Eiffel evita trucos de codificación o técnicas de codificación que pretendan ser sugerencias de optimización para el compilador. El objetivo no es sólo hacer el código más legible, sino también permitir a los programadores concentrarse en los aspectos importantes de un programa sin empantanarse en detalles de implementación. La simplicidad de Eiffel tiene como objetivo promover respuestas simples, extensibles, reutilizables y confiables a los problemas informáticos. Los compiladores para programas de computadora escritos en Eiffel proporcionan técnicas de optimización extensas, como la inserción automática en línea, que liberan al programador de parte de la carga de optimización.

Fondo

Eiffel fue desarrollado originalmente por Eiffel Software, una empresa fundada por Bertrand Meyer . Object-Oriented Software Construction contiene un tratamiento 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 detrás del lenguaje, las bibliotecas y los métodos de programación de Eiffel es permitir a los programadores crear módulos de software confiables y reutilizables. Eiffel admite herencia múltiple , genericidad , polimorfismo , encapsulación , conversiones de tipos seguros 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 afirmaciones , precondiciones , poscondiciones e invariantes de clase para ayudar a 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 una influencia menor de otros paradigmas o preocupación por el soporte de 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 texto mismo, utilizando una implementación formalizada del "Tipo de datos abstracto".

Implementaciones y entornos

EiffelStudio es un entorno de desarrollo integrado disponible bajo licencia de código abierto o comercial. Ofrece un entorno orientado a objetos para la ingeniería de software . EiffelEnvision es un complemento para Microsoft Visual Studio que permite a los usuarios editar, compilar y depurar proyectos de 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 de GNU, basada en una versión anterior del lenguaje; LibertyEiffel , basada en el compilador SmartEiffel; y Visual Eiffel .

Varios otros lenguajes de programación incorporan elementos introducidos por primera vez en Eiffel. Sather , por ejemplo, se basó originalmente en Eiffel, pero desde entonces se ha desviado y ahora incluye varias características de programación funcional . El lenguaje de enseñanza interactiva Blue, precursor de BlueJ , también está basado en Eiffel. Apple Media Tool incluye un Apple Media Language 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 aprobó por primera vez el estándar el 21 de junio de 2005 como Estándar ECMA-367, Eiffel: Análisis, Diseño y Lenguaje de Programación. En junio de 2006, ECMA e ISO adoptaron la segunda versión. En noviembre de 2006, ISO publicó por primera vez esa versión. El estándar se puede encontrar y usar de forma gratuita en el sitio de ECMA. [10] La versión ISO [11] es idéntica en todos los aspectos excepto en el formato.

Eiffel Software, "The Eiffel Compiler" tecomp y el desarrollador de la biblioteca Eiffel Gobo se han comprometido a implementar el estándar; EiffelStudio 6.1 y "The Eiffel Compiler" tecomp de Eiffel Software implementan algunos de los principales mecanismos nuevos, en particular, agentes en línea, comandos de asignación, notación entre corchetes, herencia no conforme y tipos adjuntos. El equipo SmartEiffel se ha alejado de este estándar para crear su propia versión del lenguaje, que creen que se acerca más 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 en algún punto intermedio entre el lenguaje SmartEiffel y el estándar.

La norma cita las siguientes especificaciones del lenguaje Eiffel predecesoras:

La versión actual de la norma, de junio de 2006, contiene algunas inconsistencias (por ejemplo, redefiniciones de covariantes) [ cita requerida ] . El comité de la ECMA aún no ha anunciado ningún cronograma ni dirección sobre cómo resolver las inconsistencias.

Sintaxis y semántica

Estructura general

Un "sistema" o "programa" de Eiffel es una colección de clases . Por encima del nivel de clases, Eiffel define cluster , que es esencialmente un grupo de clases y, posiblemente, de subclusters (clusters anidados). Los clusters no son una construcción sintáctica del lenguaje , sino más bien una convención organizativa estándar. Normalmente, un programa de Eiffel se organizará con cada clase en un archivo separado y cada cluster en un directorio que contiene archivos de clase. En esta organización, los subclusters son subdirectorios. Por ejemplo, según las convenciones organizativas y de mayúsculas y minúsculas estándar, x.epodría ser el nombre de un archivo que define una clase llamada X.

Una clase contiene características , que son similares a las "rutinas", "miembros", "atributos" o "métodos" en 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 la documentación y los metadatos. Los tipos de datos estándar de Eiffel, como INTEGER, STRINGy ARRAY, son todos ellos clases.

Todo sistema debe tener una clase designada como "raíz", con uno de sus procedimientos de creación designado como "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, al hacerlo se crean nuevos objetos, se invocan nuevas funciones, etc.

Eiffel tiene cinco instrucciones ejecutables básicas: asignación, creación de objeto, llamada de rutina, condición e iteración. Las estructuras de control de Eiffel son estrictas en la aplicación de la programación estructurada : cada bloque tiene exactamente una entrada y exactamente una salida.

Alcance

A diferencia de muchos lenguajes orientados a objetos, pero al igual que Smalltalk , Eiffel no permite ninguna asignación en atributos de objetos, excepto dentro de las características de un objeto, que es la aplicación práctica del principio de ocultamiento de información o abstracción de datos, que requiere interfaces formales para la mutación de datos. Para ponerlo en el lenguaje de otros lenguajes de programación orientados a objetos, todos los atributos de Eiffel están "protegidos", y se necesitan "setters" para que los objetos cliente modifiquen los valores. Una consecuencia de esto es que los "setters" pueden, y normalmente lo hacen, implementar 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 asignador", como por ejemplo:

  some_attribute : SOME_TYPE asignar set_some_attribute set_some_attribute ( v : VALUE_TYPE ) -- Establecer el valor de some_attribute en 'v'. hacer some_attribute := v fin             

Si bien es una pequeña concesión a la comunidad de desarrolladores en general al permitir algo que parezca un acceso directo (por ejemplo, violando así el principio de ocultamiento de información), la práctica es peligrosa ya que oculta u ofusca la realidad de que se está utilizando un "configurador". En la práctica, es mejor redirigir la llamada a un configurador en lugar de implicar un acceso directo a una función como some_attributeen el código de ejemplo anterior. [ cita requerida ]

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 de cliente y proveedor. La visibilidad de las características se verifica de forma estática en tiempo de compilación. Por ejemplo, (a continuación), "{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á por debajo de la palabra clave 'feature' hasta la siguiente palabra clave del conjunto de características o el final de la clase) se puede cambiar en las clases descendientes utilizando la palabra clave "export".

feature { NONE } - Inicialización default_create - Inicializa una nueva instancia decimal 'cero'. do make_zero end  

Alternativamente, la falta de una declaración de exportación {x} implica {ANY} y es similar al alcance "público" de otros lenguajes.

característica -- Constantes 

Finalmente, 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 } -- Acceso    

Aquí, 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 el funcionamiento de un lenguaje de programación suelen transmitirse mediante un programa del tipo "¡Hola, mundo!" . Un programa escrito en Eiffel podría ser:

clase HOLA_MUNDO crear hacer función hacer imprimir ( " Hola, mundo!%N" ) fin fin       

Este 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 "Hello, world!"mensaje en la salida.

Diseño por contrato

El concepto de diseño por contrato es central para Eiffel. Los contratos afirman lo que debe ser verdad antes de que se ejecute una rutina (precondición) y lo que debe seguir siendo verdad después de que finalice la rutina (poscondición). Los contratos de invariantes de clase definen qué afirmaciones deben ser verdaderas tanto antes como después de que se acceda a cualquier característica de una clase (tanto rutinas como atributos). Además, los contratos codifican en código ejecutable las suposiciones de los desarrolladores y diseñadores sobre el entorno operativo de las características de una clase o de la clase en su conjunto por medio de los invariantes.

El compilador Eiffel está diseñado para incluir los contratos de clase y de características en varios niveles. EiffelStudio, por ejemplo, ejecuta todos los contratos de clase y de características durante la ejecución en el "modo Workbench". Cuando se crea un ejecutable, el compilador recibe instrucciones a través del archivo de configuración del proyecto (por ejemplo, el archivo ECF) para incluir o excluir cualquier conjunto de contratos. De este modo, se puede compilar un archivo ejecutable para incluir o excluir cualquier nivel de contrato, lo que permite realizar pruebas unitarias y de integración de forma continua. Además, los contratos se pueden ejecutar de forma continua y metódica mediante la función de prueba automática que se encuentra en 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:

Además, el lenguaje admite una "instrucción de verificación" (una especie de "afirmación"), invariantes de bucle y variantes de bucle (que garantizan la terminación del bucle).

Capacidad de seguridad frente al vacío

La capacidad de seguridad frente a vacíos, al igual que la tipificación estática, es otra función para mejorar la calidad del software. El software de seguridad frente a vacíos está protegido de errores de tiempo de ejecución causados ​​por llamadas a referencias void y, por lo tanto, será más confiable que el software en el que pueden ocurrir llamadas a destinos void. La analogía con la tipificación estática es útil. De hecho, la capacidad de seguridad frente a vacíos podría verse como una extensión del sistema de tipos, o un paso más allá de la tipificación estática, porque el mecanismo para garantizar la seguridad frente a vacíos está integrado en el sistema de tipos.

La protección contra llamadas de destino nulas se puede ver a través de la noción de conexión y (por extensión) desconexión (por ejemplo, la palabra clave detachable). La función de seguridad frente a nulos se puede ver en una breve reelaboración del código de ejemplo utilizado anteriormente:

 some_attribute : desprendible SOME_TYPE use_some_attribute -- Establece el valor de some_attribute en `v'. do si se adjunta some_attribute como l_attribute entonces do_something ( l_attribute ) fin fin do_something ( a_value : SOME_TYPE ) -- Haz algo con `a_value'. do ... haciendo algo con ` a_value ' ... fin                             

El ejemplo de código anterior muestra cómo el compilador puede abordar de forma estática la fiabilidad de si some_attributese adjuntará o separará en el punto en que se utiliza. En particular, la attachedpalabra clave permite una "adjunción local" (eg l_attribute), que tiene como ámbito únicamente el bloque de código incluido en la construcción de la declaración if. Por lo tanto, dentro de este pequeño bloque de código, se puede garantizar de forma estática que la variable local (eg l_attribute) no sea nula (es decir, que sea segura para nulos).

Características: 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. Hay 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 comandos y consultas es importante para el método Eiffel. En particular:

Sobrecarga

Eiffel no permite la sobrecarga de argumentos . Cada nombre de característica dentro de una clase siempre se asigna a una característica específica dentro de la clase. Un nombre, dentro de una clase, significa una cosa. Esta elección de diseño ayuda a la legibilidad de las clases, al evitar una causa de ambigüedad sobre qué rutina será invocada por una llamada. También simplifica el mecanismo del lenguaje; en particular, esto es lo que hace posible el mecanismo de herencia múltiple de Eiffel. [12]

Por supuesto, los nombres se pueden reutilizar en diferentes clases. Por ejemplo, la característica plus (junto con su alias infijo "+" ) se define 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 CUENTA [SAVINGS] y CUENTA [CHECKING], etc.). Las clases pueden ser genéricas, para expresar 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 el "argumento" para las rutinas y utiliza el "parámetro" sólo para las clases genéricas). Con una declaración de este tipo, 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 ... fin         

Los 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 . elemento wl . poner ( 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, haber 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 herencia

Para heredar de una o más clases, una clase incluirá una inheritcláusula al comienzo:

La clase C hereda A y B    -- ... Resto de la declaración de clase ...

La clase puede redefinir (anular) algunas o todas las características heredadas. Esto debe anunciarse explícitamente al comienzo de la clase mediante una redefinesubcláusula de la cláusula de herencia, como en

clase C hereda A redefine f , g , h fin B redefine u , v fin             

Véase [13] para una discusión completa de la herencia de Eiffel.

Clases diferidas y características

Las clases se pueden definir con deferred classen lugar de con classpara indicar que la clase no se puede instanciar directamente. Las clases no instanciables se denominan clases abstractas en algunos otros lenguajes de programación orientados a objetos. En el lenguaje de Eiffel, solo se puede instanciar una clase "efectiva" (puede ser descendiente de una clase diferida). Una característica también se puede diferir utilizando la deferredpalabra clave en lugar de una docláusula. Si una clase tiene alguna característica diferida, debe declararse como diferida; sin embargo, una clase sin características diferidas puede, no obstante, diferirse.

Las clases diferidas cumplen algunas de las mismas funciones que las interfaces en lenguajes como Java, aunque muchos teóricos de la programación orientada a objetos creen que las interfaces son en sí mismas en gran medida una respuesta a la falta de herencia múltiple de Java (que Eiffel tiene). [14] [15]

Cambio de nombre

Una clase que hereda de una o más clases obtiene 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 hay conflictos de nombres entre las características heredadas; sin el cambio de nombre, la clase resultante violaría el principio de no sobrecarga mencionado anteriormente y, por lo tanto, no sería válida.

Tuplas

Los tipos de tuplas pueden considerarse como una forma simple de clase, que proporciona solo atributos y el procedimiento "establecedor" 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 una tupla de este tipo es simplemente una secuencia de valores con los tipos dados, entre paréntesis, como

 [ "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 asignador (ver más abajo), la notación de puntos también se puede utilizar para asignar componentes de dicha tupla, como en

 t . peso := t . peso + 0,5    

Las 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 primeros tres de los 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 "agente" de Eiffel envuelve las operaciones en objetos. Este mecanismo se puede utilizar para iteraciones, programación basada en eventos y otros contextos en los que resulta útil pasar operaciones por la estructura del programa. Otros lenguajes de programación, especialmente los que enfatizan la programación funcional , permiten un patrón similar utilizando continuaciones , cierres o generadores ; los agentes de Eiffel enfatizan el paradigma orientado a objetos del lenguaje y utilizan una sintaxis y una semántica similares a los bloques de código de Smalltalk y Ruby .

Por ejemplo, para ejecutar el my_actionbloque para cada elemento de my_list, uno escribiría:

 mi_lista.do_all ( agente mi_acción )  

Para ejecutar my_actionsolo en elementos que satisfagan my_condition, se puede agregar una limitación/filtro:

 mi_lista .do_if ( agente mi_acción , agente mi_condición )    

En estos ejemplos, my_actiony my_conditionson rutinas. Si se les antepone agentun objeto, se obtiene un objeto que representa la rutina correspondiente con todas sus propiedades, en particular la capacidad de ser llamada con los argumentos adecuados. Por lo tanto, 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 abiertos algunos argumentos para un agente y hacer que otros estén cerrados . 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

 my_list .do_all ( 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. Tenga en cuenta que la sintaxis básica agent fes una abreviatura de agent f (?, ?, ...)con todos los argumentos abiertos. También es posible hacer que el objetivo de un agente sea abierto mediante la notación {T}?donde Tes el tipo del objetivo.

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 ejecutada sobre los operandos cerrados.

El mecanismo del agente 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

my_list .do_all ( agente ( s : STRING ) requiere not_void : s /= Void hacer s .append_character ( ' , ' ) asegurar que se agregue : s .count = old s .count + 1 fin )                    

El agente en línea que se pasa aquí puede tener todos los elementos de una rutina normal, incluida la condición previa, la condición posterior, la cláusula de rescate (no se utiliza aquí) y una firma completa. Esto evita definir rutinas cuando todo lo que se necesita es un cálculo que se envuelva en un agente. Esto es útil en particular 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 ): BOOLEAN 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 condición previa 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é utilizando la oncepalabra clave en lugar de do. Las llamadas que no son las primeras 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 únicas" 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 creado Result . make ( args ) -- Esto crea el objeto y devuelve una referencia a él a través de 'Result'. end       

El objeto devuelto ( Resulten el ejemplo) puede ser mutable, pero su referencia sigue siendo la misma.

A menudo, las "rutinas de una sola vez" realizan una inicialización requerida: varias llamadas a una biblioteca pueden incluir una llamada al procedimiento de inicialización, pero solo la primera de esas llamadas realizará las acciones requeridas. Al usar este patrón, la inicialización se puede descentralizar, lo que evita la necesidad de un módulo de inicialización especial. 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.

De manera predeterminada, una "rutina única" se llama una vez por subproceso . 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 realizar conversiones entre varios tipos. Este mecanismo coexiste con la herencia y la complementa. Para evitar cualquier confusión entre ambos mecanismos, el diseño aplica el siguiente principio:

(Principio de conversión) Un tipo no puede al mismo tiempo conformarse y convertirse en otro.

Por ejemplo, NEWSPAPERpuede ajustarse a PUBLICATION, pero INTEGERse convierte a REAL(y no hereda de él).

El mecanismo de conversión simplemente generaliza las reglas de conversión ad hoc (como indeed entre INTEGERy REAL) que existen en la mayoría de los lenguajes de programación, lo que las hace aplicables a cualquier tipo siempre que se respete el principio anterior. Por ejemplo, DATEse puede declarar una clase para convertir a STRING; esto hace posible crear una cadena a partir de una fecha simplemente a través de

 mi_cadena := mi_fecha  

como atajo para utilizar una creación de objeto explícita con un procedimiento de conversión:

 crear mi_cadena .make_from_date ( 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 comienzo de la clase.

Como otro ejemplo, si existe un procedimiento de conversión de este tipo enumerado en TUPLE [day: INTEGER; month: STRING; year: INTEGER], entonces se puede asignar directamente una tupla a una fecha, lo que provoca la conversión adecuada, como en

 Día de la Bastilla := [ 14 , "julio" , 1789 ]    

Manejo de excepciones

El manejo de excepciones en Eiffel se basa en los principios del diseño por contrato. Por ejemplo, una excepción ocurre cuando el llamador de una rutina no cumple una condición previa o cuando una rutina no puede garantizar una condición posterior prometida. En Eiffel, el manejo de excepciones no se utiliza para controlar el flujo ni para corregir errores en la entrada de datos.

Un controlador de excepciones de 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 la cantidad de intentos de ejecución de la rutina y solo vuelve a intentarlo una cierta cantidad de veces:

connect_to_server ( servidor : SOCKET ) - Conectarse a un servidor o darse por vencido después de 10 intentos. require server /= Void y luego server . address /= Void local attempts : INTEGER hacer server . connect asegurar conectado : server . is_connected rescatar si intentos < 10 entonces intentos := intentos + 1 reintento fin fin                                  

Sin embargo, este ejemplo es indiscutiblemente defectuoso para cualquier programa que no sea el más simple, ya que es de esperar que falle la conexión. Para la mayoría de los programas, un nombre de rutina como attempt_connecting_to_server sería mejor y la condición posterior no prometería una conexión, dejando en manos del llamador la tarea de tomar las medidas adecuadas si la conexión no se abriera.

Concurrencia

Hay disponibles 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 asincrónicas pueden ser problemáticas (cuando una rutina lanza una excepción después de que su llamador ha terminado). [18]

Sintaxis de operadores y corchetes, comandos de asignación

La visión de Eiffel sobre la computación está completamente orientada a objetos en el sentido de que cada operación es relativa a un objeto, el "objetivo". Así, por ejemplo, una suma como

a + b  

se 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 del operador permite utilizar 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 utilizar en cualquier otra para la que dicho operador sea apropiado):

más alias "+" ( otro : INTEGER ): INTEGER -- ... Declaración de función normal... fin       

La gama de operadores que se pueden utilizar como "alias" es bastante amplia; incluye operadores predefinidos como "+" pero también "operadores libres" compuestos por símbolos no alfanuméricos. Esto permite diseñar notaciones especiales de infijo y prefijo, por ejemplo en aplicaciones de matemáticas y física.

Cada clase puede tener además una función con alias "[]", el operador "corchete", lo que permite la notación a [i, ...]como sinónimo de a.f (i, ...)donde festá la función elegida. Esto es particularmente útil para estructuras contenedoras 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 := libreta 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 conveniente y bien establecida reinterpretada en el marco de la programación orientada a objetos. Los comandos de asignación permiten que una sintaxis similar a la asignación llame a procedimientos "setter". Una asignación propiamente dicha nunca puede tener la forma a.x := vya que esto viola el ocultamiento de información; debe optar por un comando setter (procedimiento). Por ejemplo, la clase de tabla hash puede tener la función y el procedimiento

alias de elemento "[]" ( clave : STRING ): ELEMENT [ 3 ] -- El elemento de la clave `key'. -- (consulta "Getter") hacer ... fin           put ( e : ELEMENTO ; clave : CADENA ) -- Inserta el elemento `e', asociándolo con la clave `key'. -- (comando "Setter") do ... end         

Luego, para insertar un elemento, debes utilizar una llamada explícita al comando setter:

 [ 4 ] agenda_telefonica .put ( Nueva_persona , " JILL SMITH" )   

Es posible escribir esto de manera equivalente como

 [ 5 ] libreta telefónica [ "JILL SMITH" ] := Nueva persona    

(de la misma manera que phone_book ["JILL SMITH"]es sinónimo de number := phone_book.item ("JILL SMITH")), siempre que la declaración de itemahora comience (reemplazo de [3]) con

 alias de elemento "[]" ( clave : CADENA ): ELEMENTO asignar poner       

Esto se declara putcomo el comando asignador asociado con itemy, combinado con el alias de corchete, hace que [5] sea legal y equivalente a [4]. (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 ser: (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. No obstante, consulte las "reglas de estilo" que aparecen a continuación.

Los comentarios se introducen con --(dos guiones consecutivos) y se extienden hasta el final de la línea.

El punto y coma, como separador de instrucciones, es opcional. La mayoría de las veces, el punto y coma simplemente se omite, excepto para separar varias instrucciones en una línea. Esto genera menos desorden en la página del programa.

No existe anidación de declaraciones de características y clases. Como resultado, la estructura de una clase Eiffel es simple: algunas cláusulas de nivel de clase (herencia, invariante) y una sucesión de declaraciones de características, todas en el mismo nivel.

Es habitual agrupar las características en "cláusulas de características" independientes para facilitar la lectura, 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 de creación/constructores) ...   característica -- Acceso -- ... Declaraciones de consultas no booleanas sobre el estado del objeto, p. ej. 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 cambian la estructura, p. ej. poner ...   -- etc. fin

A diferencia de la mayoría de los lenguajes de programación que utilizan llaves , Eiffel hace una distinción clara entre expresiones e instrucciones. Esto está en línea 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 estas convenciones son posibles.

Si bien el lenguaje no distingue entre mayúsculas y minúsculas, los estándares 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úsculas iniciales 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 pautas para mostrar textos de software en formatos de composición tipográfica: las palabras clave en negrita, los identificadores y constantes definidos por el usuario se muestran en italics, los comentarios, operadores y signos de puntuación en Roman, y 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 de la siguiente manera en la documentación de Eiffel:

clase HOLA_MUNDO crear hacer función hacer imprimir ( "¡Hola , mundo!" ) fin fin       

Interfaces 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.

Es posible, por ejemplo, programar operaciones a nivel de máquina y de sistema operativo en C. Eiffel proporciona una interfaz sencilla para rutinas de C, incluido soporte para "C en línea" (escribir el cuerpo de una rutina de Eiffel en C, generalmente para operaciones cortas a nivel de máquina).

Aunque no existe una conexión directa entre Eiffel y C, muchos compiladores de Eiffel ( Visual Eiffel es una excepción) generan código fuente de C como lenguaje intermedio , para enviarlo a un compilador de C, para su optimización y portabilidad . Como tales, son ejemplos de transcompiladores . El compilador Eiffel tecomp puede ejecutar código de Eiffel directamente (como un intérprete) sin pasar por un código C intermedio o emitir código C que se pasará a un compilador de C para obtener código nativo optimizado. En .NET, el compilador EiffelStudio genera directamente código CIL (Common Intermediate Language). El compilador SmartEiffel también puede generar código de bytes de Java .

Referencias

  1. ^ "Eiffel en pocas palabras". archive.eiffel.com . Consultado el 24 de agosto de 2017 .
  2. ^ "EiffelStudio 24.05 ya está disponible". Eiffel.org . 14 de junio de 2024.
  3. ^ Cooper, Peter (2009). Beginning Ruby: From Novice to Professional (El comienzo de Ruby: de principiante a profesional). Beginning from Novice to Professional (2.ª ed.). Berkeley: APress. pág. 101. ISBN 978-1-4302-2363-4En menor medida , Python, LISP, Eiffel, Ada y C++ también han influido en Ruby.
  4. ^ "Eiffel – el lenguaje". berenddeboer.net . Consultado el 6 de julio de 2016 .
  5. ^ Meyer, Bertrand (28 de agosto de 2009). Touch of Class: Cómo aprender a programar bien con objetos y contratos. Springer Science & Business Media. ISBN 978-3-540-92144-8.
  6. ^ "Lenguajes de programación - Eiffel" (PDF) . Departamento de Ciencias de la Computación, Virginia Tech . Consultado el 25 de marzo de 2023 .
  7. ^ Carl Friess. "Guía de sintaxis de Eiffel". Guía de sintaxis de Eiffel . Consultado el 25 de marzo de 2023 .
  8. ^ Claus Brabrand. "El lenguaje de programación EIFFEL" (PDF) . Universidad de Tecnología de la Información de Copenhague . Consultado el 25 de marzo de 2023 .
  9. ^ Construcción de software orientado a objetos , segunda edición, por Bertrand Meyer , Prentice Hall, 1997, ISBN 0-13-629155-4 
  10. ^ ECMA International: Estándar 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/
  11. ^ Organización Internacional de Normalización: Norma ISO/IEC DIS 25436, disponible en línea en [1]
  12. ^ Bertrand Meyer: Sobrecarga frente a tecnología de objetos, en Journal of Object-Oriented Programming (JOOP), vol. 14, núm. 4, octubre-noviembre de 2001, disponible en línea
  13. ^ "9 HERENCIA". Archive.eiffel.com. 23 de marzo de 1997. Consultado el 8 de julio de 2013 .
  14. ^ "Herencia múltiple e interfaces". Artima.com. 16 de diciembre de 2002. Consultado el 8 de julio de 2013 .
  15. ^ "La herencia múltiple no es mala". C2.com. 28 de abril de 2007. Consultado el 8 de julio de 2013 .
  16. ^ Philippe Ribet, Cyril Adrian, Olivier Zendra, Dominique Colnet: Conformidad de los agentes en el lenguaje Eiffel , en Journal of Object Technology , vol. 3, núm. 4, abril de 2004, número especial: TOOLS USA 2003, pp. 125-143. Disponible en línea desde la página del artículo de JOT
  17. ^ Brooke, Phillip; Richard Paige (2008). "Cameo: un modelo alternativo de concurrencia para Eiffel" (PDF) . Aspectos formales de la informática . 21 (4). Springer: 363–391. doi :10.1007/s00165-008-0096-1. S2CID  18336088.
  18. ^ Brooke, Phillip; Richard Paige (2007). "Excepciones en Eiffel concurrente". Revista de tecnología de objetos . 6 (10): 111–126. doi : 10.5381/jot.2007.6.10.a4 .

Enlaces externos