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;

3 комментария

  1. > запись операций в Канторе является лишь синтаксическим сахаром к вызовам функций

    А как же ленивость операций? Нельзя будет писать такой код:
    if (Length(a) > 0) and (a[0] = 5)
    ?

    ОтветитьУдалить
    Ответы
    1. В Канторе функции — как представления в БД: ничего не хранят, вычисляются на лету, перед вычислением ядро строит планы и пропускает через оптимизатор. Поэтому в Канторе все функции ленивы. Раз встроенных нет, оптимизатор придется писать по-честному.

      Удалить
  2. and лучше чем &&, потому что снижает количество опечаток

    ОтветитьУдалить

Выбрав в выпадающем списке пункт «Имя/URL», можно оставить комментарий от своего имени без предварительной регистрации.