Eiffel (lenguaje de programación)

format_list_bulleted Contenido keyboard_arrow_down
ImprimirCitar
Lenguaje de programación orientado a objetos

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 Construcción de software orientada a objetos) y Eiffel Software. Meyer concibió el lenguaje en 1985 con el objetivo de aumentar la confiabilidad del desarrollo de software comercial; la primera versión estuvo disponible en 1986. En 2005, Eiffel se convirtió en un lenguaje estandarizado por 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, incluido el diseño por contrato, la separación de comando-consulta, el principio de acceso uniforme, el principio de elección única, el principio abierto-cerrado y la separación opción-operando.

Muchos conceptos introducidos inicialmente por Eiffel luego llegaron a Java, C# y otros lenguajes. Las 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:

  • Una estructura programática orientada al objeto en la que una clase sirve como unidad básica de descomposición.
  • Diseño por contrato estrechamente integrado con otros constructos de lenguaje.
  • Gestión automática de la memoria, normalmente implementada por la recolección de basura.
  • Herencia, incluyendo múltiples herencias, renombramiento, redefinición, "selección", herencia no conforme, y otros mecanismos destinados a hacer la herencia segura.
  • Programación genérica con capacitación y sin restricciones
  • Un sistema de tipo uniforme que maneja tanto el valor como la semántica de referencia en la que todos los tipos, incluyendo tipos básicos como INTEGER, están basados en clases.
  • Tipología estatica
  • Seguridad voida, o protección estática contra las llamadas a referencias nulas, a través del mecanismo de tipos adjuntos.
  • Agentes, o objetos que envuelven computaciones, estrechamente relacionados con cierres y cálculo de lambda.
  • Una vez rutinas, o rutinas evaluadas sólo una vez, para compartir objetos e inicialización descentralizada.
  • Sintaxis basada en palabras clave en la tradición ALGOL/Pascal pero libre de separadores, en la medida en que los ymicolons son opcionales, con sintaxis de operador disponible para rutinas.
  • Caso de insensibilidad
  • Programación simple orientada al objeto (SCOOP) facilita la creación de vehículos de ejecución múltiples, simultáneamente activos a un nivel de abstracción por encima de los detalles específicos de estos vehículos (por ejemplo, múltiples hilos sin una gestión específica del mutex).

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 los trucos de codificación o las técnicas de codificación concebidas como sugerencias de optimización para el compilador. El objetivo no es solo hacer que el código sea más legible, sino también permitir que los programadores se concentren en los aspectos importantes de un programa sin atascarse en los 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 de programas informáticos escritos en Eiffel proporcionan amplias técnicas de optimización, como la integración automática, que liberan al programador de parte de la carga de optimización.

Antecedentes

Eiffel fue desarrollado originalmente por Eiffel Software, una empresa fundada por Bertrand Meyer. Construcción de software orientada a objetos contiene un tratamiento detallado de los conceptos y la teoría de la tecnología de objetos que condujo al diseño de Eiffel.

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 fiables y reutilizables. Eiffel admite herencia múltiple, genericidad, polimorfismo, encapsulación, conversiones seguras 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 afirmaciones, condiciones previas, condiciones posteriores 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 la preocupación por la compatibilidad con el 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 datos abstractos".

Implementaciones y entornos

EiffelStudio es un entorno de desarrollo integrado disponible bajo una licencia comercial o de código abierto. 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 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, basado 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 ha divergido y ahora incluye varias funciones de programación funcional. El lenguaje interactivo de enseñanza Blue, precursor de BlueJ, también se basa en Eiffel. Apple Media Tool incluye un Apple Media Language basado en Eiffel.

Especificaciones y estándares

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 utilizar de forma gratuita en el sitio de ECMA. La versión ISO es idéntica en todos los aspectos excepto en el formato.

Software Eiffel, "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 "The Eiffel Compiler" tecomp implementa algunos de los principales mecanismos nuevos, en particular, agentes en línea, comandos de asignador, notación de paréntesis, 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 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 lugar entre el lenguaje SmartEiffel y el estándar.

El estándar cita las siguientes especificaciones del lenguaje Eiffel predecesor:

  • Bertrand Meyer: Eiffel: The Language, Prentice Hall, segunda impresión, 1992 (primera impresión: 1991)
  • Bertrand Meyer: Standard Eiffel (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 orientada a objetos, Prentice Hall: primera edición, 1988; segunda edición, 1997.
  • Bertrand Meyer: Touch of Class: Learning to Program Well with Objects and Contracts, Springer-Verlag, 2009 ISBN 978-3-540-92144-8 lxiv + 876 páginas Impresión de color completo, numerosas fotografías de color

La versión actual del estándar de junio de 2006 contiene algunas inconsistencias (por ejemplo, redefiniciones covariantes). El comité de 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" de Eiffel o "programa" 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 clústeres no son una construcción del lenguaje sintáctico, sino una convención organizativa estándar. Por lo general, un programa Eiffel se organizará con cada clase en un archivo separado y cada grupo en un directorio que contiene archivos de clase. En esta organización, los subclusters son subdirectorios. Por ejemplo, según las convenciones estándar de organización y mayúsculas y minúsculas, x.e podría ser el nombre de un archivo que define una clase llamada X.

Una clase contiene características, que son similares a "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 "notas" sección de documentación y metadatos. Los tipos de datos estándar de Eiffel, como INTEGER, STRING y ARRAY, son en sí mismos clases.

Todo sistema debe tener una clase designada como "raíz", con uno de sus procedimientos de creación designado como "procedimiento raíz". Ejecutar un sistema consiste en crear una instancia de la clase raíz y ejecutar su procedimiento raíz. Generalmente, hacerlo crea nuevos objetos, llama a nuevas funciones, etc.

Eiffel tiene cinco instrucciones ejecutables básicas: asignación, creación de objetos, llamada de rutina, condición e iteración. Las estructuras de control de Eiffel son estrictas a la hora de hacer cumplir 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 a atributos de objetos, excepto dentro de las características de un objeto, que es la aplicación práctica del principio de ocultación de información o abstracción de datos, que requiere interfaces 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 "establecedores" son necesarios para que los objetos del cliente modifiquen los valores. Un resultado de esto es que "setters" puede, y normalmente lo hace, 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:

  algunos_atributos: Alguna cosa. c) set_some_attribute  set_some_attribute ()v: VALUE_TYPE) -- Establece valor de algunos_atributos a `v'. do algunos_atributos := v final

Si bien es una leve reverencia a la comunidad de desarrolladores en general para permitir algo que parezca acceso directo (por ejemplo, rompiendo así el principio de ocultación de información), la práctica es peligrosa ya que oculta u ofusca la realidad de un "establecedor" siendo utilizado. En la práctica, es mejor redirigir la llamada a un setter en lugar de implicar un acceso directo a una función como some_attribute como en el código de ejemplo anterior.

A diferencia de otros idiomas, al tener nociones de "público", "protegido", "privado" y así sucesivamente, 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 la función se comprueba estáticamente en tiempo de compilación. Por ejemplo, (abajo), el "{NONE}" es similar a "protegido" en otros idiomas. Ámbito aplicado de esta manera a un "conjunto de características" (por ejemplo, todo lo que está debajo de la palabra clave 'función' a la siguiente palabra clave del conjunto de funciones o al final de la clase) se puede cambiar en las clases descendientes usando la opción "exportar" palabra clave.

función {}NINGUNA} - Iniciacióndefault_create- Iniciar una nueva instancia decimal "cero".domake_zerofinal

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

función Constantes

Finalmente, el alcance se puede controlar de forma selectiva y precisa para cualquier clase en el universo del proyecto Eiffel, como por ejemplo:

función {}DECIMAL, DCM_MA_DECIMAL_PARSER, DCM_MA_DECIMAL_HANDLER} - Acceso

Aquí, el compilador permitirá que solo las clases enumeradas entre llaves accedan a las funciones dentro del grupo de funciones (por ejemplo, DECIMAL, DCM_MA_DECIMAL_PARSER, DCM_MA_DECIMAL_HANDLER).

"¡Hola, mundo!"

La apariencia de un lenguaje de programación a menudo se transmite mediante un "¡Hola, mundo!" programa. Tal programa escrito en Eiffel podría ser:

clase HELLO_WORLDcrear hacerfunción hacer do impresión ()¡Hola, mundo!) finalfinal

Este programa contiene la clase HELLO_WORLD. El constructor (crear rutina) para la clase, llamado make, invoca la rutina de la biblioteca del sistema print para escribir un "Hola, world!" mensaje a la salida.

Diseño por contrato

El concepto de Diseño por Contrato es fundamental para Eiffel. Los contratos afirman lo que debe ser cierto antes de que se ejecute una rutina (condición previa) y lo que debe ser cierto después de que finaliza la rutina (condición posterior). Los contratos invariantes de clase definen qué aserciones 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 la clase en su conjunto por medio del invariante.

El compilador Eiffel está diseñado para incluir los contratos de funciones y clases en varios niveles. EiffelStudio, por ejemplo, ejecuta todos los contratos de características y clases 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, archivo ECF) para incluir o excluir cualquier conjunto de contratos. Por lo tanto, se puede compilar un archivo ejecutable para incluir o excluir cualquier nivel de contrato, trayendo así niveles continuos de pruebas unitarias y de integración. Además, los contratos se pueden ejercer de forma continua y metódica a través de la función Auto-Test 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 funciones en la herencia:

  • Condiciones previas de rutina: La condición previa sólo puede ser debilitada por herencia; cualquier llamada que satisfaga los requisitos del ancestro cumple con los del descendiente.
  • Condiciones postales de rutina: La condición previa sólo puede fortalecerse por herencia; cualquier resultado garantizado por el ancestro sigue siendo proporcionado por el descendiente.
  • Clase invariante: Condiciones que deben mantenerse fieles después de la creación del objeto y después de cualquier llamada a una rutina de clase exportada. Debido a que el invariante se controla tan a menudo, lo hace simultáneamente la forma más cara y más poderosa de condición o contrato.

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

Capacidad de seguridad del vacío

La capacidad de protección contra vacíos, como la escritura estática, es otra función para mejorar la calidad del software. El software seguro para anular está protegido contra errores de tiempo de ejecución causados por llamadas para anular referencias y, por lo tanto, será más confiable que el software en el que pueden ocurrir llamadas para anular objetivos. La analogía con la escritura estática es útil. De hecho, la capacidad de protección contra 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 contra vacíos está integrado en el sistema de tipos.

La protección contra las llamadas de objetivos vacíos se puede ver a través de la noción de apego y (por extensión) desapego (por ejemplo, palabra clave separable). La instalación void-safe se puede ver en una breve reelaboración del código de ejemplo utilizado anteriormente:

 algunos_atributos: desmontable Alguna cosa.  use_some_attribute -- Establece valor de algunos_atributos a `v'. do si adjunto algunos_atributos como l_attribute entonces algo ()l_attribute) final final  algo ()a_value: Alguna cosa.) Haz algo con "a_value". do ... haciendo algo con `a_value' ... final

El ejemplo de código anterior muestra cómo el compilador puede abordar estáticamente la confiabilidad de si some_attribute se adjuntará o separará en el punto en que se usa. En particular, la palabra clave attached permite un "archivo adjunto local" (p. ej., l_attribute), que tiene como ámbito solo el 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 nula (es decir, void segura).

Características: comandos y consultas

La característica principal de una clase es que define un conjunto de funciones: como una clase representa un conjunto de objetos de tiempo de ejecución, o "instancias", una función es una operación en 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 comando-consulta es importante para el método Eiffel. En particular:

  • Uniform-Access Principio: desde el punto de vista de un cliente de software haciendo una llamada a una característica de clase, si una consulta es un atributo (valor de campo) o una función (valor calculado) no debe hacer ninguna diferencia. Por ejemplo, a_vehicle.speed podría ser un atributo accedido al objeto a_vehicle, o podría ser computado por una función que divide la distancia por el tiempo. La notación es la misma en ambos casos, por lo que es fácil cambiar la implementación de la clase sin afectar el software cliente.
  • Separación de comandos y preguntas Principio: Las consultas no deben modificar la instancia. Esto no es una regla de lenguaje sino un principio metodológico. Así que en buen estilo Eiffel, uno no encuentra funciones "get" que cambian algo y devuelven un resultado; en cambio hay comandos (procedimientos) para cambiar objetos, y consultas para obtener información sobre el objeto, resultantes de cambios anteriores.

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 invocará 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.

Los nombres pueden, por supuesto, reutilizarse en diferentes clases. Por ejemplo, la función plus (junto con su alias infijo "+") se define en varios clases: INTEGER, REAL, STRING, etc.

Genericidad

Una clase genérica es una clase que varía según el tipo (por ejemplo, LISTA [TELÉFONO], una lista de números de teléfono; CUENTA [G->TIPO_CUENTA], que permite CUENTA [AHORROS] y CUENTA [CHEQUEO], 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 'argumento' para rutinas y usa 'parámetro' sólo para clases genéricas). Con tal declaración, G representa dentro de la clase un tipo arbitrario; por lo que una función puede devolver un valor de tipo G, y una rutina puede tomar un argumento de ese tipo:

Tema: G do ... final# ()x: G) do ... final

LIST [INTEGER] y LIST [WORD] son "derivaciones genéricas" de esta clase Combinaciones permitidas (con n: INTEGER, w: WORD, il: LIST [INTEGER], wl: LIST [WORD]) son:

n := il.Temawl.# ()w)

INTEGER y WORD son los "parámetros genéricos reales" en estas derivaciones genéricas.

También es posible tener 'restringido' parámetros formales, para los cuales el parámetro real debe heredar de una clase dada, la "restricción". por ejemplo, en

 clase HASH_TABLE [G, KEY - HASHABLE]

una derivación HASH_TABLE [INTEGER, STRING] solo es válida si STRING se hereda de HASHABLE (como sucede en las típicas bibliotecas de Eiffel). Dentro de la clase, tener KEY restringido por HASHABLE significa que para x: KEY es posible aplicar a x todas las características de HASHABLE, como en x.hash_code.

Conceptos básicos de herencia

Para heredar de uno o más, una clase incluirá una cláusula inherit al principio:

clase C herencia A B-... El resto de la declaración de clase...

La clase puede redefinir (anular) algunas o todas las funciones heredadas. Esto debe anunciarse explícitamente al comienzo de la clase a través de una subcláusula redefine de la cláusula de herencia, como en

clase C herencia A redefine f, g, h final B redefine u, v final

Vea para una discusión completa de la herencia de Eiffel.

Clases y características diferidas

Las clases se pueden definir con clase diferida en lugar de con clase para 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 la jerga de Eiffel, sólo un "efectivo" la clase puede ser instanciada (puede ser un descendiente de una clase diferida). Una característica también se puede diferir usando la palabra clave deferred en lugar de una cláusula do. Si una clase tiene características diferidas, debe declararse como diferida; sin embargo, una clase sin características diferidas puede no obstante ser diferida.

Las clases diferidas juegan el mismo papel que las interfaces en lenguajes como Java, aunque muchos teóricos de la programación orientada a objetos creen que las interfaces en sí mismas son en gran medida una respuesta a la falta de herencia múltiple de Java (que tiene Eiffel).

Renombrar

Una clase que hereda de una o más obtiene todas sus características, por defecto bajo sus nombres originales. No obstante, podrá cambiar sus nombres mediante cláusulas rename. Esto es necesario en el caso de herencia múltiple si hay conflictos de nombres entre características heredadas; sin cambiar el 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 verse como una forma simple de clase, proporcionando solo atributos y el correspondiente "setter" procedimiento. Un tipo típico de tupla dice

 TUPLE [Nombre: ESTADO; 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 tal tupla es simplemente una secuencia de valores con los tipos dados, dados entre paréntesis, como

 ["Brigitte", 3.5, Last_night]

Se puede acceder a los componentes de dicha tupla como si las etiquetas de la tupla fueran atributos de una clase, por ejemplo, si a t se le ha asignado la tupla anterior, entonces t.weight tiene valor 3.5.

Gracias a la noción de comando asignador (ver más abajo), la notación de puntos también se puede usar para asignar componentes de una tupla de este tipo, 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, p. TUPLE [A, B, C] es que describe secuencias de al menos tres elementos, siendo los tres primeros de tipo A, B, C respectivamente. 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 "agente" de Eiffel mecanismo envuelve operaciones en objetos. Este mecanismo se puede usar para la iteración, la programación dirigida por eventos y otros contextos en los que es útil pasar operaciones alrededor de la estructura del programa. Otros lenguajes de programación, especialmente los que enfatizan la programación funcional, permiten un patrón similar usando continuaciones, cierres o generadores; Los agentes de Eiffel enfatizan el paradigma orientado a objetos del lenguaje y usan una sintaxis y una semántica similar a los bloques de código en Smalltalk y Ruby.

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

 my_list.Do_all ()Agente mi_acción)

Para ejecutar my_action solo en elementos que cumplan con my_condition, se puede agregar una limitación/filtro:

 my_list.do_if ()Agente mi_acción, Agente my_condition)

En estos ejemplos, my_action y my_condition son rutinas. Prefijarlos con agent produce un objeto que representa la rutina correspondiente con todas sus propiedades, en particular la capacidad de ser llamado con los argumentos apropiados. Entonces, si a representa ese objeto (por ejemplo, porque a es el argumento de do_all), la instrucción

 a.llamada ()[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 para call se pasan como una tupla, aquí [x].

Es posible mantener algunos argumentos para un agente abiertos y hacer otros 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 action2 tiene dos argumentos, la iteración

 my_list.Do_all ()Agente Medida 2 ()?, Sí.)

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

La distinción entre operandos abiertos y cerrados (operandos = argumentos + objetivo) corresponde a la distinción entre variables limitadas y libres en el cálculo lambda. Una expresión de agente como action2 (?, y) con algunos operandos cerrados y algunos abiertos corresponde a una versión de la operación original curred en 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), mediante agentes como en

my_list.Do_all ()Agente ()s: ESTADO) necesidad No.: s /= Vacío do s.append_character ()',') asegurar apéndice: s.Cuenta = viejo s.Cuenta + 1 final)

El agente en línea que se pasa aquí puede tener todas las características de una rutina normal, incluida la condición previa, la condición posterior, la cláusula de rescate (no se usa aquí) y una firma completa. Esto evita definir rutinas cuando todo lo que se necesita es un cálculo para envolver en un agente. Esto es útil en particular para los contratos, como en una cláusula invariable que expresa que todos los elementos de una lista son positivos:

 my_list.Para todos ()Agente ()x: INTEGER): BOOLEAN do Resultado := ()x  0) final)

El mecanismo actual del agente deja una posibilidad de 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 verificación en tiempo de ejecución a través de la condición previa valid_arguments de call. Hay disponibles varias propuestas para una corrección puramente estática de este problema, incluida una propuesta de cambio de lenguaje de Ribet et al.

Rutinas únicas

El resultado de una rutina se puede almacenar en caché utilizando la palabra clave once en lugar de do. Las llamadas que no son primeras a una rutina no requieren cálculo adicional ni asignación de recursos, sino que simplemente devuelven un resultado calculado previamente. Un patrón común para "funciones una vez" es proporcionar objetos compartidos; la primera llamada creará el objeto, las siguientes devolverán la referencia a ese objeto. El esquema típico es:

shared_object: Alguna cosa. una vez crear Resultado.hacer ()args) -- Esto crea el objeto y devuelve una referencia a él a través del `Resultado'. final

El objeto devuelto, Result en el ejemplo, puede ser mutable, pero su referencia sigue siendo la misma.

A menudo "rutinas de una vez" realizar una inicialización requerida: múltiples llamadas a una biblioteca pueden incluir una llamada al procedimiento de inicialización, pero solo la primera llamada realizará las acciones requeridas. El uso de este patrón de inicialización se puede descentralizar, evitando la necesidad de un módulo de inicialización especial. "Rutinas de una vez" son similares en propósito y efecto al patrón singleton en muchos lenguajes de programación y al patrón Borg usado en Python.

De forma 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ándolo con una "clave de una vez", p. una vez ("PROCESO").

Conversiones

Eiffel proporciona un mecanismo para permitir conversiones entre varios tipos. El mecanismo coexiste con la herencia y la complementa. Para evitar cualquier confusión entre los dos mecanismos, el diseño aplica el siguiente principio:

(Principio de conversión) Un tipo puede no conformarse y convertirse a otro.

Por ejemplo, PERIÓDICO puede ajustarse a PUBLICACIÓN, pero INTEGER se convierte a REAL (y no hereda de eso).

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

 My_string := my_date

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

 crear My_string.make_from_date ()my_date)

Para que la primera forma sea posible como sinónimo de la segunda, basta con enumerar el procedimiento de creación (constructor) make_from_date en una cláusula convert al comienzo de la clase.

Como otro ejemplo, si existe un procedimiento de conversión de este tipo en la lista de TUPLE [day: INTEGER; mes: CADENA; year: INTEGER], entonces uno puede asignar directamente una tupla a una fecha, causando la conversión apropiada, como en

 Bastille_day := [14, "Julio", 1789]

Manejo de excepciones

El manejo de excepciones en Eiffel se basa en los principios de diseño por contrato. Por ejemplo, se produce una excepción cuando la persona que llama a 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 usa para controlar el flujo o para corregir errores de entrada de datos.

Un manejador de excepciones Eiffel se define usando la palabra clave rescue. Dentro de la sección rescue, la palabra clave retry ejecuta la rutina nuevamente. Por ejemplo, la siguiente rutina realiza un seguimiento del número de intentos de ejecución de la rutina y solo vuelve a intentarlo un cierto número de veces:

connect_to_server ()servidor: SOCKET) -- Conéctate a un servidor o renuncia después de 10 intentos. necesidad servidor /= Vacío y entonces servidor.dirección /= Vacío local intentos: INTEGER do servidor.conectar asegurar conectado: servidor.is_connected rescate si intentos . 10 entonces intentos := intentos + 1 retry final final

Sin embargo, podría decirse que este ejemplo es defectuoso para todo menos para los programas más simples, porque 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 que la persona que llama tome las medidas adecuadas si la conexión no se abrió.

Concurrencia

Hay disponibles varias bibliotecas de creación 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, o Simple Concurrent Object-Oriented Programming, que aún no forma parte de la definición del lenguaje oficial pero está disponible en EiffelStudio. CAMEO 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 genera una excepción después de que la persona que llama ha terminado).

Sintaxis de operador y paréntesis, comandos de asignador

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

a + b

se entiende conceptualmente como si fuera la llamada al método

a.más ()b)

con objetivo a, función plus y argumento b.

Por supuesto, la primera es la sintaxis convencional y generalmente la preferida. La sintaxis del operador hace posible usar cualquiera de las dos formas al declarar la función (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):

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

La variedad de operadores que se pueden usar como "alias" es bastante amplio; incluyen operadores predefinidos como "+" pero también "operadores libres" hecho de símbolos no alfanuméricos. Esto hace posible diseñar notaciones especiales de infijos y prefijos, por ejemplo, en aplicaciones de matemáticas y física.

Cada clase puede tener, además, una función asociada a "[]", el "bracket" operador, permitiendo la notación a [i,...] como sinónimo de a.f (i,...) donde f es el función elegida. Esto es particularmente útil para estructuras de contenedores como matrices, tablas hash, listas, etc. Por ejemplo, se puede escribir el acceso a un elemento de una tabla hash con claves de cadena

 Número := phone_book ["JILL SMITH"]

"Asignador de comandos" 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 del asignador permiten una sintaxis similar a la asignación para llamar a "setter" procedimientos. Una asignación propiamente dicha nunca puede tener la forma a.x:= v ya que viola la ocultación de información; tienes que ir por un comando setter (procedimiento). Por ejemplo, la clase de tabla hash puede tener la función y el procedimiento

Tema alias "[]" ()clave: ESTADO): ELEMENTO [3] -- El elemento clave de llave. - ¿Qué? do ... final# ()e: ELEMENTO; clave: ESTADO) -- Inserte el elemento `e', asociandolo con la clave 'key'. -- ("Setter" comando) do ... final

Luego, para insertar un elemento, debe usar una llamada explícita al comando setter:

 [4] phone_book.# ()New_person, "JILL SMITH")

Es posible escribir esto de manera equivalente como

 [5] phone_book ["JILL SMITH"] := New_person

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

 Tema alias "[]" ()clave: ESTADO): ELEMENTO c) #

Esto declara put como el comando asignador asociado con item y, combinado con el alias entre paréntesis, 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á limitada 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, maKe y MAKE denotan el mismo identificador. Consulte, sin embargo, las "reglas de estilo" abajo.

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 se omite el punto y coma, excepto para separar varias instrucciones en una línea. Esto da como resultado menos desorden en la página del programa.

No hay anidamiento 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, todo al mismo nivel.

Es habitual agrupar funciones en "cláusulas de funciones" para una mayor legibilidad, con un conjunto estándar de etiquetas de funciones básicas que aparecen en un orden estándar, por ejemplo:

clase HASH_TABLE [ELEMENTO, KEY - HASHABLE] herencia CUADRO [ELEMENTO] función - Iniciación -... Declaraciones de comandos de inicialización (procedimientos de creación/constructores)... función - Acceso -... Declaraciones de consultas no-boolean sobre el estado del objeto, por ejemplo... función - Informe sobre la situación -... Declaraciones de consultas booleanas sobre el estado del objeto, por ejemplo is_empty... función - Cambio de elemento -... Declaraciones de comandos que cambian la estructura, por ejemplo... - etc.final

A diferencia de la mayoría de los lenguajes de programación de llaves, Eiffel hace una distinción clara entre expresiones e instrucciones. Esto está en línea con el principio de Separación de Comando-Consulta del método Eiffel.

Convenciones de estilo

Gran parte de la documentación de Eiffel utiliza convenciones de estilo distintivas, diseñadas para imponer una apariencia uniforme. Algunas de estas convenciones se aplican al formato del código en sí mismo 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 constantes (Avogadro). El estilo recomendado también sugiere un guión bajo 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 tipográficos: las palabras clave en negrita, los identificadores definidos por el usuario y las constantes se muestran en cursiva, comentarios, operadores, y signos de puntuación en romano, con el texto del programa en azul como en el presente artículo para distinguirlo del texto explicativo. Por ejemplo, el mensaje "¡Hola, mundo!" El programa dado anteriormente se representaría como se muestra a continuación en la documentación de Eiffel:

clase HELLO_WORLDcrear hacerfunción hacer do impresión ()"¡Hola, mundo!") finalfinal

Interfaces a otras herramientas y lenguajes

Eiffel es un lenguaje puramente orientado a objetos, pero proporciona una arquitectura abierta para interactuar con aplicaciones "externas" software en cualquier otro lenguaje de programación.

Es posible, por ejemplo, programar operaciones a nivel de máquina y sistema operativo en C. Eiffel proporciona una interfaz sencilla para las rutinas de C, incluida la compatibilidad con "C en línea" (escribir el cuerpo de una rutina Eiffel en C, típicamente 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 el código fuente de C como lenguaje intermedio, para enviarlo a un compilador de C, para optimización y portabilidad. Como tales, 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 emitir 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 CIL (Common Intermediate Language). El compilador SmartEiffel también puede generar código de bytes de Java.

Más resultados...
Tamaño del texto:
  • Copiar
  • Editar
  • Resumir
undoredo
format_boldformat_italicformat_underlinedstrikethrough_ssuperscriptsubscriptlink
save