Сергей Губанов писал(а):
Зачем?
Я чего-то не понимаю, чего тут многие не понимают. Может быть, это я не так понимаю, и мне только кажется, что я понял. А кажется мне вот что: иногда NIL нужен, иногда - мешает. Тут было много длинных слов сказано, а мы консерваториев не кончали, поэтому постараюсь попроще изложить.
По сути NIL - это константа универсального типа, совместимая по присваиванию с любым указателем. Универсальность её оборачивается проблемой, особенно в тех задачах, где NIL по семантике не нужен, а реализация требует указателей (ибо куда без них?). Предлагается отказаться от NIL, а максимально своевременную инициализацию указателей проконтролировать уже на этапе компиляции. Как это реализовать - вопрос десятый. Например, заставить вместо NEW писать специализированные процедуры-конструкторы, и следить, чтобы в них было присвоение для каждого поля-указателя создаваемого объекта. Другой вариант - каким-то образом отслеживать факт неинициализированности значения и выдавать ошибку при доступе по указателю - опять же, на этапе компиляции, примерно так, как компилятор может предупреждать об использовании значения неинициализированной локальной переменной. Не важно, как именно реализовать, но идея - такая.
Соответственно, те задачи, в которых NIL не требуется, смогут быть уверены, что NIL у них не встретится. А те задачи, где NIL нужен - им сам Бог велел завести себе типизированную константу с аналогичным смыслом. Например, так:
Код:
MODULE Lists;
IMPORT Log;
TYPE
List* = POINTER TO ABSTRACT RECORD
next*: List
END;
Nil = POINTER TO RECORD (List)
END;
VAR nil-: List;
PROCEDURE (lst: List) Do- (), NEW;
BEGIN
END Do;
PROCEDURE (lst: Nil) Do ();
BEGIN
Log.String ("Lists.nil.Do() attempt!"); Log.Ln;
(* HALT (100); -- по вкусу! -- *)
END Do;
PROCEDURE Init;
VAR tmp: Nil;
BEGIN
NEW (tmp);
nil := tmp;
END Init;
BEGIN Init;
END Lists.
Дальнейшая работа - как описано в посте выше. Вместо
WHILE list.next # NIL DO ...
пишем
WHILE list.next # Lists.nil DO ...
Преимущества: присвоить беcтиповый NIL указателю next компилятор не даст, позволит только создать копию значения Lists.nil (или любой другой переменной подходящего типа, которая тоже не может быть = NIL). Соответственно, в случае плохого кода мы получим не гарантированную смерть, а предусмотренный механизм обработки. Ожидается, что большая часть - фатальных! - ошибок отфильтруется на этапе компиляции, а в рантайме останутся только такие, когда по ошибке передан не тот объект или не в ту процедуру.
Я правильно понимаю суть обсуждения?