Eiffel (linguagem de programação)
![]() | |
Paradigma | Orientado a objetos , baseado em classe , genérico , concorrente |
---|---|
Projetado por | Bertrand Meyer |
Desenvolvedor | Software Eiffel |
Apareceu pela primeira vez | 1986 [1] |
Versão estável | EiffelStudio 20.11 [2]
/ 21 de dezembro de 2020 |
Versão de visualização | EiffelStudio 20.11 [3]
/ 21 de dezembro de 2020 |
Disciplina de digitação | estático |
Linguagem de implementação | Eiffel |
Plataforma | Multiplataforma |
SO | FreeBSD , Linux , Mac OS X , OpenBSD , Solaris , Windows |
Licença | dual e empresarial |
Extensões de nome de arquivo | .e |
Local na rede Internet | www.eiffel.org |
Principais implementações | |
EiffelStudio , LibertyEiffel , SmartEiffel , Visual Eiffel , Gobo Eiffel, "The Eiffel Compiler" tecomp | |
Influenciado por | |
Ada , Simula , Z | |
Influenciado | |
Ada 2012 , Albatross , C# , D , Java , Racket , Ruby , [4] Sather , Scala |
Eiffel é uma linguagem de programação orientada a objetos projetada por Bertrand Meyer (um proponente da orientação a objetos e autor de Object-Oriented Software Construction ) e Eiffel Software. Meyer concebeu a linguagem em 1985 com o objetivo de aumentar a confiabilidade do desenvolvimento de software comercial; [5] a primeira versão ficou disponível em 1986. Em 2005, Eiffel tornou-se uma linguagem padronizada ISO .
O design da linguagem está intimamente ligado ao método de programação Eiffel. Ambos são baseados em um conjunto de princípios, incluindo design por contrato , separação comando-consulta , princípio de acesso uniforme, princípio de escolha única, princípio aberto -fechado e separação opção-operando .
Muitos conceitos introduzidos inicialmente por Eiffel mais tarde encontraram seu caminho em Java , C# e outras linguagens. [ citação necessária ] Novas idéias de design de linguagem, particularmente através do processo de padronização Ecma / ISO , continuam a ser incorporadas à linguagem Eiffel.
Características
As principais características da linguagem Eiffel incluem:
- Uma estrutura de programa orientada a objetos na qual uma classe serve como unidade básica de decomposição.
- Design por contrato fortemente integrado com outras construções de linguagem.
- Gerenciamento automático de memória, normalmente implementado pela coleta de lixo .
- Herança , incluindo herança múltipla , renomeação , redefinição , "select", herança não conforme e outros mecanismos destinados a tornar a herança segura.
- Programação genérica restrita e irrestrita
- Um sistema de tipo uniforme que trata de semântica de valor e referência em que todos os tipos, incluindo tipos básicos como INTEGER, são baseados em classe.
- Digitação estática
- Void safety ou proteção estática contra chamadas em referências nulas, por meio do mecanismo de tipos anexados.
- Agentes, ou objetos que envolvem cálculos, intimamente ligados a closures e cálculo lambda .
- Uma vez rotinas, ou rotinas avaliadas apenas uma vez, para compartilhamento de objetos e inicialização descentralizada.
- Sintaxe baseada em palavras-chave na tradição ALGOL / Pascal , mas sem separadores, na medida em que os pontos e vírgulas são opcionais, com sintaxe de operador disponível para rotinas.
- Insensibilidade a maiúsculas e minúsculas
- A Programação Orientada a Objetos Concorrente Simples ( SCOOP ) facilita a criação de vários veículos de execução simultaneamente ativos em um nível de abstração acima dos detalhes específicos desses veículos (por exemplo, vários threads sem gerenciamento de mutex específico).
Objetivos de design
Eiffel enfatiza declarações declarativas sobre o código procedimental e tenta eliminar a necessidade de instruções de contabilidade.
Eiffel evita truques de codificação ou técnicas de codificação destinadas a dicas de otimização para o compilador. O objetivo não é apenas tornar o código mais legível, mas também permitir que os programadores se concentrem nos aspectos importantes de um programa sem se prenderem aos detalhes da implementação. A simplicidade de Eiffel destina-se a promover respostas simples, extensíveis, reutilizáveis e confiáveis para problemas de computação. Compiladores para programas de computador escritos em Eiffel fornecem técnicas extensivas de otimização, como in-lining automático, que aliviam o programador de parte da carga de otimização.
Plano de fundo
Eiffel foi originalmente desenvolvido pela Eiffel Software, uma empresa fundada por Bertrand Meyer . A construção de software orientada a objetos contém um tratamento detalhado dos conceitos e da teoria da tecnologia de objetos que levou ao projeto de Eiffel. [6]
O objetivo do projeto por trás da linguagem Eiffel, bibliotecas e métodos de programação é permitir que os programadores criem módulos de software confiáveis e reutilizáveis. Eiffel suporta herança múltipla , genericidade , polimorfismo , encapsulamento , conversões de tipo seguro e covariância de parâmetro . A contribuição mais importante de Eiffel para a engenharia de software é o design por contrato (DbC), no qual asserções , pré -condições , pós -condições e invariantes de classe são empregados para ajudar a garantir a correção do programa sem sacrificar a eficiência.
O design de Eiffel é baseado na teoria de programação orientada a objetos, com apenas uma pequena influência de outros paradigmas ou preocupação com o suporte de código legado. Eiffel suporta formalmente tipos de dados abstratos . Sob o design de Eiffel, um texto de software deve ser capaz de reproduzir sua documentação de design a partir do próprio texto, usando uma implementação formalizada do "Tipo de Dados Abstrato".
Implementações e ambientes
EiffelStudio é um ambiente de desenvolvimento integrado disponível sob licença de código aberto ou comercial. Oferece um ambiente orientado a objetos para engenharia de software . EiffelEnvision é um plug-in para Microsoft Visual Studio que permite aos usuários editar, compilar e depurar projetos Eiffel de dentro do Microsoft Visual Studio IDE. Cinco outras implementações de código aberto estão disponíveis: "The Eiffel Compiler" tecomp; Gobo Eiffel; SmartEiffel , a implementação GNU, baseada em uma versão mais antiga da linguagem; LibertyEiffel , baseado no compilador SmartEiffel; e Visual Eiffel .
Várias outras linguagens de programação incorporam elementos introduzidos pela primeira vez em Eiffel. Sather , por exemplo, foi originalmente baseado em Eiffel, mas desde então divergiu e agora inclui vários recursos de programação funcional. A linguagem de ensino interativo Blue, precursora do BlueJ , também é baseada em Eiffel. A Apple Media Tool inclui um Apple Media Language baseado em Eiffel.
Especificações e padrões
A definição da linguagem Eiffel é um padrão internacional da ISO . O padrão foi desenvolvido pela ECMA International , que primeiro aprovou o padrão em 21 de junho de 2005 como Standard ECMA-367, Eiffel: Analysis, Design and Programming Language. Em junho de 2006, ECMA e ISO adotaram a segunda versão. Em novembro de 2006, a ISO publicou pela primeira vez essa versão. O padrão pode ser encontrado e usado gratuitamente no site da ECMA. [7] A versão ISO [8] é idêntica em todos os aspectos, exceto na formatação.
Eiffel Software, "The Eiffel Compiler" tecomp e Eiffel-library-developer Gobo se comprometeram a implementar o padrão; O EiffelStudio 6.1 da Eiffel Software e o "The Eiffel Compiler" tecomp implementam alguns dos principais novos mecanismos - em particular, agentes inline, comandos de atribuição, notação de colchetes, herança não conforme e tipos anexados. A equipe SmartEiffel se afastou desse padrão para criar sua própria versão da linguagem, que eles acreditam estar mais próxima do estilo original de Eiffel. A Object Tools não divulgou se versões futuras de seu compilador Eiffel cumprirão o padrão. LibertyEiffel implementa um dialeto em algum lugar entre a linguagem SmartEiffel e o padrão.
O padrão cita as seguintes especificações do idioma Eiffel predecessor:
- Bertrand Meyer: Eiffel: The Language, Prentice Hall, segunda impressão, 1992 (primeira impressão: 1991)
- Bertrand Meyer: Standard Eiffel (revisão da entrada anterior), em andamento, 1997-presente, na página ETL3 de Bertrand Meyer, e
- Bertrand Meyer: Object-Oriented Software Construction, Prentice Hall: primeira edição, 1988; segunda edição, 1997.
- Bertrand Meyer: Toque de classe: Aprendendo a programar bem com objetos e contratos, Springer-Verlag, 2009 ISBN 978-3-540-92144-8 lxiv + 876 páginas Impressão em cores, várias fotografias coloridas
A versão atual do padrão de junho de 2006 contém algumas inconsistências (por exemplo, redefinições de covariantes) [ citação necessária ] . O comitê da ECMA ainda não anunciou nenhum cronograma e direção sobre como resolver as inconsistências.
Sintaxe e semântica
Estrutura geral
Um "sistema" ou "programa" Eiffel é uma coleção de classes . Acima do nível de classes, Eiffel define cluster , que é essencialmente um grupo de classes e possivelmente de subclusters (clusters aninhados). Clusters não são uma construção de linguagem sintática , mas sim uma convenção organizacional padrão. Normalmente, um programa Eiffel será organizado com cada classe em um arquivo separado e cada cluster em um diretório contendo arquivos de classe. Nesta organização, os subclusters são subdiretórios. Por exemplo, sob convenções padrão de organização e maiúsculas, x.e
pode ser o nome de um arquivo que define uma classe chamada X.
Uma classe contém recursos , que são semelhantes a "rotinas", "membros", "atributos" ou "métodos" em outras linguagens de programação orientadas a objetos. Uma classe também define seus invariantes e contém outras propriedades, como uma seção de "notas" para documentação e metadados. Os tipos de dados padrão de Eiffel, como INTEGER
, STRING
e ARRAY
, são todos classes.
Todo sistema deve ter uma classe designada como "root", com um de seus procedimentos de criação designado como "procedimento raiz". A execução de um sistema consiste em criar uma instância da classe raiz e executar seu procedimento raiz. Geralmente, isso cria novos objetos, chama novos recursos e assim por diante.
Eiffel tem cinco instruções executáveis básicas: atribuição, criação de objeto, chamada de rotina, condição e iteração. As estruturas de controle de Eiffel são rigorosas na aplicação de programação estruturada: cada bloco tem exatamente uma entrada e exatamente uma saída.
Escopo
Ao contrário de muitas linguagens orientadas a objetos, mas como Smalltalk , Eiffel não permite nenhuma atribuição em atributos de objetos, exceto dentro das características de um objeto, que é a aplicação prática do princípio de ocultação de informações ou abstração de dados, exigindo interfaces formais para dados mutação. Para colocá-lo na linguagem de outras linguagens de programação orientadas a objetos, todos os atributos Eiffel são "protegidos" e "setters" são necessários para que os objetos cliente modifiquem valores. Um resultado disso é que "setters" podem, e normalmente o fazem, implementar as invariantes para as quais Eiffel fornece sintaxe.
Embora Eiffel não permita acesso direto aos recursos de uma classe por um cliente da classe, ele permite a definição de um "comando de atribuição", como:
some_attribute : SOME_TYPE atribuir set_some_attribute
set_some_attribute ( v : VALUE_TYPE )
-- Configura o valor de some_attribute para `v'.
do
some_attribute := v
end
Embora seja uma pequena reverência à comunidade geral de desenvolvedores para permitir algo parecido com acesso direto (por exemplo, quebrando o Princípio de Ocultação de Informações), a prática é perigosa, pois oculta ou ofusca a realidade de um "setter" sendo usado. Na prática, é melhor redirecionar a chamada para um setter ao invés de implicar um acesso direto a um recurso como some_attribute
no código de exemplo acima. [ citação necessária ]
Ao contrário de outras linguagens, tendo noções de "público", "protegido", "privado" e assim por diante, Eiffel usa uma tecnologia de exportação para controlar com mais precisão o escopo entre as classes cliente e fornecedor. A visibilidade do recurso é verificada estaticamente em tempo de compilação. Por exemplo, (abaixo), o "{NONE}" é semelhante a "protegido" em outros idiomas. O escopo aplicado desta forma a um "conjunto de recursos" (por exemplo, tudo abaixo da palavra-chave 'recurso' até a próxima palavra-chave do conjunto de recursos ou o final da classe) pode ser alterado em classes descendentes usando a palavra-chave "exportar".
feature { NONE } -- Inicialização
default_create
-- Inicializa uma nova instância decimal 'zero'.
fazer
make_zero
final
Como alternativa, a falta de uma declaração de exportação {x} implica em {ANY} e é semelhante ao escopo "público" de outros idiomas.
recurso -- Constantes
Finalmente, o escopo pode ser controlado de forma seletiva e precisa para qualquer classe no universo do projeto Eiffel, como:
recurso { DECIMAL , DCM_MA_DECIMAL_PARSER , DCM_MA_DECIMAL_HANDLER } -- Acesso
Aqui, o compilador permitirá que apenas as classes listadas entre as chaves acessem os recursos dentro do grupo de recursos (por exemplo , DECIMAL, DCM_MA_DECIMAL_PARSER, DCM_MA_DECIMAL_HANDLER ).
"Olá Mundo!"
A aparência de uma linguagem de programação é muitas vezes transmitida usando um "Hello, world!" programa. Tal programa escrito em Eiffel pode ser:
class
HELLO_WORLD
criar
make
feature
make
do
print ( "Olá, mundo!%N" )
end
end
Este programa contém a classe HELLO_WORLD
. O construtor (rotina de criação) para a classe, chamada make
, chama a print
rotina da biblioteca do sistema para gravar uma "Hello,
world!"
mensagem na saída.
Design por contrato
O conceito de Design by Contract é central para Eiffel. Os contratos afirmam o que deve ser verdadeiro antes de uma rotina ser executada (pré-condição) e o que deve ser verdadeiro após o término da rotina (pós-condição). Os contratos invariáveis de classe definem quais asserções devem ser verdadeiras antes e depois de qualquer recurso de uma classe ser acessado (tanto rotinas quanto atributos). Além disso, os contratos codificam em código executável as suposições dos desenvolvedores e designers sobre o ambiente operacional dos recursos de uma classe ou da classe como um todo por meio do invariante.
O compilador Eiffel foi projetado para incluir os contratos de recursos e classes em vários níveis. O EiffelStudio, por exemplo, executa todos os contratos de recurso e classe durante a execução no "modo Workbench". Quando um executável é criado, o compilador é instruído por meio do arquivo de configurações do projeto (por exemplo, arquivo ECF) para incluir ou excluir qualquer conjunto de contratos. Assim, um arquivo executável pode ser compilado para incluir ou excluir qualquer nível de contrato, trazendo assim níveis contínuos de testes unitários e de integração. Além disso, os contratos podem ser exercidos contínua e metodicamente por meio do recurso Auto-Test encontrado no EiffelStudio.
Os mecanismos do Design by Contract estão fortemente integrados à linguagem e guiam a redefinição de recursos na herança:
- Pré-condição de rotina: A pré-condição só pode ser enfraquecida por herança; qualquer chamada que atenda aos requisitos do antepassado atende aos do descendente.
- Pós-condição de rotina: A pós-condição só pode ser reforçada por herança; qualquer resultado garantido pelo ancestral ainda é fornecido pelo descendente.
- Invariante de classe: Condições que devem ser verdadeiras após a criação do objeto e após qualquer chamada a uma rotina de classe exportada. Como a invariante é verificada com tanta frequência, ela a torna simultaneamente a forma mais cara e mais poderosa de condição ou contrato.
Além disso, a linguagem suporta uma "instrução de verificação" (uma espécie de "assert"), invariantes de loop e variantes de loop (que garantem o término do loop).
Capacidade à prova de vazios
O recurso Void-safe, como a digitação estática, é outro recurso para melhorar a qualidade do software. O software Void-safe é protegido contra erros de tempo de execução causados por chamadas para referências nulas e, portanto, será mais confiável do que o software no qual podem ocorrer chamadas para destinos nulos. A analogia com a tipagem estática é útil. Na verdade, a capacidade de void-safe pode ser vista como uma extensão do sistema de tipos, ou um passo além da tipagem estática, porque o mecanismo para garantir a segurança de voids está integrado ao sistema de tipos.
A proteção contra chamadas de alvo nulo pode ser vista por meio da noção de anexo e (por extensão) desapego (por exemplo, palavra-chave destacável). O recurso void-safe pode ser visto em um pequeno retrabalho do código de exemplo usado acima:
some_attribute : destacável SOME_TYPE
use_some_attribute
-- Configura o valor de some_attribute para `v'.
faça
se anexado some_attribute como l_attribute then
do_something ( l_attribute )
end
end
do_something ( a_value : SOME_TYPE )
-- Faça algo com `a_value'.
do
... fazendo algo com ` a_value ' ...
end
O exemplo de código acima mostra como o compilador pode abordar estaticamente a confiabilidade de some_attribute
ser anexado ou desconectado no ponto em que é usado. Notavelmente, a palavra- attached
chave permite um "anexo local" (por exemplo l_attribute
, ), que tem como escopo apenas o bloco de código delimitado pela construção da instrução if. Assim, dentro desse pequeno bloco de código, a variável local (por exemplo, l_attribute
) pode ser estaticamente garantida como não nula (ou seja, void segura).
Funcionalidades: comandos e consultas
A principal característica de uma classe é que ela define um conjunto de recursos: como uma classe representa um conjunto de objetos em tempo de execução, ou "instâncias", um recurso é uma operação nesses objetos. Existem dois tipos de recursos: consultas e comandos. Uma consulta fornece informações sobre uma instância. Um comando modifica uma instância.
A distinção comando-consulta é importante para o método Eiffel. Em particular:
- Princípio de acesso uniforme : do ponto de vista de um cliente de software fazendo uma chamada para um recurso de classe, se uma consulta é um atributo (valor de campo) ou uma função (valor calculado) não deve fazer nenhuma diferença. Por exemplo,
a_vehicle.speed
pode ser um atributo acessado no objetoa_vehicle
ou pode ser calculado por uma função que divide a distância pelo tempo. A notação é a mesma em ambos os casos, de modo que é fácil alterar a implementação da classe sem afetar o software cliente. - Princípio da Separação Comando-Consulta : As consultas não devem modificar a instância. Esta não é uma regra de linguagem, mas um princípio metodológico. Assim, no bom estilo Eiffel, não se encontram funções "get" que alteram algo e retornam um resultado; em vez disso, existem comandos (procedimentos) para alterar objetos e consultas para obter informações sobre o objeto, resultantes de alterações anteriores.
Sobrecarregando
Eiffel não permite sobrecarga de argumentos . Cada nome de recurso dentro de uma classe sempre mapeia para um recurso específico dentro da classe. Um nome, dentro de uma classe, significa uma coisa. Essa escolha de design ajuda a legibilidade das classes, evitando uma causa de ambiguidade sobre qual rotina será invocada por uma chamada. Também simplifica o mecanismo de linguagem; em particular, é isso que torna possível o mecanismo de herança múltipla de Eiffel. [9]
Os nomes podem, é claro, ser reutilizados em diferentes classes. Por exemplo, o recurso plus (junto com seu alias infixo "+" ) é definido em várias classes: INTEGER , REAL , STRING , etc.
Genericidade
Uma classe genérica é uma classe que varia de acordo com o tipo (por exemplo, LIST [PHONE], uma lista de números de telefone; ACCOUNT [G->ACCOUNT_TYPE], permitindo ACCOUNT [SAVINGS] e ACCOUNT [CHECKING], etc.). As classes podem ser genéricas, para expressar que são parametrizadas por tipos. Os parâmetros genéricos aparecem entre colchetes:
classe LISTA [ G ] ...
G é conhecido como um "parâmetro genérico formal". (Eiffel reserva "argumento" para rotinas e usa "parâmetro" apenas para classes genéricas.) Com tal declaração G representa dentro da classe um tipo arbitrário; então uma função pode retornar um valor do tipo G, e uma rotina pode receber um argumento desse tipo:
item : G do ... end
put ( x : G ) do ... end
Os LIST [INTEGER]
e LIST [WORD]
são "derivações genéricas" desta classe. As combinações permitidas (com n: INTEGER
, w: WORD
, il: LIST [INTEGER]
, wl: LIST [WORD]
) são:
n := il . item
w . coloque ( w )
INTEGER
e WORD
são os "parâmetros genéricos reais" nessas derivações genéricas.
Também é possível ter parâmetros formais 'restringidos', para os quais o parâmetro real deve herdar de uma determinada classe, a "restrição". Por exemplo, em
class HASH_TABLE [ G , KEY -> HASHABLE ]
uma derivação HASH_TABLE [INTEGER, STRING]
é válida apenas se STRING
herda de HASHABLE
(como de fato acontece em bibliotecas Eiffel típicas). Dentro da classe, tendo KEY
restringido por HASHABLE
meio de que x: KEY
é possível aplicar a x
todos os recursos de HASHABLE
, como em x.hash_code
.
Noções básicas de herança
Para herdar de uma ou mais outras, uma classe incluirá uma inherit
cláusula no início:
classe C herda
A
B
-- ... Declaração de resto da classe ...
A classe pode redefinir (substituir) alguns ou todos os recursos herdados. Isso deve ser explicitamente anunciado no início da classe por meio de uma redefine
subcláusula da cláusula de herança, como em
classe C herda
A
redefine f , g , h end
B
redefine u , v end
Veja [10] para uma discussão completa da herança Eiffel.
Aulas e recursos adiados
As classes podem ser definidas com deferred class
em vez de com class
para indicar que a classe não pode ser instanciada diretamente. Classes não instanciadas são chamadas de classes abstratas em algumas outras linguagens de programação orientadas a objetos. Na linguagem de Eiffel, apenas uma classe "efetiva" pode ser instanciada (pode ser descendente de uma classe diferida). Um recurso também pode ser adiado usando a palavra- deferred
chave no lugar de uma do
cláusula. Se uma classe tiver alguma característica diferida, ela deve ser declarada como diferida; no entanto, uma classe sem características diferidas pode, no entanto, ser diferida.
As classes deferidas desempenham um papel semelhante às interfaces em linguagens como Java, embora muitos teóricos da programação orientada a objetos acreditem que as próprias interfaces são em grande parte uma resposta à falta de herança múltipla do Java (que Eiffel tem). [11] [12]
Renomeando
Uma classe que herda de uma ou mais outras obtém todos os seus recursos, por padrão, sob seus nomes originais. Pode, no entanto, alterar seus nomes por meio de rename
cláusulas. Isso é necessário no caso de herança múltipla se houver conflitos de nome entre os recursos herdados; sem renomear, a classe resultante violaria o princípio de não sobrecarga mencionado acima e, portanto, seria inválida.
Tuplas
Os tipos de tuplas podem ser vistos como uma forma simples de classe, fornecendo apenas atributos e o procedimento "setter" correspondente. Um tipo de tupla típico lê
TUPLE [ nome : STRING ; peso : REAL ; data : DATA ]
e poderia ser usado para descrever uma noção simples de registro de nascimento se uma classe não for necessária. Uma instância de tal tupla é simplesmente uma sequência de valores com os tipos fornecidos, dados entre colchetes, como
[ "Brigitte" , 3.5 , Last_night ]
Os componentes de tal tupla podem ser acessados como se as tags da tupla fossem atributos de uma classe, por exemplo, se t
a tupla acima foi atribuída, ela t.weight
tem valor 3.5.
Graças à noção de comando de atribuição (veja abaixo), a notação de ponto também pode ser usada para atribuir componentes de tal tupla, como em
t . peso := t . peso + 0,5
As tags de tupla são opcionais, de modo que também é possível escrever um tipo de tupla como TUPLE [STRING, REAL, DATE]
. (Em alguns compiladores, esta é a única forma de tupla, pois as tags foram introduzidas com o padrão ECMA.)
A especificação precisa de eg TUPLE [A, B, C]
é que ele descreve sequências de pelo menos três elementos, sendo os três primeiros do tipo A
, B
, C
respectivamente. Como resultado, TUPLE [A, B, C]
está em conformidade com (pode ser atribuído a) TUPLE [A, B]
, a TUPLE [A]
e a TUPLE
(sem parâmetros), o tipo de tupla superior ao qual todos os tipos de tupla estão em conformidade.
Agentes
O mecanismo de "agente" de Eiffel envolve operações em objetos. Esse mecanismo pode ser usado para iteração, programação orientada a eventos e outros contextos nos quais é útil passar operações pela estrutura do programa. Outras linguagens de programação, especialmente aquelas que enfatizam a programação funcional , permitem um padrão semelhante usando continuations , closures ou generators ; Os agentes de Eiffel enfatizam o paradigma orientado a objetos da linguagem e usam uma sintaxe e semântica semelhantes aos blocos de código em Smalltalk e Ruby .
Por exemplo, para executar o my_action
bloco para cada elemento de my_list
, escrever-se-ia:
minha_lista . do_all ( agente minha_ação )
Para executar my_action
apenas em elementos que satisfaçam my_condition
, uma limitação/filtro pode ser adicionada:
minha_lista . do_if ( agente minha_ação , agente minha_condição )
Nestes exemplos, my_action
e my_condition
são rotinas. Prefixá-los com agent
produz um objeto que representa a rotina correspondente com todas as suas propriedades, em particular a capacidade de ser chamada com os argumentos apropriados. Então se a
representa aquele objeto (por exemplo porque a
é o argumento para do_all
), a instrução
um . chamar ( [ x ] )
chamará a rotina original com o argumento x
, como se tivéssemos chamado diretamente a rotina original: my_action (x)
. Argumentos para call
são passados como uma tupla, aqui [x]
.
É possível manter alguns argumentos para um agente abertos e fechar outros . Os argumentos abertos são passados como argumentos para call
: eles são fornecidos no momento do uso do agente . Os argumentos fechados são fornecidos no momento da definição do agente . Por exemplo, se action2
tiver dois argumentos, a iteração
minha_lista . do_all ( ação do agente2 ( ? , y ) )
itera action2 (x, y)
para valores sucessivos de x
, onde o segundo argumento permanece definido como y
. O ponto de interrogação ?
indica um argumento aberto; y
é um argumento fechado do agente. Observe que a sintaxe básica agent f
é um atalho para agent f (?, ?, ...)
com todos os argumentos abertos. Também é possível abrir o alvo de um agente através da notação {T}?
onde T
está o tipo do alvo.
A distinção entre operandos abertos e fechados (operandos = argumentos + destino) corresponde à distinção entre variáveis vinculadas e livres no cálculo lambda . Uma expressão de agente como action2 (?, y)
com alguns operandos fechados e alguns abertos corresponde a uma versão da operação original cursada nos operandos fechados.
O mecanismo do agente também permite definir um agente sem referência a uma rotina existente (como my_action
, my_condition
, action2
), através de agentes inline como em
minha_lista . do_all ( agente ( s : STRING )
require
not_void : s / = Void
do
s . append_character ( ' , ' )
garantir
anexado : s.count = old s.count + 1 end ) _
O agente inline passado aqui pode ter todas as armadilhas de uma rotina normal, incluindo pré-condição, pós-condição, cláusula de resgate (não usada aqui) e uma assinatura completa. Isso evita definir rotinas quando tudo o que é necessário é uma computação a ser encapsulada em um agente. Isso é útil em particular para contratos, como em uma cláusula invariável que expressa que todos os elementos de uma lista são positivos:
minha_lista . for_all ( agent ( x : INTEGER ): BOOLEAN do Result := ( x > 0 ) end )
O mecanismo do agente atual deixa uma possibilidade de erro do tipo em tempo de execução (se uma rotina com n argumentos for passada para um agente esperando m argumentos com m < n ). Isso pode ser evitado por uma verificação em tempo de execução por meio da pré-condição valid_arguments
de call
. Várias propostas para uma correção puramente estática deste problema estão disponíveis, incluindo uma proposta de mudança de linguagem por Ribet et al. [13]
Uma vez rotinas
O resultado de uma rotina pode ser armazenado em cache usando a palavra- once
chave no lugar de do
. Chamadas não iniciais para uma rotina não requerem computação adicional ou alocação de recursos, mas simplesmente retornam um resultado calculado anteriormente. Um padrão comum para "funções únicas" é fornecer objetos compartilhados; a primeira chamada criará o objeto, as subsequentes retornarão a referência a esse objeto. O esquema típico é:
shared_object : SOME_TYPE
depois de
criar Result . make ( args )
-- Isso cria o objeto e retorna uma referência a ele por meio de `Result'.
fim
O objeto retornado - Result
no exemplo - pode ser mutável, mas sua referência permanece a mesma.
Muitas vezes, "rotinas únicas" executam uma inicialização necessária: várias chamadas para uma biblioteca podem incluir uma chamada para o procedimento de inicialização, mas apenas a primeira dessas chamadas executará as ações necessárias. Usando este padrão, a inicialização pode ser descentralizada, evitando a necessidade de um módulo de inicialização especial. As "rotinas únicas" são semelhantes em propósito e efeito ao padrão singleton em muitas linguagens de programação e ao padrão Borg usado em Python.
Por padrão, uma "rotina única" é chamada uma vez por encadeamento . A semântica pode ser ajustada para uma vez por processo ou uma vez por objeto , qualificando-a com uma "chave única", por exemplo once ("PROCESS")
.
Conversões
Eiffel fornece um mecanismo para permitir conversões entre vários tipos. Os mecanismos coexistem com a herança e a complementam. Para evitar qualquer confusão entre os dois mecanismos, o design impõe o seguinte princípio:
- (Princípio de conversão) Um tipo pode não estar em conformidade e ser convertido em outro.
Por exemplo, NEWSPAPER
pode estar em conformidade com PUBLICATION
, mas INTEGER
converte para REAL
(e não herda dele).
O mecanismo de conversão simplesmente generaliza as regras de conversão ad hoc (como de fato entre INTEGER
e REAL
) que existem na maioria das linguagens de programação, tornando-as aplicáveis a qualquer tipo desde que o princípio acima seja observado. Por exemplo, uma DATE
classe pode ser declarada para converter em STRING
; isso torna possível criar uma string a partir de uma data simplesmente
minha_string := minha_data
como um atalho para usar uma criação de objeto explícito com um procedimento de conversão:
crie minha_string . make_from_date ( my_date )
Para tornar a primeira forma possível como sinônimo da segunda, basta listar o procedimento de criação (construtor) make_from_date
em uma convert
cláusula no início da classe.
Como outro exemplo, se houver um procedimento de conversão listado de TUPLE [day: INTEGER; month: STRING; year: INTEGER]
, pode-se atribuir diretamente uma tupla a uma data, causando a conversão apropriada, como em
Bastille_day := [ 14 , "Julho" , 1789 ]
Tratamento de exceções
O tratamento de exceções em Eiffel é baseado nos princípios de design por contrato. Por exemplo, uma exceção ocorre quando o chamador de uma rotina não satisfaz uma pré-condição ou quando uma rotina não pode garantir uma pós-condição prometida. Em Eiffel, o tratamento de exceções não é usado para controle de fluxo ou para corrigir erros de entrada de dados.
Um manipulador de exceção Eiffel é definido usando a palavra-chave rescue . Dentro da seção de resgate , a palavra-chave retry executa a rotina novamente. Por exemplo, a rotina a seguir rastreia o número de tentativas de execução da rotina e tenta apenas um certo número de vezes:
connect_to_server ( server : SOCKET )
-- Conecta-se a um servidor ou desiste após 10 tentativas.
require
server /= Void e então server . address /= Tentativas
locais
anuladas : INTEGER
do
server . conectar
garantir
conectado : servidor . is_connected
rescue
se tentativas < 10 então
tentativas := tentativas + 1
nova tentativa
end
end
Este exemplo é indiscutivelmente falho para qualquer coisa, exceto os programas mais simples, no entanto, porque a falha de conexão é esperada. Para a maioria dos programas, um nome de rotina como try_connecting_to_server seria melhor, e a pós-condição não prometeria uma conexão, deixando para o chamador tomar as medidas apropriadas se a conexão não fosse aberta.
Simultaneidade
Várias bibliotecas de rede e threading estão disponíveis, como EiffelNet e EiffelThreads. Um modelo de simultaneidade para Eiffel, baseado nos conceitos de design por contrato, é o SCOOP , ou Simple Concurrent Object-Oriented Programming , que ainda não faz parte da definição oficial da linguagem, mas está disponível no EiffelStudio . CAMEO [14] é uma variação (não implementada) do SCOOP para Eiffel. A simultaneidade também interage com exceções. Exceções assíncronas podem ser problemáticas (onde uma rotina gera uma exceção depois que seu chamador termina). [15]
Sintaxe de operador e colchete, comandos de atribuição
A visão de computação de Eiffel é completamente orientada a objetos no sentido de que cada operação é relativa a um objeto, o "alvo". Assim, por exemplo, uma adição como
a + b
é conceitualmente entendido como se fosse a chamada do método
um . mais ( b )
com alvo a
, recurso plus
e argumento b
.
Claro, a primeira é a sintaxe convencional e geralmente preferida. A sintaxe do operador torna possível usar qualquer uma das formas declarando o recurso (por exemplo, em INTEGER
, mas isso se aplica a outras classes básicas e pode ser usado em qualquer outro para o qual tal operador seja apropriado):
mais alias "+" ( other : INTEGER ): INTEGER
-- ... Declaração de função normal...
end
A gama de operadores que podem ser usados como "alias" é bastante ampla; eles incluem operadores predefinidos como "+", mas também "operadores livres" feitos de símbolos não alfanuméricos. Isso torna possível projetar notações especiais de infixo e prefixo, por exemplo, em aplicações matemáticas e físicas.
Cada classe pode ainda ter uma função alias a "[]", o operador "colchete", permitindo a notação a [i, ...]
como sinônimo de a.f (i, ...)
onde f
está a função escolhida. Isso é particularmente útil para estruturas de contêiner como arrays, tabelas de hash , listas etc. Por exemplo, o acesso a um elemento de uma tabela de hash com chaves de string pode ser escrito
número := agenda_telefone [ "JILL SMITH" ]
"Comandos de atribuição" são um mecanismo complementar projetado no mesmo espírito de permitir notação conveniente e bem estabelecida reinterpretada na estrutura da programação orientada a objetos. Os comandos de atribuição permitem que uma sintaxe semelhante à atribuição chame procedimentos de "setter". Uma atribuição adequada nunca pode ser da forma a.x := v
, pois isso viola a ocultação de informações; você tem que ir para um comando setter (procedimento). Por exemplo, a classe de tabela de hash pode ter a função e o procedimento
item alias "[]" ( key : STRING ): ELEMENT [ 3 ]
-- O elemento da chave 'key'.
-- (consulta "Getter")
do
...
end
put ( e : ELEMENT ; key : STRING )
-- Insere o elemento `e', associando-o com a chave `key'.
-- (comando "Setter")
do
...
end
Então, para inserir um elemento, você deve usar uma chamada explícita ao comando setter:
[ 4 ] agenda_telefone . put ( New_person , "JILL SMITH" )
É possível escrever isso equivalentemente como
[ 5 ] agenda_telefone [ "JILL SMITH" ] := New_person
(da mesma forma que phone_book ["JILL SMITH"]
é sinônimo de number := phone_book.item ("JILL SMITH")
), desde que a declaração de item
now comece (substituição de [3]) por
item alias "[]" ( key : STRING ): ELEMENT assign put
Isso declara put
como o comando de atribuição associado a item
e, combinado com o alias de colchetes, torna [5] legal e equivalente a [4]. (Também poderia ser escrito, sem tirar vantagem do colchete, como phone_book.item ("JILL SMITH") := New_person
.
Nota: A lista de argumentos do atribuídor de a é restrita a ser: (tipo de retorno de a; toda a lista de argumentos de a...)
Propriedades léxicas e de sintaxe
Eiffel não diferencia maiúsculas de minúsculas. Os tokens make
e todos denotam o mesmo identificador. Veja, no entanto, as "regras de estilo" abaixo.
maKe
MAKE
Os comentários são introduzidos por --
(dois traços consecutivos) e se estendem até o final da linha.
O ponto e vírgula, como separador de instruções, é opcional. Na maioria das vezes, o ponto e vírgula é omitido, exceto para separar várias instruções em uma linha. Isso resulta em menos confusão na página do programa.
Não há aninhamento de declarações de recurso e classe. Como resultado, a estrutura de uma classe Eiffel é simples: algumas cláusulas de nível de classe (herança, invariante) e uma sucessão de declarações de recursos, todas no mesmo nível.
É costume agrupar recursos em "cláusulas de recursos" separadas para maior legibilidade, com um conjunto padrão de tags de recursos básicos aparecendo em uma ordem padrão, por exemplo:
class HASH_TABLE [ ELEMENT , KEY -> HASHABLE ] herdar TABLE [ ELEMENT ]
feature -- Inicialização
-- ... Declarações de comandos de inicialização (procedimentos/construtores de criação) ...
feature -- Access
-- ... Declarações de consultas não booleanas no estado do objeto, por exemplo, item ...
feature -- Status report
-- ... Declarações de consultas booleanas sobre o estado do objeto, ex. is_empty ...
feature -- Alteração de elemento
-- ... Declarações de comandos que alteram a estrutura, por exemplo, colocar ...
-- etc.
fim
Em contraste com a maioria das linguagens de programação de colchetes , Eiffel faz uma distinção clara entre expressões e instruções. Isso está de acordo com o princípio de separação de comando-consulta do método Eiffel.
Convenções de estilo
Grande parte da documentação de Eiffel usa convenções de estilo distintas, projetadas para impor uma aparência consistente. Algumas dessas convenções se aplicam ao próprio formato do código e outras à renderização tipográfica padrão do código Eiffel em formatos e publicações onde essas convenções são possíveis.
Embora a linguagem não faça distinção entre maiúsculas e minúsculas, os padrões de estilo prescrevem o uso de letras maiúsculas para nomes de classes ( LIST
), letras minúsculas para nomes de recursos ( make
) e letras maiúsculas para constantes ( Avogadro
). O estilo recomendado também sugere sublinhado para separar os componentes de um identificador de várias palavras, como em average_temperature
.
A especificação de Eiffel inclui diretrizes para exibir textos de software em formatos de tipografia: palavras-chave em negrito, identificadores e constantes definidos pelo usuário são mostrados em italics
, comentários, operadores e sinais de pontuação em Roman
, com texto do programa blue
como no presente artigo para distingui-lo de texto explicativo. Por exemplo, o "Olá, mundo!" programa dado acima seria renderizado como abaixo na documentação Eiffel:
class
HELLO_WORLD
criar
make
feature
make
do
print ( "Hello, world!" )
end
end
Interfaces para outras ferramentas e linguagens
Eiffel é uma linguagem puramente orientada a objetos, mas fornece uma arquitetura aberta para interface com software "externo" em qualquer outra linguagem de programação.
É possível, por exemplo, programar operações no nível da máquina e do sistema operacional em C . Eiffel fornece uma interface direta para rotinas C, incluindo suporte para "inline C" (escrevendo o corpo de uma rotina Eiffel em C, normalmente para operações curtas no nível da máquina).
Embora não haja conexão direta entre Eiffel e C, muitos compiladores Eiffel ( Visual Eiffel é uma exceção) geram código fonte C como uma linguagem intermediária , para enviar a um compilador C, para otimização e portabilidade . Como tal, eles são exemplos de transcompiladores . O compilador Eiffel tecomp pode executar código Eiffel diretamente (como um interpretador) sem passar por um código C intermediário ou emitir código C que será passado para um compilador C para obter código nativo otimizado. No .NET, o compilador EiffelStudio gera diretamente o código CIL (Common Intermediate Language). O SmartEiffelcompilador também pode gerar bytecode Java .
Referências
- ^ "Eiffel em poucas palavras" . archive.eiffel . com . Recuperado em 24 de agosto de 2017 .
- ^ "EiffelStudio 19.05 já está disponível!" . Eiffel.org . 22 de dezembro de 2020.
- ^ "Lançamentos do EiffelStudio 20.11" . Eiffel.org .
- ^ Cooper, Peter (2009). Ruby iniciante: de iniciante a profissional . Começando do Iniciante ao Profissional (2ª ed.). Berkeley: A Press. pág. 101. ISBN 978-1-4302-2363-4.
Em menor grau, Python, LISP, Eiffel, Ada e C++ também influenciaram Ruby.
- ^ "Eiffel - a Língua" . Recuperado em 6 de julho de 2016 .
- ^ Construção de software orientada a objetos , segunda edição, por Bertrand Meyer , Prentice Hall, 1997, ISBN 0-13-629155-4
- ^ ECMA International: Standard ECMA-367 – Eiffel: Análise, Design e Linguagem de Programação 2ª edição (Junho de 2006); disponível online em www.ecma-international.org/publications/standards/Ecma-367.htm
- ^ Organização Internacional para Padronização: Padrão ISO/IEC DIS 25436, disponível online em [1]
- ^ Bertrand Meyer: Overloading vs Object Technology, no Journal of Object-Oriented Programming (JOOP), vol. 14, não. 4, outubro-novembro de 2001, disponível online
- ^ "9 HERANÇA" . Archive.eiffel. com. 23-03-1997 . Recuperado 2013-07-08 .
- ^ "Herança múltipla e interfaces" . Artima. com. 2002-12-16 . Recuperado 2013-07-08 .
- ^ "A herança múltipla não é ruim" . C2.com. 28-04-2007 . Recuperado 2013-07-08 .
- ^ Philippe Ribet, Cyril Adrian, Olivier Zendra, Dominique Colnet: Conformance of agents in the Eiffel language , in Journal of Object Technology , vol. 3, não. 4, abril de 2004, Edição especial: TOOLS USA 2003, pp. 125-143. Disponível on-line na página do artigo JOT
- ^ Brooke, Phillip; Ricardo Paige (2008). "Cameo: um modelo alternativo de simultaneidade para Eiffel" (PDF) . Aspectos formais da computação . Springer. 21 (4): 363–391. doi : 10.1007/s00165-008-0096-1 . S2CID 18336088 .
- ^ Brooke, Phillip; Ricardo Paige (2007). "Exceções na Eiffel simultânea" . Jornal de Tecnologia de Objetos . 6 (10): 111–126. doi : 10.5381/jot.2007.6.10.a4 .