Форма вызова подпрограмм clone и equal является стилевой особенностью, которая может вызвать удивление. На первый взгляд нотация:
clone (x) equal (x, y)
выглядит не слишком объектно-ориентированной. Догматичное следование принципу "ОО-стиля вычислений" из предыдущей лекции предполагает другую форму (См. "Объектно-ориентированный стиль вычислений", лекция 7):
x.twin -- twin это двойник - клон. x.is_equal (y)
В первой версии нотации так и делалось, однако возникла проблема пустых ссылок. Вызов компонента вида x.f (...) не может быть корректно выполнен в случае пустого x во время выполнения. В этом случае вызов инициирует исключение, которое повлечет аварийное завершение всей системы, если в соответствующем классе не предусмотрена обработка исключений. Поскольку во многих случаях x может быть действительно пустой ссылкой, то это означало бы, что каждый вызов twin должен предусматривать охрану и выглядеть так:
if x = Void then z := Void else z := x.twin end
Соответственно, реализация вызова is_equal должна выглядеть (and then является вариантом and. См. "Нестрогие булевы операции", лекция 13):
if ((x = Void) and (y = Void)) or ((x /= Void) and then x.is_equal (y)) then ...
Излишне говорить, что не следует придерживаться этих соглашений. Нам быстро надоест писать подобные витиеватые фрагменты, а когда мы забудем это сделать, то результатом будет ошибка времени выполнения. Окончательный вариант соглашений, сформулированный в данной лекции, замечателен еще и тем, что дает ожидаемые результаты для x, равного void, - clone (x) вернет void, а equal (x, y) вернет true, если и y - void.
Вызов процедуры copy в форме x.copy (y) не создает подобных проблем, поскольку требует непустых x и y. Это следствие семантики процедуры copy, копирующей поля одного объекта в поля другого, и имеющей смысл, только если существуют оба объекта. Как показано далее, такое условие для y фиксируется формальным предусловием copy, заданным в явном виде в документации.
Отметим, что введенная выше функция is_equal существует в библиотеке системы. Причина в том, что часто удобнее определить специфические варианты эквивалентности элементов конкретного класса, перекрыв семантику по умолчанию. Для достижения этого эффекта достаточно переопределить функцию is_equal в соответствующем классе. Функция equal определяется в терминах is_equal (выражением, показанным выше при иллюстрации использования is_equal), и поэтому следует за всеми переопределениями is_equal.
Когда есть функция clone, то нет необходимости в twin. Это связано с тем, что функция clone определена как создание объекта с последующим вызовом copy. Поэтому для адаптации clone к специфике класса достаточно переопределить процедуру copy данного класса. (См. также лекция 16)