Logo

22 апреля 2016 г.

Этюд 10. Логические и битовые операции

На сайте compiler.su есть статья: «Так ли нужны операции «&&», «||» и «^^»?». Затронутая в ней тема очень интересна, особенно для функционального языка с выводом типов.

На данный момент многие вещи, которые в традиционных языках решаются усложнением синтаксиса и/или конечного кода, в Канторе решены перегрузкой функций. Нельзя ли решить перегрузкой проблему разнотипности операций? В чем вообще проблема?

В других языках

В Delphi для логических и битовых операций используются одни и те же ключевые слова — not, and, or и xor, поэтому в выражениях нужно использовать скобки:
MaybeUTF8 := (FirstByte and $С0 = $80) and (NextByte and $С0 = $80);
В SQL проблемы нет, но в нем нет ни битовых операций, ни логического типа (зачастую). Например, в Oracle есть только встроенная функция bitand и нет логического типа:
select nvl((select 1 from dual where bitand(first_byte, 192) = 128 and
  bitand(next_byte, 192) = 128), 0) maybe_utf8 from dual;
Для получения требуемого результата пришлось использовать числа 0 и 1, играющие роль логических значений, и встроенную функцию nvl, чтобы превратить null в 0. В целом код не слишком очевиден, но из него видно, что логические и битовые операции в SQL различаются, как и в Си. Напоминаю, что именно SQL является примером для подражания Кантора.

В Канторе

Поскольку в данный момент принято, что битовые и логические операции в Канторе различны, наш пример записывается без скобок:
maybeUTF8 = firstByte & $С0 = $80 && nextByte & $С0 = $80;
В сравнении вместо = можно писать ==, а вместо символьного && — ключевое слово and, то есть можно и так:
maybeUTF8 = firstByte & $С0 == $80 and nextByte & $С0 == $80;
Навскидку не могу сказать, какая запись наглядней и чище.

Что поменяется, если мы вдруг воспользуемся перегрузкой функций по типу и захотим использовать одни и те же символы/слова для записи обоих видов операций? Чисто внешне поменяется вроде бы мало:
maybeUTF8 = firstByte and $С0 == $80 and nextByte and $С0 == $80;
А вот с точки зрения синтаксического разбора мы придем к ситуации в Delphi, поскольку у предложенной записи возможны несколько интерпретаций, в том числе и такая:
maybeUTF8 = (firstByte and $С0) == ($80 and nextByte) and ($С0 == $80);
Сразу понятно, зачем в Delphi нужны скобки. Можно попытаться решить задачу установкой приоритетов, но они всегда будут неочевидны для рядового программиста, что превратит код в ребусы, а программирование на Канторе — в составление и разгадывание ребусов... По части ребусов как-то не хочется отбирать пальму первенства у Си, перед Кантором стоит противоположная задача.

Справедливости ради нужно отметить, что разделение битовых и логических операций в Си — одно из немногих различий в синтаксисе, сделанных в Си лучше Паскаля.

Нашим последним спасением может стать вывод типов, имеющийся в Канторе как и в любом другом функциональном языке. Если из-за множественной перегрузки вывод неоднозначен, можно использовать оператор-подсказку is:
maybeUTF8 = (firstByte and $С0 == $80) is Core:Boolean and
  (nextByte and $С0 == $80) is Core:Boolean;
Однако, увы, результат не соответствует ожиданиям. Поскольку оператор is имеет наивысший приоритет, для применения его к части выражения выводимое подвыражение надо заключать в скобки, что сводит на нет весь смысл, — со скобками и без is всё понятно. Стало быть, нововведение не состоялось: в борьбе за бесскобочный синтаксис битовые и логические операции должны быть различны.

Напоминаю, что естественная запись операций в Канторе является лишь синтаксическим сахаром к вызовам функций, а в объектном коде всё хранится «как в Лиспе»:
new from firstByte.BitAnd($С0).Equals($80).And(
  nextByte.BitAnd($С0).Equals($80)
) as maybeUTF8;

9 апреля 2016 г.

Форматирование строк

Форматирование строк в Канторе будет реализовано как предметно-ориентированный язык (DSL). Символ традиционный — знак процента:
s = {%Строка {%} из {%}.%}.AsString[{index, count}];
Если свойству AsString будет сопоставлена операция *, вызов можно записать и так:
s = {%Строка {%} из {%}.%} * {index, count};
Формат {%} подразумевает, что тип сопоставляемого выражения будет передаваться через RTTI, как это сделано в Delphi. Соответствующая реализация в CoreLite пока находится в разработке (отложена), но повлияла на Кантор.

На Delphi

Штатная функция SysUtils.Format:
s = Format('Строка %d из %d.', [Index, Count]);
Удивительно, но формируемые компилятором Delphi сведения о типе передаваемых параметров лишь ограниченно используются в стандартных функциях RTL/VCL — только для генерации исключений о несоответствии типа. В известных мне форматтерах строк Delphi нет символа, соответствующего понятию «тип из параметра».

Поскольку необходимая информация передается, соответствующая реализация возможна. Формат «тип из параметра» будет реализован в CoreLite и в дальнейшем будет использоваться в Канторе.

Цели

Сейчас все борятся за безопасность кода и статический анализ, а в Канторе его можно сразу встроить в язык, благо общий механизм для этого есть — расширения в виде предметно-ориентированных языков.