Logo

27 мая 2014 г.

Блочная форма записи условий

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

Решение языка Кантор — дать возможность записывать условия в виде блоков с отступом, разделяя отдельные предложения точкой с запятой. Поскольку на практике чаще всего используются связки and и or, блочные формы введены именно для них.

Блок all — блочный and

Для наглядности блочные условия рассматриваются на примере оператора if. Предположим, что у нас есть следующая функция:
with
  var Core:Word screenWidth = 1280, screenHeight = 1024;
do
  onScreenColor[Core:Integer x, y; Core:Word color] =
    if x >= 0 and x < screenWidth.AsInteger and y >= 0 and y < screenHeight.AsInteger then
      color
    else
      -1.RawWord
    end;
end;
Из-за строгости сравнения знаковых и беззнаковых целых условие получилось громоздким. Можно без всяких блоков записать его в две строчки, но в байт-коде они не сохранятся, и в будущем отекстовщик не будет знать, где поставить перевод строки.

Для преодоления этой проблемы в языке Кантор предлагается оператор all — блочная форма логического оператора and:
with
  var Core:Word screenWidth = 1280, screenHeight = 1024;
do
  onScreenColor[Core:Integer x, y; Core:Word color] =
    if 
      all
        x >= 0 and x < screenWidth.AsInteger;
        y >= 0 and y < screenHeight.AsInteger;
      end
    then
      color
    else
      -1.RawWord
    end;
end;
Как и везде, строчные и блочные операторы можно произвольно комбинировать, а блоки могут вкладываться друг в друга. Каждый блочный оператор требует завершающего end.

Имея в синтаксисе блочный оператор, достаточно лишь придумать правило для отекстовщика, когда его применять. В примере выше использована группировка по аргументам.

Блок any — блочный or

Пусть имеется некая функция с условием or:
onScreenMsg[Core:Integer what, where1, where2] =
  if what < where1 or what < where2 then
    'меньше'
  else
    'не меньше'
  end;
В блочной форме вместо or используется any:
onScreenMsg[Core:Integer what, where1, where2] =
  if 
    any
      what < where1;
      what < where2;
    end
  then
    'меньше'
  else
    'не меньше'
  end;
Тут также требуется завершающий end. Во всех примерах блочная запись используется внутри строчного оператора, поэтому завершающая точка с запятой после end не ставится.

26 мая 2014 г.

Оператор if как подмножество case

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

Приведение if к case

Поскольку булев тип является обычным перечислимым типом, оператор if может быть записан в форме case над булевым выражением:
if выражение then
  КогдаИстинно(1);
  КогдаИстинно(2);
else
  КогдаЛожно;
end;

case выражение
  when True then
    КогдаИстинно(1);
    КогдаИстинно(2);
else
  КогдаЛожно;
end;
Обе записи синтаксически допустимы, логически тождественны, и потому должны порождать идентичный байт-код.

Оператор следования

Выявленные условия заставляют пересмотреть синтаксис операторов if и case, описанный ранее.

Операторы if и case объединяются в единый оператор следования, имеющий общую форму case и сокращенную if, используемую для перечислимых типов с двумя значениями. Так if становится синтаксическим сахаром для для частного случая case, причем не только для булева типа.

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

Оператор следования теперь выглядит так:
case выражение1
  when значение1_1 then
    ветвь;
  when значение1_2 then
    ветвь;
  when значение1_3 then
    ветвь;
elsif выражение2 of
  when значение2_1 then
    ветвь;
  when значение2_2 then
    ветвь;
else
  ветвь;
end;
Блок elsif может повторяться много раз. Аналогичная запись без elsif потребует явного вложения со своим завершающим end:
case выражение1
  when значение1_1 then
    ветвь;
  when значение1_2 then
    ветвь;
  when значение1_3 then
    ветвь;
else
  case выражение2
    when значение2_1 then
      ветвь;
    when значение2_2 then
      ветвь;
  end;
else
  ветвь;
end;
В байт-коде обе формы неразличимы. Использование или неиспользование elsif в case может выступать как параметр отекстовки.

Форма записи оператора if не претерпела изменений, старое описание верно.

Решение проблемы unless

Найдено также элегантное решение для аналога оператора unless — достаточно лишь разрешить писать else сразу за условием if, тем самым сделав ветвь then необязательной:
if условие else
  ветвь;
end;
Поскольку ветвь else всегда стоит последней, эта форма if безальтернативна.

Пролог и эпилог

Предыдущая форма оператора case имела пролог — блок операторов, выполнявшийся перед операторами ветви, если хоть одна ветвь выбрана. Необходимость в прологе, а также возможное добавление эпилога по ключевому слову default рассматривалось, но в данный момент выглядит спорным. Вместо пролога и эпилога всегда можно использовать замыкания, задаваемые префиксом with, придется лишь дать им имена. Стоит ли экономия на именах введения новых сущностей, пока решается. В данной статье оператор case записан без of, подразумевая отсутствие пролога.

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

24 мая 2014 г.

Ссылочные выражения требуют обсуждения

Реализация концепции ссылочных выражений в Канторе требует обсуждения с независимым оппонентом, понимающим, что такое указатели. Лимит обсуждений с самим собой временно исчерпан, увы. Лучше, если обсуждение будет голосовым.

Желающих оппонировать прошу писать на почту или в Jabber. В Skype выхожу по договоренности.

15 мая 2014 г.

Будет ли unless?

Разработка байт-кода несколько продвинулась, и теперь можно дать ответ на вопрос, будет ли в Канторе оператор unless — обратный if, в котором ветка then исполняется при ложности условия. Похожий оператор есть в языке Руби.

Для оператора unless не хватает битов

Необходимость в операторе unless с самого начала была под сомнением, и окончательное решение о его добавлении в язык решено было оставить на стадию разработки байт-кода: мол, логика команд всё решит. Так пока и получается.

Байт-код Кантора представляет собой вариант синтаксического дерева (AST), узлы которого кодируются не командами виртуальной машины, а набором состояний. В байте 8 бит, тем самым можно закодировать восемь одновременных состояний, среди которых есть Conditional, Ordered и Reversed, а также Parameters, совмещенный с Reversed. Смысл состояний следующий:
Parameters = Reversed

{Conditional}             // if
{Conditional, Parameters} // case, пересекается с {Conditional, Reversed}

{Conditional, Ordered}           // while
{Conditional, Ordered, Reversed} // repeat
Из примера видно, что набор битов для кодирования оператора case совпадает с набором битов для unless. Коллизия вполне ожидаемо разрешается исключением unless из возможного набора операторов.

Поскольку разработка байт-кода еще продолжается, данное описание является предварительным.