Eiffel — объектно-ориентированный язык программирования, разработанный Бертраном Мейером (сторонником объектно-ориентированного программирования и автором Object-Oriented Software Construction ) и Eiffel Software. Мейер задумал язык в 1985 году с целью повышения надежности разработки коммерческого программного обеспечения; [4] первая версия стала доступна в 1986 году. В 2005 году Eiffel стал языком, стандартизированным по ISO .
Дизайн языка тесно связан с методом программирования Эйфеля. Оба они основаны на наборе принципов, включая дизайн по контракту , разделение команд и запросов , принцип равномерного доступа , принцип единственного выбора , принцип открытости и закрытости , разделение опций и операндов .
Многие концепции, изначально введенные Эйфелем, позже нашли свое применение в Java , C# и других языках. [5] Новые идеи проектирования языка, особенно в рамках процесса стандартизации Ecma / ISO , продолжают внедряться в язык Эйфеля.
Ключевые характеристики языка Эйфеля включают в себя:
Эйфель отдает предпочтение декларативным заявлениям, а не процессуальному кодексу, и пытается исключить необходимость в инструкциях по ведению бухгалтерского учета.
Eiffel избегает трюков кодирования или методов кодирования, предназначенных для подсказок по оптимизации компилятору. Цель состоит не только в том, чтобы сделать код более читаемым, но и в том, чтобы позволить программистам сосредоточиться на важных аспектах программы, не увязая в деталях реализации. Простота Eiffel направлена на продвижение простых, расширяемых, повторно используемых и надежных ответов на вычислительные проблемы. Компиляторы для компьютерных программ, написанных на Eiffel, предоставляют обширные методы оптимизации, такие как автоматическое встраивание, которые снимают с программиста часть бремени оптимизации.
Eiffel изначально был разработан Eiffel Software, компанией, основанной Бертраном Мейером . Object-Oriented Software Construction содержит подробное рассмотрение концепций и теории объектной технологии, которая привела к дизайну Эйфеля. [9]
Целью разработки языка Eiffel, библиотек и методов программирования является предоставление программистам возможности создавать надежные, повторно используемые программные модули. Eiffel поддерживает множественное наследование , универсальность , полиморфизм , инкапсуляцию , безопасные по типу преобразования и ковариантность параметров . Наиболее важным вкладом Eiffel в разработку программного обеспечения является проектирование по контракту (DbC), в котором утверждения , предварительные условия , постусловия и инварианты классов используются для обеспечения корректности программы без ущерба для эффективности.
Дизайн Эйфеля основан на теории объектно-ориентированного программирования, с незначительным влиянием других парадигм или заботой о поддержке устаревшего кода. Eiffel формально поддерживает абстрактные типы данных . Согласно дизайну Эйфеля, программный текст должен иметь возможность воспроизводить свою проектную документацию из самого текста, используя формализованную реализацию «Абстрактного типа данных».
EiffelStudio — это интегрированная среда разработки, доступная как по открытой , так и по коммерческой лицензии. Она предлагает объектно-ориентированную среду для разработки программного обеспечения . EiffelEnvision — это подключаемый модуль для Microsoft Visual Studio , который позволяет пользователям редактировать, компилировать и отлаживать проекты Eiffel из среды разработки Microsoft Visual Studio. Доступны еще пять реализаций с открытым исходным кодом : «The Eiffel Compiler» tecomp; Gobo Eiffel; SmartEiffel , реализация GNU, основанная на более старой версии языка; LibertyEiffel , основанная на компиляторе SmartEiffel; и Visual Eiffel .
Несколько других языков программирования включают элементы, впервые представленные в Eiffel. Sather , например, изначально был основан на Eiffel, но с тех пор разошелся и теперь включает несколько функциональных функций программирования. Интерактивный обучающий язык Blue, предшественник BlueJ , также основан на Eiffel. Apple Media Tool включает Apple Media Language на основе Eiffel.
Определение языка Eiffel является международным стандартом ISO . Стандарт был разработан ECMA International , которая впервые утвердила стандарт 21 июня 2005 года как Стандарт ECMA-367, Eiffel: Analysis, Design and Programming Language. В июне 2006 года ECMA и ISO приняли вторую версию. В ноябре 2006 года ISO впервые опубликовала эту версию. Стандарт можно найти и использовать бесплатно на сайте ECMA. [10] Версия ISO [11] идентична во всех отношениях, за исключением форматирования.
Eiffel Software, tecomp "The Eiffel Compiler" и разработчик Eiffel-library-developer Gobo взяли на себя обязательство по внедрению стандарта; EiffelStudio 6.1 и tecomp "The Eiffel Compiler" от Eiffel Software реализуют некоторые из основных новых механизмов, в частности, встроенные агенты, команды присваивателя, скобочную нотацию, несоответствующее наследование и присоединенные типы. Команда SmartEiffel отвернулась от этого стандарта, чтобы создать собственную версию языка, которая, по их мнению, ближе к оригинальному стилю Eiffel. Object Tools не раскрыла, будут ли будущие версии ее компилятора Eiffel соответствовать стандарту. LibertyEiffel реализует диалект где-то между языком SmartEiffel и стандартом.
В стандарте приводятся следующие спецификации на языке предшественника Eiffel:
Текущая версия стандарта от июня 2006 года содержит некоторые несоответствия (например, ковариантные переопределения) [ требуется ссылка ] . Комитет ECMA пока не объявил никаких сроков и указаний по устранению несоответствий.
«Система» или «программа» Eiffel — это набор классов . Выше уровня классов Eiffel определяет кластер , который по сути является группой классов и, возможно, подкластеров (вложенных кластеров). Кластеры — это не синтаксическая языковая конструкция , а скорее стандартное организационное соглашение. Обычно программа Eiffel организована так, что каждый класс находится в отдельном файле, а каждый кластер — в каталоге, содержащем файлы классов. В этой организации подкластеры являются подкаталогами. Например, в соответствии со стандартными организационными и регистровыми соглашениями x.e
может быть именем файла, который определяет класс с именем X.
Класс содержит функции , которые похожи на «процедуры», «члены», «атрибуты» или «методы» в других объектно-ориентированных языках программирования. Класс также определяет свои инварианты и содержит другие свойства, такие как раздел «заметки» для документации и метаданных. Стандартные типы данных Eiffel, такие как INTEGER
, STRING
и ARRAY
, сами по себе являются классами.
Каждая система должна иметь класс, обозначенный как «корневой», с одной из его процедур создания, обозначенной как «корневая процедура». Выполнение системы состоит из создания экземпляра корневого класса и выполнения его корневой процедуры. Обычно это создает новые объекты, вызывает новые функции и т. д.
Eiffel имеет пять основных исполняемых инструкций: назначение, создание объекта, вызов процедуры, условие и итерация. Управляющие структуры Eiffel строго следуют структурному программированию : каждый блок имеет ровно один вход и ровно один выход.
В отличие от многих объектно-ориентированных языков, но подобно Smalltalk , Eiffel не допускает никакого назначения в атрибуты объектов, за исключением свойств объекта, что является практическим применением принципа сокрытия информации или абстракции данных, требуя формальных интерфейсов для мутации данных. Если выразить это на языке других объектно-ориентированных языков программирования, все атрибуты Eiffel «защищены», и «сеттеры» необходимы для клиентских объектов для изменения значений. Результатом этого является то, что «сеттеры» могут и обычно реализуют инварианты, для которых Eiffel предоставляет синтаксис.
Хотя Eiffel не позволяет клиенту класса осуществлять прямой доступ к функциям класса, он позволяет определить «команду присваивателя», например:
some_attribute : SOME_TYPE assign set_some_attribute set_some_attribute ( v : VALUE_TYPE ) -- Устанавливает значение some_attribute равным `v'. do some_attribute := v end
Хотя это и небольшой поклон всему сообществу разработчиков, чтобы разрешить что-то, выглядящее как прямой доступ (например, тем самым нарушая принцип сокрытия информации), эта практика опасна, поскольку она скрывает или запутывает реальность использования "сеттера". На практике лучше перенаправить вызов на сеттер, а не подразумевать прямой доступ к функции, как some_attribute
в примере кода выше. [ необходима цитата ]
В отличие от других языков, имеющих понятия «public», «protected», «private» и т. д., Eiffel использует технологию экспорта для более точного управления областью действия между классами клиента и поставщика. Видимость функций проверяется статически во время компиляции. Например, (ниже) «{NONE}» похоже на «protected» в других языках. Область действия, примененная таким образом к «набору функций» (например, все, что находится ниже ключевого слова «feature» или до следующего ключевого слова набора функций, или до конца класса), может быть изменена в классах-потомках с помощью ключевого слова «export».
feature { NONE } -- Инициализация default_create -- Инициализация нового десятичного экземпляра `ноль'. do make_zero end
В качестве альтернативы отсутствие экспортной декларации {x} подразумевает {ANY} и аналогично «публичной» области действия других языков.
особенность -- Константы
Наконец, область действия может выборочно и точно контролироваться для любого класса во вселенной проекта Eiffel, например:
функция { DECIMAL , DCM_MA_DECIMAL_PARSER , DCM_MA_DECIMAL_HANDLER } -- Доступ
Здесь компилятор разрешит доступ к функциям внутри группы функций только классам, перечисленным в фигурных скобках (например, DECIMAL, DCM_MA_DECIMAL_PARSER, DCM_MA_DECIMAL_HANDLER ).
Внешний вид и восприятие языка программирования часто передаются с помощью программы "Hello, world!" . Такая программа, написанная на Eiffel, может быть:
класс HELLO_WORLD создать создать функцию сделать сделать печать ( "Hello, world!%N" ) конец конец
Эта программа содержит класс HELLO_WORLD
. Конструктор (процедура создания) для класса, названного make
, вызывает print
системную библиотечную процедуру для записи "Hello,
world!"
сообщения на выход.
Концепция Design by Contract является центральной для Eiffel. Контракты утверждают, что должно быть истинным до выполнения процедуры (предусловие) и что должно быть истинным после завершения процедуры (постусловие). Контракты инвариантов классов определяют, какие утверждения должны быть истинными как до, так и после доступа к любой функции класса (как к процедурам, так и к атрибутам). Более того, контракты кодифицируют в исполняемый код предположения разработчиков и дизайнеров об операционной среде функций класса или классе в целом с помощью инварианта.
Компилятор Eiffel предназначен для включения контрактов функций и классов на различных уровнях. Например, EiffelStudio выполняет все контракты функций и классов во время выполнения в «режиме Workbench». Когда создается исполняемый файл, компилятор получает указание с помощью файла настроек проекта (например, файла ECF) включить или исключить любой набор контрактов. Таким образом, исполняемый файл может быть скомпилирован для включения или исключения любого уровня контракта, тем самым обеспечивая непрерывные уровни модульного и интеграционного тестирования. Более того, контракты могут непрерывно и методично выполняться с помощью функции Auto-Test, имеющейся в EiffelStudio.
Механизмы «Проектирования по контракту» тесно интегрированы с языком и направляют переопределение функций при наследовании:
Кроме того, язык поддерживает «инструкцию проверки» (своего рода «утверждение»), инварианты цикла и варианты цикла (которые гарантируют завершение цикла).
Возможность Void-safe, как и статическая типизация, является еще одним средством для улучшения качества программного обеспечения. Программное обеспечение Void-safe защищено от ошибок времени выполнения, вызванных вызовами ссылок void , и поэтому будет более надежным, чем программное обеспечение, в котором могут происходить вызовы целевых объектов void. Аналогия со статической типизацией полезна. Фактически, возможность void-safe можно рассматривать как расширение системы типов или шаг за пределы статической типизации, поскольку механизм обеспечения безопасности void интегрирован в систему типов.
Защита от вызовов void target может быть рассмотрена с помощью понятия присоединения и (в расширении) отсоединения (например, ключевое слово detachable). Возможность void-safe можно увидеть в короткой переработке примера кода, использованного выше:
some_attribute : detachable SOME_TYPE use_some_attribute -- Устанавливает значение some_attribute в `v'. do if attached some_attribute as l_attribute then do_something ( l_attribute ) end end do_something ( a_value : SOME_TYPE ) -- Делает что-то с `a_value'. do ... делает что-то с ` a_value ' ... end
Пример кода выше показывает, как компилятор может статически решать вопрос надежности того, some_attribute
будет ли он присоединен или отсоединен в точке его использования. Примечательно, что attached
ключевое слово допускает "локальное присоединение" (например, l_attribute
), которое ограничено только блоком кода, заключенным в конструкцию if-statement. Таким образом, в этом небольшом блоке кода локальная переменная (например, l_attribute
) может быть статически гарантированно непустой (т. е. безопасной для void).
Основная характеристика класса заключается в том, что он определяет набор функций: поскольку класс представляет набор объектов времени выполнения или «экземпляров», функция является операцией над этими объектами. Существует два вида функций: запросы и команды. Запрос предоставляет информацию об экземпляре. Команда изменяет экземпляр.
Различие между командой и запросом важно для метода Эйфеля. В частности:
a_vehicle.speed
может быть атрибутом, к которому осуществляется доступ на объекте a_vehicle
, или он может быть вычислен функцией, которая делит расстояние на время. Нотация одинакова в обоих случаях, поэтому можно легко изменить реализацию класса, не влияя на клиентское программное обеспечение.Eiffel не допускает перегрузки аргументов . Каждое имя функции в классе всегда сопоставляется с определенной функцией в классе. Одно имя в одном классе означает одно. Такой выбор дизайна способствует читаемости классов, избегая причины неоднозначности относительно того, какая процедура будет вызвана вызовом. Это также упрощает механизм языка; в частности, это делает возможным механизм множественного наследования Eiffel. [12]
Имена, конечно, могут быть повторно использованы в разных классах. Например, признак plus (вместе с его инфиксным псевдонимом "+" ) определен в нескольких классах: INTEGER , REAL , STRING и т. д.
Универсальный класс — это класс, который различается по типу (например, LIST [PHONE], список телефонных номеров; ACCOUNT [G->ACCOUNT_TYPE], позволяющий ACCOUNT [SAVINGS] и ACCOUNT [CHECKING] и т. д.). Классы могут быть универсальными, чтобы выразить, что они параметризованы по типам. Универсальные параметры отображаются в квадратных скобках:
класс СПИСОК [ G ] ...
G известен как «формальный универсальный параметр». (Эйфель резервирует «аргумент» для подпрограмм и использует «параметр» только для универсальных классов.) При таком объявлении G представляет внутри класса произвольный тип; поэтому функция может возвращать значение типа G, а подпрограмма может принимать аргумент этого типа:
элемент : G сделать ... конец положить ( x : G ) сделать ... конец
И являются «общими производными» этого класса. Разрешенные комбинации (с , , LIST [INTEGER]
, ) следующие:LIST [WORD]
n: INTEGER
w: WORD
il: LIST [INTEGER]
wl: LIST [WORD]
n := il . item wl . put ( w )
INTEGER
и WORD
являются «фактически общими параметрами» в этих общих выводах.
Также возможно иметь «ограниченные» формальные параметры, для которых фактический параметр должен наследовать от заданного класса, «ограничение». Например, в
класс HASH_TABLE [ G , KEY -> HASHABLE ]
вывод HASH_TABLE [INTEGER, STRING]
допустим только в том случае, если STRING
наследует от HASHABLE
(как это действительно происходит в типичных библиотеках Eiffel). Внутри класса, имея KEY
ограничение HASHABLE
средствами , для x: KEY
можно применить ко x
всем функциям HASHABLE
, как в x.hash_code
.
Чтобы унаследовать от одного или нескольких других классов, класс должен включать inherit
в начало предложение:
класс C наследует A B -- ... Остальная часть объявления класса ...
Класс может переопределять (переопределять) некоторые или все унаследованные функции. Это должно быть явно объявлено в начале класса через redefine
подпункт пункта наследования, как в
класс C наследует A переопределяет f , g , h конец B переопределяет u , v конец
Полное обсуждение наследования Эйфеля см . в [13] .
Классы могут быть определены с помощью , deferred class
а не с , class
чтобы указать, что класс не может быть напрямую инстанцирован. Не инстанцируемые классы называются абстрактными классами в некоторых других объектно-ориентированных языках программирования. На языке Эйфеля, только «эффективный» класс может быть инстанцирован (он может быть потомком отложенного класса). Функция также может быть отложена с помощью deferred
ключевого слова вместо do
предложения. Если класс имеет какие-либо отложенные функции, он должен быть объявлен как отложенный; однако класс без отложенных функций тем не менее сам может быть отложенным.
Отложенные классы играют отчасти ту же роль, что и интерфейсы в таких языках, как Java, хотя многие теоретики объектно-ориентированного программирования считают, что интерфейсы сами по себе в значительной степени являются ответом на отсутствие множественного наследования в Java (которое есть в Eiffel). [14] [15]
Класс, который наследует от одного или нескольких других, получает все свои функции по умолчанию под их исходными именами. Однако он может изменить их имена с помощью rename
предложений. Это требуется в случае множественного наследования, если есть конфликты имен между унаследованными функциями; без переименования результирующий класс нарушил бы принцип отсутствия перегрузки, отмеченный выше, и, следовательно, был бы недействительным.
Типы кортежей можно рассматривать как простую форму класса, предоставляющую только атрибуты и соответствующую процедуру «setter». Типичный тип кортежа читается как
КОРТЕЖ [ имя : СТРОКА ; вес : ДЕЙСТВИТЕЛЬНЫЙ ; дата : ДАТА ]
и может быть использован для описания простого понятия записи о рождении, если класс не нужен. Экземпляр такого кортежа — это просто последовательность значений с заданными типами, указанными в скобках, например
[ "Бриджит" , 3.5 , Last_night ]
К компонентам такого кортежа можно получить доступ, как если бы теги кортежа были атрибутами класса, например, если t
был назначен вышеуказанный кортеж, то t.weight
он имеет значение 3.5.
Благодаря понятию команды назначения (см. ниже) точечная нотация также может использоваться для назначения компонентов такого кортежа, как в
т . вес := т . вес + 0,5
Теги кортежа являются необязательными, поэтому тип кортежа также можно записать как TUPLE [STRING, REAL, DATE]
. (В некоторых компиляторах это единственная форма кортежа, поскольку теги были введены вместе со стандартом ECMA.)
Точная спецификация eg TUPLE [A, B, C]
заключается в том, что она описывает последовательности из как минимум трех элементов, первые три из которых имеют типы A
, B
, C
соответственно. В результате TUPLE [A, B, C]
соответствует (может быть назначен) TUPLE [A, B]
, TUPLE [A]
и TUPLE
(без параметров) самому верхнему типу кортежа, которому соответствуют все типы кортежей.
Механизм «агента» Эйфеля оборачивает операции в объекты. Этот механизм может использоваться для итерации, событийно-управляемого программирования и других контекстов, в которых полезно передавать операции вокруг структуры программы. Другие языки программирования, особенно те, которые делают упор на функциональное программирование , допускают схожий шаблон с использованием продолжений , замыканий или генераторов ; агенты Эйфеля подчеркивают объектно-ориентированную парадигму языка и используют синтаксис и семантику, похожие на блоки кода в Smalltalk и Ruby .
Например, чтобы выполнить my_action
блок для каждого элемента my_list
, нужно написать:
my_list.do_all ( агент my_action )
Чтобы выполнить my_action
только те элементы, которые удовлетворяют my_condition
, можно добавить ограничение/фильтр:
my_list.do_if ( агент my_action , агент my_condition )
В этих примерах my_action
и my_condition
являются подпрограммами. Префикс с ними agent
дает объект, который представляет соответствующую подпрограмму со всеми ее свойствами, в частности, возможностью быть вызванным с соответствующими аргументами. Так что если a
представляет этот объект (например, потому что a
является аргументом для do_all
), инструкция
а . вызов ( [ x ] )
вызовет исходную процедуру с аргументом x
, как если бы мы напрямую вызвали исходную процедуру: my_action (x)
. Аргументы call
передаются в виде кортежа, здесь [x]
.
Можно оставить некоторые аргументы агента открытыми и сделать другие закрытыми . Открытые аргументы передаются как аргументы в call
: они предоставляются во время использования агента . Закрытые аргументы предоставляются во время определения агента . Например, если action2
имеет два аргумента, итерация
my_list.do_all ( действие агента2 ( ? , y ) )
итерации action2 (x, y)
для последовательных значений x
, где второй аргумент остается установленным на y
. Вопросительный знак ?
указывает на открытый аргумент; y
является закрытым аргументом агента. Обратите внимание, что базовый синтаксис agent f
является сокращением для agent f (?, ?, ...)
со всеми открытыми аргументами. Также возможно сделать цель агента открытой с помощью нотации {T}?
, где T
является типом цели.
Различие между открытыми и закрытыми операндами (операнды = аргументы + цель) соответствует различию между связанными и свободными переменными в лямбда-исчислении . Выражение агента, например, action2 (?, y)
с некоторыми закрытыми операндами и некоторыми открытыми, соответствует версии исходной операции, каррированной на закрытых операндах.
Механизм агента также позволяет определять агента без ссылки на существующую процедуру (например my_action
, my_condition
, , action2
), через встроенные агенты, как в
my_list.do_all ( agent ( s : STRING ) require not_void : s / = Void do s.append_character ( ' , ' ) ensure appended : s.count = old s.count + 1 end )
Встроенный агент, переданный здесь, может иметь все атрибуты обычной процедуры, включая предусловие, постусловие, спасательное предложение (здесь не используется) и полную сигнатуру. Это позволяет избежать определения процедур, когда все, что нужно, — это вычисление, которое должно быть упаковано в агента. Это полезно, в частности, для контрактов, как в инвариантном предложении, которое выражает, что все элементы списка являются положительными:
my_list . for_all ( агент ( x : INTEGER ): BOOLEAN do Результат := ( x > 0 ) конец )
Текущий механизм агента оставляет возможность ошибки типа времени выполнения (если процедура с n аргументами передается агенту, ожидающему m аргументов с m < n ). Этого можно избежать проверкой времени выполнения через предварительное условие valid_arguments
. call
Доступно несколько предложений по чисто статической коррекции этой проблемы, включая предложение об изменении языка от Рибета и др. [16]
Результат подпрограммы можно кэшировать, используя once
ключевое слово вместо do
. Непервые вызовы подпрограммы не требуют дополнительных вычислений или выделения ресурсов, а просто возвращают ранее вычисленный результат. Распространенный шаблон для «функций once» — предоставление общих объектов; первый вызов создаст объект, последующие вернут ссылку на этот объект. Типичная схема:
shared_object : SOME_TYPE once create Result . make ( args ) — Создает объект и возвращает ссылку на него через `Result'. end
Возвращаемый объект — Result
в примере — сам по себе может быть изменяем, но его ссылка остается прежней.
Часто «однажды выполняемые процедуры» выполняют требуемую инициализацию: несколько вызовов библиотеки могут включать вызов процедуры инициализации, но только первый такой вызов выполнит требуемые действия. Используя этот шаблон, инициализация может быть децентрализована, что позволяет избежать необходимости в специальном модуле инициализации. «Однажды выполняемые процедуры» по своему назначению и эффекту аналогичны шаблону singleton во многих языках программирования и шаблону Borg, используемому в Python.
По умолчанию "once routine" вызывается один раз на поток . Семантика может быть настроена на один раз на процесс или один раз на объект , квалифицируя ее с помощью "once key", например once ("PROCESS")
.
Eiffel предоставляет механизм, позволяющий осуществлять преобразования между различными типами. Механизмы сосуществуют с наследованием и дополняют его. Чтобы избежать путаницы между двумя механизмами, конструкция реализует следующий принцип:
Например, NEWSPAPER
может соответствовать PUBLICATION
, но INTEGER
преобразуется в REAL
(и не наследуется от него).
Механизм преобразования просто обобщает специальные правила преобразования (такие как, например, между INTEGER
и REAL
), которые существуют в большинстве языков программирования, делая их применимыми к любому типу, пока соблюдается указанный выше принцип. Например, класс DATE
может быть объявлен для преобразования в STRING
; это позволяет создать строку из даты просто через
моя_строка := моя_дата
как сокращение для использования явного создания объекта с процедурой преобразования:
создать my_string . make_from_date ( my_date )
Чтобы сделать первую форму возможной в качестве синонима второй, достаточно перечислить процедуру создания (конструктор) make_from_date
в convert
предложении в начале класса.
В качестве другого примера, если есть такая процедура преобразования, перечисленная в TUPLE [day: INTEGER; month: STRING; year: INTEGER]
, то можно напрямую присвоить кортеж дате, вызвав соответствующее преобразование, как в
Bastille_day := [ 14 , "Июль" , 1789 ]
Обработка исключений в Eiffel основана на принципах проектирования по контракту. Например, исключение возникает, когда вызывающий подпрограмму не может удовлетворить предварительное условие или когда подпрограмма не может гарантировать обещанное постусловие. В Eiffel обработка исключений не используется для управления потоком или для исправления ошибок ввода данных.
Обработчик исключений Eiffel определяется с помощью ключевого слова rescue . В разделе rescue ключевое слово retry выполняет процедуру снова. Например, следующая процедура отслеживает количество попыток выполнения процедуры и повторяет ее только определенное количество раз:
connect_to_server ( server : SOCKET ) — Подключиться к серверу или отказаться от попыток после 10. require server /= Void и затем server . address /= Void локальные попытки : INTEGER do server . connect sure connected : server . is_connected rescue if attempts < 10 then attempts := attempts + 1 retry end end
Однако этот пример, возможно, некорректен для чего угодно, кроме самых простых программ, поскольку сбой соединения следует ожидать. Для большинства программ имя процедуры вроде attempt_connecting_to_server было бы лучше, а постусловие не обещало бы соединение, оставляя вызывающему процессу принятие соответствующих мер, если соединение не было открыто.
Доступно несколько сетевых и потоковых библиотек, таких как EiffelNet и EiffelThreads. Модель параллелизма для Eiffel, основанная на концепциях проектирования по контракту, — это SCOOP , или простое параллельное объектно-ориентированное программирование , пока не являющееся частью официального определения языка, но доступное в EiffelStudio . CAMEO [17] — это (нереализованный) вариант SCOOP для Eiffel. Параллелизм также взаимодействует с исключениями. Асинхронные исключения могут быть проблемными (когда процедура вызывает исключение после того, как ее вызывающий завершил работу). [18]
Взгляд Эйфеля на вычисления полностью объектно-ориентирован в том смысле, что каждая операция относится к объекту, «цели». Так, например, такое сложение, как
а + б
концептуально понимается так, как если бы это был вызов метода
а . плюс ( б )
с целью a
, функцией plus
и аргументом b
.
Конечно, первый вариант является общепринятым синтаксисом и обычно предпочтителен. Синтаксис оператора позволяет использовать любую форму, объявляя функцию (например, в INTEGER
, но это применимо к другим базовым классам и может использоваться в любом другом, для которого подходит такой оператор):
плюс псевдоним "+" ( другое : INTEGER ): INTEGER -- ... Обычное объявление функции... конец
Диапазон операторов, которые можно использовать в качестве "псевдонима", довольно широк; они включают предопределенные операторы, такие как "+", а также "свободные операторы", состоящие из небуквенно-цифровых символов. Это позволяет разрабатывать специальные инфиксные и префиксные обозначения, например, в математических и физических приложениях.
Каждый класс может дополнительно иметь одну функцию, псевдонимом "[]", оператор "скобка", позволяющий использовать нотацию a [i, ...]
как синоним для того, a.f (i, ...)
где f
находится выбранная функция. Это особенно полезно для структур контейнеров, таких как массивы, хэш-таблицы , списки и т. д. Например, доступ к элементу хэш-таблицы со строковыми ключами можно записать
номер := телефонная_книга [ "ДЖИЛЛ СМИТ" ]
"Команды присваивателя" — это сопутствующий механизм, разработанный в том же духе, позволяющем использовать устоявшуюся, удобную нотацию, переосмысленную в рамках объектно-ориентированного программирования. Команды присваивателя позволяют синтаксису, похожему на присваивание, вызывать процедуры "установщика". Собственно присваивание никогда не может иметь форму , a.x := v
поскольку это нарушает сокрытие информации; вам придется использовать команду (процедуру) установщика. Например, класс хэш-таблицы может иметь функцию и процедуру
псевдоним элемента "[]" ( ключ : STRING ): ELEMENT [ 3 ] -- Элемент ключа `key'. -- (Запрос "Getter") do ... end put ( e : ELEMENT ; key : STRING ) -- Вставляет элемент `e', связывая его с ключом `key'. -- (Команда "Setter") do ... end
Затем, чтобы вставить элемент, необходимо использовать явный вызов команды setter:
[ 4 ] телефонная_книга . put ( Новая_персона , "ДЖИЛЛ СМИТ" )
Это можно эквивалентно записать как
[ 5 ] телефонная_книга [ "ДЖИЛЛ СМИТ" ] := Новый_человек
(таким же образом, что phone_book ["JILL SMITH"]
является синонимом number := phone_book.item ("JILL SMITH")
), при условии, что объявление item
now начинается (замена для [3]) с
псевдоним элемента "[]" ( ключ : СТРОКА ): ЭЛЕМЕНТ назначить положить
Это объявляет put
команду присваивателя, связанную с item
и, в сочетании с псевдонимом скобок, делает [5] допустимым и эквивалентным [4]. (Это также можно было бы записать, не используя скобки, как phone_book.item ("JILL SMITH") := New_person
.
Примечание: Список аргументов присваивателя a ограничен следующим образом: (возвращаемый тип a; весь список аргументов a...)
Eiffel не чувствителен к регистру. Токены make
, maKe
и MAKE
все обозначают один и тот же идентификатор. Однако см. «правила стиля» ниже.
Комментарии начинаются с --
(двух последовательных тире) и продолжаются до конца строки.
Точка с запятой, как разделитель инструкций, необязательна. В большинстве случаев точка с запятой просто опускается, за исключением случаев разделения нескольких инструкций в строке. Это приводит к уменьшению беспорядка на странице программы.
Нет вложенности деклараций функций и классов. В результате структура класса Eiffel проста: некоторые предложения уровня класса (наследование, инвариант) и последовательность деклараций функций, все на одном уровне.
Обычно функции группируют в отдельные «пункты функций» для большей удобочитаемости, при этом стандартный набор базовых тегов функций отображается в стандартном порядке, например:
класс HASH_TABLE [ ЭЛЕМЕНТ , КЛЮЧ -> ХЕШИРОВАННЫЙ ] наследует ТАБЛИЦА [ ЭЛЕМЕНТ ] функция -- Инициализация -- ... Объявления команд инициализации (процедуры создания/конструкторы) ... feature -- Access -- ... Объявления небулевых запросов к состоянию объекта, например, item ... функция -- Отчет о состоянии -- ... Объявления булевых запросов о состоянии объекта, например is_empty ... функция -- Изменение элемента -- ... Объявления команд, которые изменяют структуру, например, put ... -- и т.д. конец
В отличие от большинства языков программирования с фигурными скобками , Eiffel делает четкое различие между выражениями и инструкциями. Это соответствует принципу разделения команд и запросов метода Eiffel.
Большая часть документации Eiffel использует отличительные стилистические соглашения, призванные обеспечить единообразный внешний вид и восприятие. Некоторые из этих соглашений применяются к самому формату кода, а другие — к стандартному типографскому отображению кода Eiffel в форматах и публикациях, где эти соглашения возможны.
Хотя язык нечувствителен к регистру, стандарты стиля предписывают использование всех заглавных букв для имен классов ( LIST
), всех строчных букв для имен функций ( make
) и начальных заглавных букв для констант ( Avogadro
). Рекомендуемый стиль также предполагает подчеркивание для разделения компонентов идентификатора из нескольких слов, как в average_temperature
.
Спецификация Eiffel включает в себя рекомендации по отображению текстов программного обеспечения в форматах набора: ключевые слова выделены жирным шрифтом, определяемые пользователем идентификаторы и константы показаны в italics
, комментарии, операторы и знаки препинания в Roman
, а текст программы в blue
как в настоящей статье, чтобы отличить его от пояснительного текста. Например, программа "Hello, world!", приведенная выше, будет отображаться в документации Eiffel следующим образом:
класс HELLO_WORLD создать сделать функцию сделать сделать печать ( "Привет, мир!" ) конец конец
Eiffel — чисто объектно-ориентированный язык, но он обеспечивает открытую архитектуру для взаимодействия с «внешним» программным обеспечением на любом другом языке программирования.
Например, можно программировать операции на уровне машины и операционной системы на языке C. Eiffel предоставляет простой интерфейс для подпрограмм C, включая поддержку «встроенного C» (написание тела подпрограммы Eiffel на языке C, обычно для коротких операций на уровне машины).
Хотя между Eiffel и C нет прямой связи, многие компиляторы Eiffel ( Visual Eiffel — одно из исключений) выводят исходный код C как промежуточный язык для передачи компилятору C для оптимизации и переносимости . Таким образом, они являются примерами транскомпиляторов . Компилятор Eiffel tecomp может выполнять код Eiffel напрямую (как интерпретатор), не проходя через промежуточный код C, или выдавать код C, который будет передан компилятору C для получения оптимизированного собственного кода. В .NET компилятор EiffelStudio напрямую генерирует код CIL (Common Intermediate Language). Компилятор SmartEiffel также может выводить байт-код Java .
В меньшей степени на Ruby также оказали влияние Python, LISP, Eiffel, Ada и C++.