Оберон-технология: особенности и перспективы |
Тематика обсуждения: Оберон-технология. Особенности, перспективы, практическое применение.
Всего в теме 6256 сообщений
Добавить свое сообщение
Отслеживать это обсуждение Обсуждение из раздела Школа ОБЕРОНА
№ 2716 15-02-2007 08:33 | |
Ответ на »сообщение 2714« (pepper)
___________________________
Ты все усложняешь...
Напротив. Упростим задачу до минимума. Есть набор правил (инварианты). Есть сущность, по отношению к которым инварианты должны выполняться. Как? Три подхода:
1. Инварианты гарантируются корректными операциями над сущностью.
2. Инварианты гарантируются предварительными проверками перед каждым обращением к сущности (ASSERT).
3. Комбинация 1 и 2 (именно это и предлагаю).
Очевидно, что в постановке задачи (как дополнительный фактор) надо учесть эволюцию системы. Она постоянно развивается и то, что написано сегодня, завтра может быть изменено (в том числе и другими).
В первом варианте проблема в том, что если к сущности будет получен доступ, минуя "уполномоченных", то кранты. Если код, реализующий инварианты, запутанный (здесь помимо проверки условий уже "вшиваются" действия), то работа может вестись с некорректным объектом и об этом никто знать не будет (худший сценарий).
Во втором варианте проверки размазываются по тексту, вносят дополнительные накладные расходы, не гарантируют, что их везде поставят перед использованием, но зато в случае использования дают надежность. Что такое проверочный код при контроле выхода массива за границы? Те же неявные ASSERT. Только ставит их компилятор автоматически, а потому не забудет. Чистые инварианты (вариант 2) при эволюционировании системы чувствительны к изменениям в меньшей степени, чем операции, в которых перемешаны эти инварианты (вариант 1).
Расставляя в каждом методе pred-условия, я могу лишь гарантировать, что все попытки использования невалидного объекта будут обнаружены.
Что есть фича, а что баг? :) Кто это определяет и в чьей голове сидит данное определение? Если гарантируется, что с некорректным объектом работа недопускается, то это никак не влияет на наличие-отсутствие реализаций замысла. Верифицировать логику (все ветви и все варианты) надо не охранниками, а иначе. Это лишь предохранитель. Не более.
№ 2715 15-02-2007 08:32 | |
Ответ на »сообщение 2714« (pepper)
___________________________
В Обероне ближайший аналог конструктора -- фабричная функция.
В КП можно гарантировать (с помощью компилятора), что объект будет создан только посредством фабричной функции.
Это можно сделать как минимум двумя путями: с помощью LIMITED (конкретный тип экспортируется, но создать его самому нельзя) и с помощью ABSTRACT (конкретный тип не экспортируется, прячась за абстрактным интерфейсом).
№ 2714 Удалено модератором | |
№ 2713 15-02-2007 05:37 | |
Ответ на »сообщение 2712« (pepper)
___________________________
Разве оберон не позволяет экспортировать только определенные поля объекта и связанные процедуры, запретив таким образом использование неинициализированного объекта?
Для поддержания "целостности" совсем не обязательно проверять перед каждым использованием. Можно и только применять избранные "корректные" процедуры/методы. Но если pred-проверка гарантирует, что оперируете целостной вещью, то во втором случае должны молиться, что нигде не просмотрели, ведь работаете по живому, без наркоза, без права на ошибку. В идеале эти вещи надо сочетать, понимая где родной, "лояльный" контекст (напр., внутри модуля), а где -- чужой, "зашумленный" участок. Где включить автопилот, а где перейти на ручное управление.
В Обероне, конечно, можно экспортировать определенные поля (включая и процедурные), а в Обероне-2 и КП -- еще и с контролем по чтению-записи (read-only). Но это все те же -- "ручки". Вопрос, как я понимаю, шел об "автомате", при том инициализирующем.
В принципе в том же классическом Обероне вполне можно расширить на уровне run-time семантику NEW, в дополнение к NEW(v) ввести еще и NEW(v,pred,post), где pred, post: PROCEDURE(v); -- инициализатор и финализатор. Вещь элементарная. Раз не сделали, значит, считали, что хлопот и дыр будет больше. А кто мешает написать вполне законный свой собственный MySystem и в нем свою MySystem.New, через которую и работать "под контролем" так, как надобно? Единственно потребуется иметь у сборщика "ручку переключения" синхронности/асинхронности сборки. Так в идеале у него неплохо бы иметь еще и другие ручки тюнинга: разные стратегии сборки, да еще с делением по областям (у каждой своя стратегия, независящая от других). Плюс еще понавтыкать туда различие по времени жизни (block, procedure, module, component и т.д.). Но это все спокойно делается вне контроля компилятора, то бишь ручками.
№ 2712 Удалено модератором | |
№ 2711 15-02-2007 04:39 | |
Ответ на »сообщение 2708« (Антон Григорьев)
___________________________
Хотелось бы поднять такую тему: как в оберонах гарантировать правильную инициализацию объектов?
Обычная практика -- использование фабричных функций.
Например:
PROCEDURE NewSomeObject() : SomeObject;
VAR p: SomeObject;
BEGIN
NEW(p);
(* здесь инициализация... *)
RETURN p
END NewSomeObject;
Правда, фабричные функции работают только с объектами кучи.
С другой стороны, конструктор по определению не может быть "виртуальным", поэтому даже в Си++ часто прибегают к фабричным функциям и методам (например, порождающие паттерны GoF).
В Компонентном Паскале для поддержки инвариантов введен спецификатор LIMITED, при использовании которого объект может порождаться только в рамках модуля, в котором объявлен LIMITED тип.
ИМХО, близкого результата можно добиться и в Обероне-2 (и даже в классическом Обероне), если не экспортировать объявление конкретного типа, а все методы объявлять для указателей:
PROCEDURE (p: SomeObject) SomeMethod ;
Развитием той же идеи являются фабричные объекты-каталоги в ББ.
№ 2710 15-02-2007 03:26 | |
Ответ на »сообщение 2708« (Антон Григорьев)
___________________________
Хотелось бы поднять такую тему: как в оберонах гарантировать правильную инициализацию объектов?
Вопрос хороший. Если говорить только о классическом Обероне и смотреть с операционной точки зрения, то гарантировать -- собственными силами (специальными процедурами/методами).
Но раз уж речь зашла о прецедентах в других языках и тем более о контроле выхода за границы массива, то стоит посмотреть на это и с концептуальной точки зрения.
1. Язык программирования оперирует сущностями (остановимся на представителях типов и классов, соответственно - на переменных и объектах).
2. Каждая из этих сущностей имеет свои фазы жизненного цикла -- объявление, порождение, использование, утилизация.
3. Каждая из сущностей может иметь безусловные ("пожизненные") и условные (динамически изменяемые в зависимости от контекста) ограничения, которые накладываются на ее состояние и использование. Элементарным примером может служить
TYPE Date =
RECORD
day : [1..31];
month : [1..12];
year : [0..3000]
END;
Очевидно, что (концептуальная) корректность ("целостность", когерентность, согласованность) даты требует применения нескольких правил (инвариантов), как то: недопустимость 30 и 31 февраля, високосность, компенсация по границе столетий и т.д. Эта концептуальная корректность должна обеспечиваться не только и не столько на фазе порождения сущности, сколько на фазе ее использования.
После объявления сущность должна находиться в состоянии неопределенности (UNDEFINED), которое должно порождать исключение (ошибку) при попытке использования (не модификации) сущности. На фазе порождения (напр., конструктором) она должна вводиться в согласованное состояние. А на фазе использования гарантировать сохранение целостности. На фазе утилизации (напр., деструктор) должна гарантироваться согласованность других сущностей (и контекста) после изъятия данной сущности из обращения.
Что есть определение границ массива: это безусловное ограничение, при нарушении которого "исчезает" сама сущность, а не просто нарушаются значения ее компонентов. Согласование значений (состояний) -- это условное ограничение. В этом большая разница.
Чем объект (как сущность) лучше переменной (как сущности) с точки зрения программной системы и ее надежности? Ничем. Они равноправны. Следовательно, надо гарантировать контроль целостности обеих на всех фазах жизненного цикла. Полумеры -- это иллюзия надежности, это еще хуже, чем точное знание о том, что надежность нужно обеспечивать.
Выше я специально привел пример не на Обероне, а на Модуле-2. Из Оберона, как известно, перечисление было изъято -- это еще один шаг к тому, что инварианты надо обдумывать и обеспечивать всесторонне (на всех фазах), а не только в отношении области значений или целостности при порождении.
Операционная точка зрения исходит из удобства использования (если можно добавить еще одну проверочку, еще один частный случай, чтобы не заморачиваться потом программисту, да еще с небольшими накладными расходами, то почему и не добавить). Это прагматическая фрагментарность решений. Частности порождают потерю контроля сложности. В результате программист должен не опираться на четкую аксиоматику и небольшой набор правил ("таблицу умножения"), а хранить в голове уйму частностей, полезных для каждого особого случая, да еще полагаться на чужой run-time и чужие библиотеки, которые должны выручить в сложной ситуации.
Концептуальная точка зрения исходит из продуманности и сбалансированности на абстрактном (математическом) уровне. Это просто разные взгляды. Каждый из них имеет свое право на жизнь.
№ 2709 15-02-2007 02:36 | |
Сегодня проф. Вирту исполняется 73 года. К его дню рождения на europrog.ru опубликованы редкие работы 1963-1993 гг., включая его работу со знаменитым Гарри Хаски (в Berkeley) и материалы Стенфордского периода, предваряющего работы над Паскалем. Тамже подборки по первому языку Вирта -- EULER. Гарри Хаски работал вместе с Аланом Тьюрингом в National Physical Lab в предместье Лондона над компьютером ACE и по возвращению в Штаты был научным руководителем Никлауса Вирта в Berkeley.
В свете тематики Королевства хочу обратить особое внимание на выступление Вирта по истории разработки Паскаля (History of Programming Languages. Recollections about the Development of Pascal) на конференции HOPL-II (1993). Важные дополнительные материалы по этому выступлению, включая стенограмму со слайдами и ответы на вопросы авторов других языков конференции HOPL, будут представлены позднее. Они не вошли в ACM SIGPLAN Notices, а были опубликованы отдельным томом.
№ 2708 14-02-2007 22:42 | |
Хотелось бы поднять такую тему: как в оберонах гарантировать правильную инициализацию объектов?
Состояние объекта определяется значением его полей, и не любая комбинация этих значений даёт допустимое состояние. Поэтому первым действием с объектом должна становиться его инициализация. В языках Delphi/C++/C#/Java есть понятие конструктора, и синтаксис не допускает создания объектов без вызова этого самого конструктора. А в Обероне? Можно предусмотреть для инициализации процедуру (обычную в Обероне или связанную с типом в Обероне-2) и написать в руководстве, что эта процедура должна вызываться раньше всех остальных. Но будет ли она действительно вызвана, решает тот, кто будет пользоваться соответствующим типом, синтаксис языка его к этому не принуждает. Очень напоминает ситуацию с границами массивов в С/С++ - они могут быть известны, но ответственность за их соблюдение компилятор не несёт. Получается, что надёжность оберонов в смысле инициализации объектов находится на том же уровне, что и надёжность С/С++ в смысле проверки попадания индекса массива в его диапазон?
№ 2707 12-02-2007 02:50 | |
Добавить свое сообщение
Отслеживать это обсуждение
Дополнительная навигация: |
|