Вечный (но лучше нет) вопрос функции без скобочек и параметров


#1

Чистя OpenCLABC от костылей, в связи с исправлениями нескольких багов - я написал эту строчку:

  if (prev_ev<>cl_event.Zero) or (Func0&<T>(self.f) <> nil) then

(self.f имеет тип ()->T)

Но потом задумался, а надо ли там явное преобразование к Func0, чтоб удостоверится что идёт проверка - содержит ли self.f значение. То есть чтоб там был точно НЕ вызов. И - таки нет, не нужно. Но просто убирать преобразование я боюсь, вдруг в будущем изменится. А компилироваться от этого код не перестанет, вместо этого получим неопределённое поведение, которое фиг отследишь. В связи с этим:

Предлагаю определится и записать куда то типа справки - что происходит если у функции без параметров не поставить скобки. Когда вызов, а когда получение ссылки?

На сколько я понимаю как работает сейчас - есть 2 поведения:

  1. Если написано имя функции
  2. Если написано имя переменной, которая является ссылкой на функцию

В обычной ситуации, для имени функции - происходит вызов, а для переменной - получение ссылки.

Особая ситуация (с поведением - противоположным обычному) происходит тогда, когда тип, получаемый обычным поведением - не подходит:

function f: object;
begin
  writeln('вызов');
  Result := nil;
end;

begin
  
  // особые ситуации:
  var v: ()->object := f; // точно получение ссылки, потому что тип переменной указали явно
  var o: object := v; // точно вызов, по той же причине
  
  // обычные ситуации:
  writeln(f = nil); // точно вызов, потому что имя функции, а в выражение можно подставить и object и ()->object
  writeln(v = nil); // точно получение ссылки, потому что v - переменная
  
end.

@Admin, @ibond это поведение такое - как вы задумывали, или где то тут ошибка?

И в любом случае, после этого ответа - есть ещё несколько каверзных вопросов по ситуации, включая некоторые осложнения с where и не только.


#2

Идея следующая. Имя функции не является вызовом только если оно присваивается функциональной переменной. В остальных выражениях это вызов. Исключение - сравнение с nil.

Процедурная переменная по идее ведет себя так же.

Однако есть баг, который надо устранять:

function f(): integer;
begin
  writeln('вызов');
  Result := 666;
end;

begin
  var v: ()->integer := f;
  Print(f=nil)
end.

Вывод: вызов False

Это - совсем безобразное поведение.

Написал это в Issue


#3

То есть это неправильное поведение:

function f(): integer;
begin
  writeln('вызов');
  Result := 666;
end;

begin
  var v: ()->integer := f;
  var v2 := v; // вызова нет
end.

?


#4

Правильное.


#5

Ну так вы же сказали

Значит всё же не так же, а как раз наоборот?


#6

Нет, процедурная переменная всё же ведёт себя не так же. Мы склоняемся к тому, что без скобочек она должна возвращать ссылку


#7

Ну хорошо, тогда почему:

function f(): integer;
begin
  writeln('вызов');
  Result := 666;
end;

begin
  var v: ()->integer := f;
  var v2: object := v; // вызов есть
end.

Я предлагаю сделать так, чтоб процедурная переменная всегда возвращала ссылку всегда, кроме случаев, когда её тип не подходит к тому, чему она присваивается.

То есть первый код из этого сообщения должен быть получением ссылки. А этот:

function f(): integer;
begin
  writeln('вызов');
  Result := 666;
end;

begin
  var v: ()->integer := f;
  var v2: integer := v;
end.

Должен быть вызовом, потому что нельзя преобразовать тип ()->integer к integer.


#8

В Паскале при вызове функции без параметров разрешено не указывать пустые скобки.

  1. Если F - имя функции без параметров, то появление F в правой части оператора присваивания предполагает вызов функции F. Исключением является случай, когда в логическом выражении идет сравнение с nil по типу F = nil. В этом случае функция не вызывается.
  2. Из (1) следует, что конструкция вида var G := F трактуется, как вызов функции F, создание переменной G необходимого типа и присваивание ей значения, которое возвращено функцией F.
  3. Из (2) имеется исключение, когда G является процедурной переменной подходящего типа. В этом случае функция F не вызывается, а в G заносится ссылка на F. Каким способам описана процедурная переменная (например, через лямбду или более традиционно) - значения не имеет.
  4. Если имя функции передается как фактический параметр в другую функцию/процедуру, то в теле этой (вызываемой) функции/процедуры снова происходит все, описанное в (1-3).

Я что-то не учел?


#9

Ну и… Если это текст для справки - стоит перечитать и подправить.


#10

Это что сейчас было такое? И для кого?


#11

Для вас. Или я что то не понял и “способАм” - это так и задумывалось? Ну и я не самый грамотный человек, поэтому и говорю, лучше перечитайте сами и проверьте, если этот текст писался для справки.


#12

Столько написали, чтобы указать на элементарную и очевидную описку? Трижды процитировали? Этот текст не предназначается для справки, Справку пишут разработчики. Это всего лишь размышление в форме такого небольшого алгоритма, как на мой взгляд это все должно работать. Не более того.


#13

Первая из 3 цитат вас была не про это.

Я поддерживаю идею, что процедурные переменные и сами функции должны работать одинаково, это сильно упростило бы работу с процедурными переменными.

Но вы нигде в своём высказывании не упомянули что это личное мнение. Выглядело так, будто это уже утверждённое поведение функций. А раз это противоречит тому, что сказал разработчик выше - наиболее вероятным было то, что вы не читали предыдущие 7 сообщений. Именно по этому поводу была первая цитата.


#14

Товарищи, ночью спать надо!


#15

Опишем ))


#16

Да я сто раз уже писал, что если помещаю сюда что-то, то это либо личное мнение, либо я указываю на источник тем или иным способом. И третьего не бывает.

Это отлично! Лишь бы не вистами… ))


#17

Здесь често говоря шредингер. Нельзя точно сказать, когда будет вызов, когда ссылка. Вирт (или кто там) в погоне за “простотой” или лишь бы не так в С заложили тут бомбу. Вот приходится терпеть.


#18

Почему нельзя? Нельзя угадать, но можно наложить четкие правила, когда передается ссылка без вызова. Это бывает нужно для: присваивания процедурной переменной, сравнения пары процедурных переменных и проверки процедурной переменной на nil. В остальных случаях следует вызов.

Что до Вирта, он заложил только одну бомбу, да и та появилась лишь позднее в развитие его идей: невозможность отличить символ от односимвольной строки. Вот тут уж точно приходится терпеть.


#19

Что вы имели в виду под этим?


#20

Когда писал, думал немного о другом. Для функций это неактуально, только для процедур. Поправил.