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 и в дальнейшем будет использоваться в Канторе.

Цели

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

14 мая 2015 г.

Фрактальная модель: теория объектно-ориентированной ОС

Эта статья продолжает серию публикаций о фрактальной модели, лежащей в основе объектно-ориентированной ОС, реализация которой запланирована в рамках проекта. Предыдущие статьи серии давали определение объектно-ориентированной ОС и подчеркивали важность наследования в ООП.

Данная статья основана на материалах 2012 года, публиковавшихся на предыдущем сайте проекта и даже упомянутом в качестве литературы к одной из статей конференции «Объектные системы — 2013» (см. ссылки в конце статьи).

Сейчас текст адаптирован под текущую ситуацию в проекте и публикуется как предварительный материал к описанию фрактальной модели как ключевой концепции. Изначально планировалось разбить его на несколько частей, но поскольку тема не развивалась с 2013 года, решено опубликовать всё в одной статье, закрыв технический долг по теориям ООП.

21 марта 2015 г.

Множественное наследование и взаимная абстрактность

Этот пост по-быстрому адаптирован из моего ответа на «Тостере» ради краткого изложения некоторых принципов ООП в Канторе.

Принципы SOLID покрывают общий случай наследования в ООП, когда классы могут наследоваться друг от друга без посредничества интерфейсов. Следование SOLID должно способствовать нормализации классов, чтобы получилась сбалансированная ОО-система. При этом в самих SOLID явного правила нормализации нет, оно появляется в конкретной реализации:
Множественное наследование допустимо только от взаимно-абстрактных классов, — то есть классов, не имеющих реализации одинаковых методов. Одинаковость методов в языке определяется совместимостью по присваиванию с учетом ООП.
В языках с интерфейсами интерфейсы всегда взаимно-абстрактны реализующим их классам, поэтому классы могут реализовывать любое количество интерфейсов, принципы SOLID от этого не пострадают. Собственно, интерфейсы были введены в языки программирования ради упрощения модели наследования.

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

Если все унаследованные свойства имеют разные сигнатуры, классы взаимно-абстрактны. В вырожденном случае взаимная абстрактность определяет, являются ли классы интерфейсами друг другу.

13 февраля 2015 г.

Неоднозначность разбора и возможный возврат elsif

Похоже, ключевое слово elsif придется вернуть в язык, поскольку возникает неоднозначность. На фоне предновогодней эйфории я больше думал над синтаксисом со стороны отекстовки, а посмотреть со стороны синтаксического разбора забыл.

Неоднозначность

Без elsif при синтаксическом разборе всегда возникает неоднозначность, поскольку на момент входа в оператор еще не известно, единственный он во вложенном блоке или нет. Все последующие блоки также под вопросом, поскольку теперь не понятно, какой end какой блок завершает:
if условие1 then
  операторы;
else if условие2 then
  операторы;
end;
  операторы;
end; /* это лишний end или завершение предыдущего if,
        не единственного в блоке? */
С привычными отступами выглядит понятней:
if условие1 then
  операторы;
else
  if условие2 then
    операторы;
  end;
  операторы;
end;
Проблема характерна для любого языка с блочными операторами ветвления и свободной записью: SQL, Модула-2, частично Ада.

В Канторе я вижу два выхода:
  • Вернуть elsif и получить тонны ненависти от программистов, не любящих это ключевое слово.
  • Не бояться «лишних» end при разборе. Тут нужен дополнительный анализ — что делать с возможными неоднозначностями. В любом случае это усложнит компилятор и затруднит проверку синтаксиса.
Возвращение elsif, если случится, может быть частичным, — только для алгоритмических блоков. В выражениях с if/case вполне может остаться принудительное совмещение.

Принудительное совмещение в выражениях

В выражениях с if/case внутри веток могут стоять только одиночные значения или кортежи, поэтому принудительное совмещение не приводит к неоднозначности и elsif не нужен:
a = if условие1 then
  значение1 // в выражениях точка с запятой не ставится
else if условие1 then
  значение2
  // из-за отсутствия точки с запятой никакие другие операторы
  // тут недопустимы
end;
Пример с кортежем отличается только числом значений:
a, b, c = if условие1 then
  значение1, значение2, значение3 // точка с запятой опять не ставится
else if условие2 then
  значение4, значение5, значение6
end;