Logo

29 декабря 2013 г.

Отказ от встроенных типов и булевый тип

Объявленный ранее отказ от встроенных типов оказывает большое влияние на булевый тип —единственный конечный тип, значения которого не имеют естественного представления. В других языках программирования значения «ложь» и «истина» обозначаются либо константами, либо ключевыми словами. Как поступить в Канторе?

Ранее, вместе со встроенными типами в Канторе были зарезервированы ключевые слова false и true, упрощавшие, как казалось, вывод типов при объявлении функций. Отказ от встроенных типов заставляет принять решение — либо признать булевский тип исключительным и оставить ключевые слова, либо изъять ключевые слова в пользу констант. Не усложнит ли это объявление булевых функций?

Константы и пространства имен

Основная сложность перехода на булевы константы — размещение класса Boolean в пространстве :Core. Это логично, поскольку булевый тип — один из базовых. Но из других пространств ссылки на константы будут иметь комплексный вид — :Core:Boolean.False и :Core:Boolean.True. На первый взгляд смотрится неудобно, но даже самый простой пример вносит ясность:
:Core:Boolean a = False;
Локальная функция a имеет тип :Core:Boolean, что заставляет компилятор искать объявление False в пространстве класса :Core:Boolean, объявление которого выглядит так:
public final class :Core:Boolean = [False, True];
Данное объявление подразумевает, что свойство False объявлено как статическое (концепция перечислений пока в разработке), поэтому объявление функции a валидно, и она успешно скомпилируется.

Поиск имени в пространстве класса — базовое соглашение видимости имен в Канторе, сильно упрощающее использование перечислимых типов, в том числе Boolean:

Альтернативные способы

Если базовый способ объявления булевых значений по каким-то причинам не подходит, есть как минимум два альтернативных способа — при помощи выражения или же обращением к свойству AsBoolean:
a = 0 == 1;      // выражением, как в SQL
b = 0.AsBoolean; // через свойство
Сравнение констант, дающее предопределенный результат, долгое время было распространено в СУБД, поскольку не влияло на выдачу, но задействовало оптимизатор.

Свойство AsBoolean должно быть реализовано классом, как уже сделано в классе :Core:Word — тип выведен из значения 0.

Практика использования булевых констант

Так ли часто придется явно использовать булевы константы на практике? Видятся следующие случаи:
  • Если при объявлении параметра или свойства задать тип, константа становится видна по короткому имени.
  • Параметры и свойства, имеющие значения по умолчанию, чаще всего опускаются при обращении, если значение по умолчанию подобрано верно.
  • Если булевы свойства и параметры не имеют значений по умолчанию, либо умолчания нужно перекрыть, в точке обращения тип свойства или параметра уже известен компилятору, и к соответствующей константе можно обращаться по короткому имени. Тут действует то же соглашение, что и в первом рассмотренном примере.
Единственная сложность, которая может возникнуть — обращение к перегруженному свойству с булевыми параметрами в перегруженной части. Это относится не только к булевому, но и к любому другому перечислимому типу. При пересечении имен свойств в классах перегруженных параметров возникнет коллизия, и при обращении к свойству придется указывать полное имя константы. Поскольку коллизии возникают при неправильной декомпозиции, это может стать поводом для рефакторинга и нормализации классов.

Текущее состояние

Исходя из перечисленных соглашений и случаев использования, ключевые слова false и true планируется исключить из синтаксиса, приравняв булевый тип к другим перечислимым типам. Для принятия окончательного решения требуется также проработать представление перечислимых типов в ядре (в данной статье оно дано схематично).

28 декабря 2013 г.

Цикл с параметром как итератор

Представленные ранее циклы «пока» и «до» — чисто алгоритмические конструкции, их описание не вызвало затруднений. В отношении цикла с параметром, напротив, высказана гипотеза, что данный цикл является не просто итератором, но алгоритмической формой запроса, по смыслу аналогичного оператору SQL select. В настоящий момент концепция прорабатывается, поэтому статья носит предварительный характер.

На момент написания статьи принято, что оператор for в декларативном синтаксисе может являться аналогом оператора select в командном синтаксисе, при условии, что оператор select также имеется в языке Кантор.

Итератор вместо счетчика

В практическом программировании цикл с параметром чаще всего используется для обхода контейнеров — в качестве итератора. Язык Кантор ориентирован на контейнеры, поэтому для цикла с параметром форма итератора принята в качестве основной. Синтаксис for в Канторе близок к for из PL/SQL и больше похож на foreach в некоторых языках программирования, нежели на традиционный for со счетчиком. Простой счетчик, если потребуется, будет представлен как виртуальный (пустой) контейнер, не содержащий ничего, кроме индексов.

Опора на итератор однозначно решает вопрос видимости внутренней переменной цикла, соответствующей текущему элементу: объявление переменной в Канторе является частью оператора цикла, а сама переменная — локальным именем внутри него, снаружи она не видна.

Простой перебор

Как уже говорилось, цикл for в Канторе восходит к PL/SQL:
for элемент in контейнер do
  ветвь;
end;
Внутренняя переменная элемент имеет тип элемента контейнера — выводится компилятором. Поддержка итераций реализуется контейнером через множественное наследование, как конкретно — будет проработано в дальнейшем.

Перебор нескольких контейнеров

Один из архитектурных принципов языка Кантор — множественность шагов цикла, что уже позволило реализовать цикл Дейкстры и цикл «паук». Применение принципа к циклу с параметром вполне логично ведет к возможности перебора нескольких контейнеров одним оператором, примерно так:
for элемент1 in контейнер1, элемент2 in контейнер2, элемент3 in контейнер3 do
  ветвь;
end;
Сложность в том, что в арифметике контейнеров такая запись соответствует декартову произведению и на практике требуется довольно редко. Кроме того, контейнеры в Канторе имеют собственные итераторы — inner и outer — для обхода деревьев и графов соответственно. Выражения контейнерной арифметики могут также содержать блоки условий where и агрегатных функций. С этой точки зрения декартово произведение в for выглядит весьма спорно, поэтому данная конструкция пока не утверждена.

Стои́т также вопрос, останется ли вообще оператор select, принятый в Канторе на ранних этапах, в виде самостоятельного оператора, или же ключевое слово select уйдет из языка, уступив место обычным выражениям в простых случаях и блокам with, в том числе with с for — в сложных? На этот вопрос пока нет однозначного ответа, понятно лишь, что имеется тесная взаимосвязь select, for и контейнерной арифметики. Решение по ним будет приниматься комплексно.

27 декабря 2013 г.

Отказ от встроенных типов

Ранее считалось, что в языке Кантор есть встроенные типы, обозначаемые ключевыми словами int, word, float, bool, byte, char, string, text и wiki. Компромисс основывался на допущении, что встроенных типов будет достаточно для общих случаев, особенно при наличии развитого вывода типов.

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

Можно ли обойтись встроенными типами?

Как говорилось, встроенные типы предназначались для неких общих и простых случаев. Высказывалось предположение, что такие случаи бывают, и возможностей встроенных типов окажется достаточно для их покрытия. Допущение было чисто умозрительным, поскольку на ранних этапах еще не было примеров кода на Канторе, а у его автора не было опыта программирования на нем. Позже, когда примеры появились, вместе с опытом пришло понимание, что «общие» и «простые» случаи весьма затруднительно отделить от всех остальных, что делает аргумент о надобности встроенных типов негодным, а выбор конкретных типов из множества — волюнтаристским. В самом деле, почему именно эти типы делать встроенными?

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

Неоднородность синтаксиса

Мысль об отказе от встроенных типов возникла из-за видимой глазом неоднородности синтаксиса в примерах: встроенные типы обозначаются ключевым словом, а остальные — обычным идентификатором. Сравните два описания:
var of
  int IntValue1 = 0;
  Integer IntValue2 = 0;
end;
IntValue1 и IntValue2 — целые переменные, а объявлены по-разному. Никакого скрытого смысла здесь нет, просто так захотелось программисту. Если язык допускает подобную самодеятельность, — это плохой язык.

Как обойтись без встроенных типов?

Язык Кантор — чисто объектно-ориентированный, то есть все типы в нем являются классами. Это означает, что имеется только один встроенный тип — class, функциональность которого обеспечивается компилятором. Данная концепция — прямая и однозначная реализация как понятия чистого ОО-языка, так и принятого в Канторе архитектурного подхода, что ключевыми словами обозначаются только те понятия, которые могут быть реализованы только магией компилятора и никак иначе.

Поскольку Кантор изначально проектировался как язык, синтаксис которого реализуется RTL, вывод типов в нем привязывался к предопределенным классам RTL. Затем часть предопределенных классов обозначалась ключевыми словами, что и давало встроенные типы. Предполагалось, что при компиляции ключевые слова будут развертываться в классы, поскольку в байт-коде могут храниться только ссылки на классы, а ключевые слова — синтаксическое понятие. Отказ от встроенных типов лишь избавит компилятор и отекстовщик от работы по развертыванию ключевых слов в классы RTL и наоборот, не затрагивая остальных концепций.

Межпространственная адресация

Очень редко отказ от чего-либо проходит полностью безболезненно. Отказ от встроенных типов в Канторе требует решения другой проблемы — упрощения ссылок на классы из другого пространства. Проще пояснить это на примере:
public class Model of
  public class CantorSys of
    public class HelloWorld of
      public out :Core:String Hello;
    end;
  end;
end;
Фрактал, в котором работает Кантор, делится на пространства — корневые контейнеры, инкапсулирующие среды с разным временем жизни объектов и способом доступа к ним, играющие также роль пространства имен. Корень всех пространств обозначается двоеточием, вложенные объекты в пространствах также разделяются двоеточием.

В примере выше корневые пространства :Model и :Core являются классами, а описание свойства :Model:CantorSys:HelloWorld.Hello ссылается на класс :Core:String. Во фрактальной среде вложенных описаний не избежать никак, и даже в простом примере они уже не такие короткие. На будущее нужно продумать механизм, упрощающий межпространственную адресацию, делающий это очевидным способом и однозначно ложащийся в байт-код.

5 декабря 2013 г.

Цикл «до» и цикл «паук»

Раз цикл Дейкстры — многоуровневая форма цикла «пока», должен существовать его зеркальный аналог в форме цикла «до» с несколькими ветвями. В литературе он известен как цикл «паук».

При разработке языка Кантор оператор многоуровневого «до» был выведен эмпирически, а его описание и сам термин «паук» найдены позже. На момент написания статьи Википедия утверждает, что цикл «паук» ни в одном языке программирования пока не реализован, поэтому Кантор вполне может стать первым языком с циклом «паук».

В языке Кантор пока принято, что циклы имеются только в декларативном синтаксисе.

Цикл «до»

В форме с одной ветвью оператор repeat имеет вид обычного цикла «до»:
repeat
  ветвь;
until условие;
Цикл repeat — блочный оператор, поэтому операторы его ветвей разделяются точкой с запятой. Краткая форма repeat в Канторе — единственный блочный оператор, не оканчивающийся обязательным end.

Вырождение цикла «до» в бесконечный цикл

Использование цикла «до» в реальном программировании требует выполнения завершающих действий перед выходом — эпилога. Цикл «до» при этом вырождается в бесконечный, требуя дополнительных механизмов выхода из него. Традиционное решение — оператор досрочного выхода.

О вырождении цикла «до» в бесконечный наверняка знали разработчики языка PL/SQL. В PL/SQL цикл «до» отсутствует вовсе, зато имеется бесконечный do loop и оператор досрочного выхода exit when, который пишется столько раз, сколько разных условий (ветвей) выхода имеется.

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

Цикл «до» с эпилогом

Введение эпилога — первый шаг по адаптации цикла «до» к форме, не требующей оператора досрочного выхода. Цикл «до» с эпилогом имеет в Канторе следующий вид:
repeat
  ветвь;
until условие then
  эпилог;
end;
В этой форме после условия выхода until пишется ключевое слово then, за которым идут операторы эпилога, разделенные точкой с запятой. В конце эпилога ставится обязательный end.

Цикл «паук»

Оператор repeat с эпилогом легко расширяем до формы с несколькими ветвями. Дополнительные ветви начинаются ключевым словом else и оканчиваются собственным until и условием выхода, после которого также идет эпилог:
repeat
  ветвь;
until условие then
  эпилог;
else
  ветвь;
until условие then
  эпилог;
else
  ветвь;
until условие then
  эпилог;
end;
Полная форма оператора repeat не позволяет пропустить эпилог какой-либо ветви, кроме последней. В рамках проекта высказана гипотеза, что принятая в Канторе запись является естественной для цикла «паук», и надобности в пропуске эпилогов промежуточных ветвей не возникнет. В будущем гипотеза будет проверена на практике.

После исключения циклов из синтаксиса ключевые слова repeat и until больше не подсвечиваются.