Ошибки и их исправление в эргономике API

Необходимы перепосты и переводы на другие языки. Газета "Компьютерные вести" kv.by/index2010253801.htm, kv.by/index2009071109.htm Издание 4-е, исправленное и дополненное. Другие статьи (оглавление)

Состояние проблемы

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

Системно решив проблему, мы не только поможем энтузиастам, но и сэкономим деньги на казенных проектах. Речь идет о том, что сила и устойчивость политических режимов определяется сравнительной производительностью труда, и нужно дать простому смертному программировать так же легко, как писать шариковой ручкой. В первом мире вся катастрофа в совокупности уже обозначена как необходимость создания нового поколения масштабируемого и простого в написании ПО (см. например "Report to the president. Computational Science: Ensuring America's Competitiveness. President's Information Technology Advisory Committee. June 2005"). В этой связи Китай поступил крайне недальновидно, клонировав вчерашний день западной индустрии в национальной ОС Kylin. Чтобы это не повторилось, автор хотел бы указать на стратегические ошибки и предложить пути их преодоления.

Тайна, скрытая от общественного сознания, состоит в том, что Запад спроектировал интерфейс с расчетом на когнитивные способности, которыми средний человек не обладает. Чтобы доказать это, разумеется, нужно предъявить другие средства, несовпадающие с уже известными индустрии. И проследить чьи интересы это затрагивает. Автор глубоко изучил вопрос в рамках обсуждения в международных организациях по стандартизации HTML (см. w3.org/Search/Mail/Public/search?keywords=Dmitry+Turin) и по стандартизации SQL - в ISO JTC1 SC32 WG3.

Разумеется, нет никакой государственной программы США, которая запрещала бы усовершенствования, но каждая фирма стремится создать препоны, чтобы пользователь не вздумал скопировать ПО и уйти, не заплатив. Основные деньги корпорации получают не продавая программы, а обучая пользоваться ими (Microsoft с его Office и Windows - в том числе). Правда, что касается W3-консорциума, есть у него в хартии пункт, запрещающий улучшения в явном виде, а именно применение дублирующих технологий (именно таким образом появление языка JavaScript надолго остановило развитие языка HTML). Периодически вспыхивает агитация за отмену этого пункта, но протесты, перегорев, плавно сходят на нет. Все мы бессознательно воспринимаем международные организации как созданные пользователями и для пользователей, на самом деле скинулись мужчины, у которых деньги есть. Да и скинулись в основном, чтобы перекрыть ветер в паруса конкурентов. Кроме W3C автор имел возможность убедиться в этом и в ISO JTC1 SC32 WG3, где ключевые посты заняты представителями Oracle, Microsoft и IBM. Отсюда и расходы на программиста, например, под Oracle превышают стоимость дистрибутива Oracle.

Отсюда и бóльшая цена программ по сравнению со стоимостью схемотехники (стоимостью материального производства!). Но мало кто задумывался, что это создано искусственно и сакрализировано (автор устал читать о сакральном в книгах серии "PHILOSOPHY").

Чем вообще отличается нормальная работа пользователя от ненормальной, из каких частей она состоит? Берусь утверждать, что это - доступ к данным и отображение данных. Рассмотрим эти две составляющие подробно.

Доступ по третьей парадигме

За всю историю существования компьютера было три парадигмы хранения данных. Их отличительными особенностями являются:

В последнем случае структуры ссылаются друг на друга по физическому адресу, доступному только для сравнения и для перехода к структуре, связанной с данной. К несчастью, изобретатель третьей парадигмы (Кодд) не имел доступа к ядру какой-либо ОС, и не мог ее туда вмонтировать. И что еще более печально, он слил систему хранения с одной из многих возможных машин вывода и сделал физический адрес недоступным из-за ненадобности для этой машины - и этими двумя поступками смутил все последущие поколения разработчиков. Результат называется СУБД и состоит из части ОС и из машины вывода для оператора 'join' - и ни разработчики ОС, ни разработчики машин вывода не могут разделить эти два факта в своей уме.

Автор настоящей статьи столкнулся с этим, когда задумал машину вывода, делающую перспективную проекцию 3D-фигур в окно программы с помощью единственной функции printg("SELECT * FROM GroupsTable.FiguresTable.TrianglesTable.PointsTable WHERE ..."), аналогичной функции print для вывода текста (подробности ниже). Безотносительно к тому, правильное решение принял автор для 3D-фигур, неправильное - он вынужден прикрепить свою машину к СУБД, а не к ОС, потому что в ОС третья парадигма не реализована. Сбросьте пелену с глаз, повседневная реальность вообще комична - бизнес-приложения используют машину вывода 'join' для извлечения из хранилища по одной структуре без всякого вывода.

Сегодня в нашей индустрии наблюдается помутнение умов - что-то похожее на танцевальную лихорадку или тюльпанную лихорадку в средневековой Европе. Кто-то на этом делает деньги. Произошел откат от структурного мировозрения к файловому: концепция "всё есть файл" снова стала повсеместной, скатились даже до хранение XML (разоблачение XML-СУБД см. на debank.com).

При уходе со сцены файлового мировозрения IP-соединение не должно помнить свое состояние, а порядковый номер пакета заменяется на идентификатор транзакции. Номер не нужен, потому что часть BLOB-поля помещается в пакет с указанием начала и конца участка (см. sql5.19.3.pdf с.166-167, 169), часть поля-массива - с указанием диапазона индексов элементов (с.152, а также далее), а в пакете всегда можно разместить целое количество записей, выслав их BLOBы и массивы отдельно. К идентификатору же транзакции привязаны приватный ключ шифрования данного пользователя, выполняющий роль логина; ник хранилища, с.152; а вместе с последним - и приоритет пакета в рамках ОС-получателя (назовем его важностью, чтобы отличить от приоритета для прохождения интернета, указываемого в IP-пакете). TCP вообще не нужен - номер порта (равно как и PID) технологически устарел, а занять эти байты нечем: идентификаторы таблиц и физические адреса записей в них не имеет смысла выносить из байт-кода языка запросов в заголовок пакета.

Буфера ОС заменяются на транзакции. Стандартный поток вывода stdout - на небуферизированную доставку из одного хранилища в другое, запуск которой сам по себе начинает новую транзакцию (сегмент отката - в терминах Oracle - все-таки буфером не является). Стандартный поток ввода stdin заменяется на условие для триггеров AFTER OUTSIDE, действительное только в случае, если структура была добавлена, изменена, удалена или прочитана другим триггером, не состоящим с данным триггером в одной программе. Вложенные директории заменяются на вложенные схемы.

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

Необходимость этого уже вылезла в аппаратуре под названием стена памяти. В то время, как за один такт процессор в состоянии выполнить несколько команд, на единичный доступ к межпроцессорной памяти уходит около сотни тактов. И все из-за того, что ядера и АЛУ ждут окончания выполнения команды, и изменения порядка выполнения команд оказывается недостаточно. Соответственно все задачи делятся на CF (cash friendly) с частыми обращениями по последовательным адресам или непоследовательно, но к одним и тем же ячейкам - и на DIS/DIC (data intensive system/computing) с обращениями по слабо предсказуемым, практически случайным адресам. Но почти все современные задачи - управление социально-экономическими системами и войсками, криптоанализ, создание генного оружия и лекарств, предсказание погоды, климата, распространения загрязнений, проектирование крупногабаритных самолетов и кораблей - относятся ко второму классу. На них техника показывает производительность на три порядка ниже (список тестов см. в конце статьи osp.ru/os/2008/01/4836914 после реквизитов авторов), и повышают её, соединяя пакетный режим памяти и треды ядра. Аналогичную асинхронность вводят в общении между ядрами и между процессорами. Естественным отображением на языки высокого уровня является асинхронное программирование и третья парадигма, которые уперлись в намеренно созданный файловый контроль доступа.

Доступ к BLOB-полю

Две наиболее типичные операции с BLOB - это скачивание с сервера и получение от клиента, который отправляет его по собственной инициативе. В первом случае соединение иногда разрывается, может понадобится выключить-включить ноутбук-получатель и продолжить с прерванного места. Значит в поле нужно хратить адрес BLOB, его уже скачанную часть (или части, если происходит скачивание сразу нескольких частей как в программе ReGet через отдельные TCP-соединения) и смещения начала и конца каждой части. А для обращения с хранилищем нужны операторы, возвращающие сам BLOB, процент завершённости скачивания, адрес BLOB-а на удалённом сервере (значение, отличное от NULL, означает, процесс еще не закончен и сообщает где можно взять BLOB, не дожидаясь завершения - или в случае, если хранилище будет разрушено) и оператор, запускающий скачивание в фоновом режиме и возвращающий, запущено скачивание или нет.

Во втором случае мы не можем продолжить получение после разрыва TCP-соединения, после перезагрузки или выключения питания - мы можем только надеяться, что соединение не будет разорвано, компьютер не повиснет и что источник бесперебойного питания дотянет до конца передачи. Но если второе хранилище попросит в получающем это поле до завершения процесса, что получающее должно ответить? Отвечая NULL, оно сообщит, что ничего не знает о BLOB, что является ложью, т.к. его получение - это только вопрос времени. Отсюда поле должно хранить уже полученную часть BLOB и специальное значение ACCEPTING (во всех операциях, кроме сравнения, выступающее как NULL, но в сравнении не равное ему). Пусть третье хранилище копирует в себя поле из второго. Может ли третье содержать значение ACCEPTING? Очевидно нет, т.к. это выглядело бы, будто оно само получает BLOB. Таким образом оно дезинформировало бы своих клиентов и внушило бы им ложные надежды. Итак, нам нужно ещё одно специальное значение, значение NOTGOT, в которое превращается ACCEPTING. При копировании NOTGOT превращается в NOTGOT.

Вообще же BLOB - это файл, и как всякий файл, он имеет тип - jpg, mpg, и т.д. Файлы одних форматов содержат указание типа в самом начале, файлы других - не содержат, и в этом случае тип должен храниться в BLOB-поле и копироваться из поля в поле при копировании BLOB (хорошо, если бы хранилище понимало большое количество форматов и исправляло бы тип файла в BLOB-поле на значение из содержимого файла). Подытожим, BLOB-поле должно быть способно содержать любой из следующих наборов значений:

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

Масштабируемый доступ

Язык запросов более гибок и удобен для сбора данных из нескольких хранилищ и рассовывание их в несколько хранилищ, чем частные программы. Но нет необходимого синтаксиса - попробуем его создать. Рассмотрим на примере SQL. Первое, пусть каждое хранилище получит прозвище. Прозвище будем ставить в запросе перед именем таблицы через двоеточие - как в sql5.19.3.pdf на с.121. Второе, группу хранилищ назовем обществом. Общество также указывается перед именем таблицы через двоеточие, и означает прозвища всех хранилищ, входящих в группу (с.123). Таким образом одно sql-выражение с обществом означает множество sql-выражений с прозвищами. Кроме того, чтобы несколько обществ или несколько упоминаний одного общества никогда не указали на одно и то же хранилище одновременно, поместим перед ними символ %; а чтобы несколько упоминаний одного общества наоборот всегда синхронно указыли на одно и то же хранилище, поместим любое слово (одинаковое) и символ % перед этими упоминаниями (с.122-123, 126-128). И третье, введем понятие хранилище по умолчанию - в нем хранятся все прозвища и все общества. Без специальных указаний это - первое хранилище, к которому клиент приконнектился; потом его можно поменять. В целях безопасности распределенные запросы должны удовлетворять некоторым требованиям. Предлагаю концепцию полного недоверия одного хранилища другому (первые три пункта) и концепцию крайней простоты клиента (последний пункт):

Таким образом одно хранилище не может ввести sql-команду в другое хранилище ни прямо, ни косвенно (прося клиента перенаправить команду). От клиента не требуется создавать новую sql-команду путем парсинга ситуации, но предполается умение запомить отправленные команды в стеке и при необходимости совершить элементарные текстовые замены в них и повторные отправки. Т.е. при взаимодействии клиента сразу с двумя хранилищами команда отправляется сначала в одно, затем то посылает клиенту специальное уведомление, прося сделать подстановку в только что отправленной команде и выслать результат преобразования второму хранилищу. Специальные уведомления должны быть настолько ограниченными, чтобы не допускать порождения sql-команды, вредной для второго хранилища. Теперь нам удобно делать репликацию, аггрегировать данные со множества серверов. Содержимое бинарных протоколов нам нужно как-то зарисовывать в чертежах и документах, предлагаем sql-команды записывать просто текстом, данные - в xml-виде, а уведомления как <?notification/?> (с.133, 149, 171).

Чтобы несколько раз включить и выключить ноутбук в течение одной длинной транзакции, длящейся дни или даже недели, достаточно ввести оператор FREEZE, подобный DISCONNECT, который сохранит транзакцию в текущем состоянии; и оператор UNFREEZE, подобный CONNECT, который продолжит транзакцию с замороженного состояния, т.е. не начнет новую, как CONNECT. FREEZE возвращает идентификатор замороженной транзакции, который должен быть указан в UNFREEZE. На случай падения одного хранилища во время распределенной транзакции предлагаем оператор POSTPONE для заморозки на второй стадии двухстадийных и трехстадийных COMMIT, называемых на жаргоне 2PC и 3PC, и оператор ADJOURN для заморозки на третей стадии трехстадийных COMMIT.

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

Сначала где хранить. В 3DMLW, 3DXML, COLLADA? Заведомо известно, что наши биолог, физик, библиотекарь не разберутся в этих форматах, не напишут конвертеры своих данных в них, не инициируют событие в программах-визуализаторах для обновления экрана. Может быть в современных СУБД? Практика показывает, не подключат они драйвера. Кроме того, сложно задействовать данные, поставляемые корпорациями в вычурных типах от ISO JTC1 SC32 WG3 - сводку 2D-типов вы можете видеть в P.Scarponcini "SQL Multimedia and Application Packages - Part 3: Spatial. Bentley Transportation" по адресу wiscorp.com/sqlspat.zip, скоро подоспеют 3D-типы. Всё это настолько неподъемно, что нужно отдельное высшее образование только для staging-а, т.е. для ETL. Кто-то на этом искусственном барьере вытаскивает из нас деньги.

Затем нужно визуализировать данные. Не запрограммируют прикладные специалисты OpenGL. Это уже результат сговора других ТНК. И если кто-то соберётся отъедать у них часть рынка, то его DirectX не будет проще. Ибо если где-то образуется простота, через нее утекут все деньги.

Итого два барьера, на которых долго, дорого плюс дублирование информации на компьютере (OpenGL и DirectX - это тоже хранилища).

Не сомневаемся, что вычурные типы даже не пришли вам в голову, и вы поступили самым естественным образом: представили 3D-конструкцию как иерархию точек, треугольников, фигур (групп треугольников), групп фигур, групп групп, и т.д. И записи, содержащие эти объекты, связали внешними ключами. Что теперь нужно? Во-первых, не копировать поштучно точки и треугольники в OpenGL, вызывая её процедуры, а указать предикат, которому записи подчиняются - например узел дерева, дистальнее которого концы ветвей содержат точки, и маршрут к ним с помощью "SELECT * FROM GroupsTable.FiguresTable.TrianglesTable.PointsTable" Во-вторых, не обновлять собственноручно координаты в реляционном хранилище, подслушивая мышиные drag-and-drop, и уж тем более не редактировать самому координаты в OpenGL после каждого изменения в хранилище. Всё это гуманная библиотека сделает самостоятельно. Назовем ее машиной проекций. А участок экрана, порожденный её функцией print, - пространственным полем. Итого любая программа отображает содержимое хранилища на экране функцией print("SELECT * FROM GroupsTable.* ").

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

Чтобы названия таблиц не имели значения, и чтобы допустить ветви дерева ниже 3D-точек (возможно нам понадобится прикрепить к последним что-нибудь глубокомысленное), пометим колонки, содержащие координаты, служебным словом "DOT", которое по аналогии с типом данным назовем визуальным типом. Треугольники (или четырехугольники) предполагаются расположенными на один уровень выше в каждой ветви дерева, а записи остальных уровней не визуализируются. Как отсекать ненужные ветви дерева см. sql5.19.3.pdf с.12-16, 21-22, 34-46, 53-55, 58-68. В связи с использованием точки для указания маршрута по дереву, схема должна быть отделена от названий таблиц не точкой, а каким-то другим знаком, например "§": SELECT * FROM user§GroupsTable.*

Аналогично в операторе UPDATE также можно указать всю иерархию записей от фигуры или группы до 3D-точки с помощью "UPDATE GroupsTable.FiguresTable.TrianglesTable.PointsTable SET ...", чтобы обновить не все записи в хранилище, а только входящие именно в такие деревья. Т.е. любая программа изменяет положение объекта в хранилище и на экране функцией print("UPDATE GroupsTable.* SET ...") Сдвиг, растяжение и вращение органичнее поставлять не как хранимые процедуры, а как встроенные в SQL, например сдвиг на 5 единиц по каждой координате выглядел бы как "UPDATE GroupsTable.* SHIFT (5,5,5)". SHIFT, SPRAIN, ROTATION действуют только на колонки, отмеченные визуальным типом DOT. На них может быть установлен триггер типа "CREATE TRIGGER ... AFTER SHIFT".

Но заранее спланированное движение должно задаваться не обновлением записей по таймеру в процедуре пользователя, писать и поддерживать которую крайне тяжело для него, а путем указания времени актуальности записей. Сегодняшний синтаксис такой актуальности переусложнен. Избавимся от лишнего слова VALID и будем вместо VALID PERIOD писать просто PERIOD. Сейчас с периодом актуальности оперируют не как с полем структуры, что неудобно для извлечения записей за произвольный отрезок времени - особенно если этот отрезок задан не константами, а под-запросом. Кроме того, использование для сравнения таких периодов не операций над массивами, а служебных слов UNION, INTERSECT, EXCEPT, уже задействованных для обработки целых записей, вызывает когнитивный диссонанс. Поэтому будем указывать время актуальности в самом настоящем поле, для чего пометим в структуре один из двухэлементных массивов типа DATATIME визуальным типом PERIOD как на с.220-221 упомянутого pdf-документа.

Интересно, сможет ли хоть DARPA с её "mission to prevent technological surprise for the United States and to create technological surprise for its adversaries" побороть американские концерны. Помочь американским ученым, самостоятельно разрабатывающим некачественное ПО, мучающимся от низкой реальной продуктивности программирования. Или даже на супер-компьютерах стратегического назначения HPCS они будут визуализировать постаринке!?

Архитектура ОС

Подход "print-SELECT командует не только хранилищем, но и оконным сервером, плюс работа пользователя клавиатурой и мышью вместо последующих запросов" должен быть применен и к визуализации строковых и числовых данных. Пока что додумались только до того, что после редактирования записей в плоской таблице изменения автоматически фиксируются в базе данных, а при отображении двух таблиц в подчиненной демонстрируются только записи, ссылающиеся на текущую запись главной. Это реализовано во всевозможных RAD-системах (вместо печатания print-SELECT применяется drag-and-drop иконок). Но не реализованы остальные динамические связки оконного сервера и хранилища, ручное написание которых и есть суть создания интерфейса над новыми структурами данных и алгоритмами обработки. Эти связки перечислим в следующем разделе, а машину, их выполняющую, назовем машиной демонстраций (будем использовать одну и ту же функцию 'print' для всех машин, а предназначение машине проекций выделять служебным словом PROJECTING в конце запроса "SELECT ... FROM ... PROJECTING"). Машина демонстраций запрашивает данные не только у винчестера, но и у машины 'join', когда ее парсер обнаруживает join-овский SQL. Таким образом все машины должны использовать общий механизм контроля доступа/транзакций (в т.ч. savepoint). А программа при последовательном проектировании ОС должна взаимодействовать не с оконным сервером, а непосредственно с нужными ей машинами.

И уж хотя бы это должно дать понять, что права доступа к записям должны не реализовываться пользователем каждый раз заново вручную, а поставляться самим хранилищем, например так: создавать-удалять некие департаменты командами "CREATE DEPARTMENT dp" и "DROP DEPARTMENT dp", делегировать-удалять полномочия на них командами "BESTOW select ON dp FOR username" и "VANISH update ON dp FOR rolename", а имя департамента указывать в самих записях в поле специального типа "CREATE TABLE a (... , a5 DEPARTMENT, ...)", "INSERT INTO a VALUES (... , dp, ...)". Отсутствие полномочия SELECT означает невидимость записи, полномочия UPDATE - невозможность ее редактировать, полномочия DELETE - невозможность удалить, полномочия INSERT - невозможность вставить запись, маркированную данным департаментом. Само специальное поле доступно для изменения только тем, у кого есть полномочие DIRECTOR на значение, содержащееся в поле, "BESTOW director ON dp FOR rolename". Мы преднамеренно выбрали существительное (director) для обозначения этого полномочия, а не глагол подобный 'select, update, delete, insert', чтобы выделить его особую роль в голове пользователя.

Если механизм контроля доступа/транзакций реализован в ОС, однотипные машины разных производителей взаимозаменяемы, конкуренция между ними велика. Если он находится в линейке машин 'join', то их производители организуют второй (после операционных систем) рубеж Vendor lock-in. Могут отказывать производителям других машин, чтобы реализовывать машины самим. И торговать ими в нагрузку (распухание СУБД). Или запретить работу лицензированного под них продукта с 'join' конкурента. А если и будет работать продукт с несколькими машинами 'join', для общения с каждой нужен перекодировщик, падает производительность из-за драйверов. Помимо контроля доступа/транзакций, в общем механизме ОС должен быть реализован вызов триггеров.

Строковые и числовые данные - также как и пространственные - должны храниться не в СУБД, а на жестком диске, расформатированным под третью парадигму; ведь машина 'join' - очень частный случай, предназначенный для изучения статистики, не более чем один из сервисов ОС, такой же как и машины проекций и демонстраций.

На месте удаленной структуры может быть создана новая, но ссылка на удалённую не должна указывать на новую. Для этого в таблицах необходима системная колонка, в которую записывается следующее значение для каждой новой записи. Такое значение может браться из счетчика для всей таблицы, что экономит разрядность этой колонки, но требует для таблицы хэш-индекса; либо просто инкрементироваться при удалении записи, тогда внешние ключи должны содержать два числа - физический адрес записи и значение в колонке. Значение внешнего ключа в первом случае называется первичным ключем, значение во втором случае по аналогии будем называть физическим ключом. Почти все приложения не изучают статистику, поэтому хранилище резонно построить на основе физического ключа, а не первичного. Это даст выигрыш в скорости. Аналогично данные должны храниться не по колонкам, что повышает производительность аггрегатов avg, sum и прочих (OLAP), а построчно, что нужно в большинстве операций (OLTP).

Для оконного сервера и для машин нужен единый формат данных, разумеется бинарный. Подобно тому, как реализация третьей парадигмы с физическими ключами поверх FAT и СУБД с её первичными ключами и хэшами превращает хранилище в монстра, жрущего ресурсы; так и проектирование оконного сервера без учета того, в каком формате машины передают друг другу, нагружает оконный сервер еще одним перекодировщиком и превращает в медленно едущую телегу. Среди прочего формат должен кодировать специальные значения ACCEPTING и NOTGOT, необходимые для информирования о том, что особо крупный объект все еще находится в процессе получения.

Кроме того, ОС запоминает за клиента отправленные им команды в своем стеке; принимая для него уведомления, делает подстановки в запомненном и спрашивает у пользователя пароли к другим хранилищам, чтобы отправить измененные команды (таким образом большую часть масштабируемого доступа осуществляет совсем не клиент). Операционные системы хранилищ откатывают и накатывают транзакции по методу временных меток, чтобы разрешить deadlock-и; и обмениваются для этого специальными декларациями - мы не изображали их на слайдах, но зарезервируем для них обозначение <<declaration/>>. И формат должен также их кодировать. Обмен происходит через ОС клиента, чтобы та могла информировать пользователя как происходит этот процесс, в т.ч. об истечении собственного таймаута и о стороннем запросе его продлить.

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

Отображение полей хранилища как есть

Нужно расширить определения "UNIQUE" и "PRIMARY KEY" до "UNIQUE SUBSTITUTE af" и "PRIMARY KEY SUBSTITUTE af", где "af" (another field) - имя другой колонки той же таблицы. Машина демонстраций из записей, на которые ссылаются внешние ключи, извлекает значения af-полей и демонстрирует их вместо значений самих внешних ключей. Для этого, разумеется, ей нужно считать из системных таблиц схему хранилища (т.е. схему базы данных, из которой удалена машина 'join'). При нажатии "Enter" на поле, содержащем внешний ключ, машина открывает новое окно с той единственной записью, на которую ключ ссылается. Нажатие некоторой кнопки "All" нового окна - или пункта меню окна, или кнопки клавиатуры - отображает все записи той другой таблицы, можно делать скроллинг; а нажатие некоторой "Choice" на какой-либо записи закрывает окно и копирует её физический ключ во внешний ключ записи в первом окне. Каждое окно является точкой останова (savepoint); нажимая некоторую "Back", пользователь не только закрывает окно, но и откатывает все сделанные в нем изменения. Машина отображает комментарии к колонкам вместо их названий. Можно добавить второй комментарий для колонки, в котором указать, что сначала нужно показать в новом окне не ту вторую таблицу, на которую колонка ссылается, а третью таблицу, на которую вторая таблица ссылается - пользователь сначала выберет в третьей, потом во второй. Вот сколько запросов, произведенных машиной автоматически, порождает один print-SELECT ее клиента.

Ну, а теперь главное: позволим упоминать в запросе несуществующие, виртуальные колонки, которые представляют таблицы, ссылающиеся на данную - "SELECT v1, v2 FROM tab1 CULTIVATE v1 BY tab2(fld2), v1 BY tab3(fld3)". В таких колонках демонстрируется название таблицы, ссылающейся на них; а при попытке ввода в такое поле открывается новое окно, и отображаются записи, ссылающиеся на данное поле (нажатие "All" в окне демонстрирует все записи той другой таблицы, ссылающиеся записи выделяются цветом фона). Если в машине установлен сессионный параметр показывать по умолчанию все возможные виртуальные и невиртуальные колонки, то запросив любые поля любой таблицы, пользователь может дальше путешествовать по всему хранилищу независимо от его схемы, редактировать и заполнять его вообще без программирования, ему остается только создать саму схему хранилища. Разумеется, необходимы кнопки "Add" и "Delete" для добавления-удаления записей. Также полезны будут "Next", симметричная "Back", чтобы накатывать изменения, и "Commit".

При этом слово LIMIT ("SELECT ... LIMIT 10") относится не только к парсеру хранилища, но и к парсеру машины демонстраций: последняя должна создать полосу прокрутки по правому боку этих 10 записей, при нажатии "стрелки-вниз" на последней записи - автоматически запросить cледующую порцию "SELECT ... FROM ... DOWNWARD ... LIMIT 10" (где DOWNWARD перечисляет значения полей последней записи), при нажатии "стрелки-вверх" на первой записи второй порции - запросить предыдущую порцию "SELECT ... FROM ... UPWARD ... LIMIT 10" (где UPWARD перечисляет значения полей первой записи). И высоту слайдера в полосе прокрутки установить в соответствии с результатом "SELECT COUNT(*)".

И еще новизны следовало бы добавить вот какой. Если колонка таблицы содержит картинки или фильмы, то они воспроизводятся в поле. А не вычерпываются вручную пользователем на каком-то сервере приложений, а потом же вручную визуализируются в окне с помощью вызовов библиотек. Показ единственной картинки достигается запросом единственного поля; наблюдение real video - одновременным копированием в поле, помеченное визуальным типом "REALVIDEO", который приказывает удалять с винчестера уже продемонстрированные части BLOBa. Ширина колонок и высота строк (означающие масштабирование картинки или фильма), координаты таблицы на экране, а также возможные другие параметры визуализации данной таблицы, отличные от принятых по умолчанию или вычисленных эвристически, могут быть перечислены в других таблицах, на которые она ссылается с помощью "CREATE TABLE tab1(... , STYLED BY tab2, tab3)"; или заданы при извлечении информации с помощью "SELECT ... FROM ... STYLING ..."; или изменены пользователем прямо на экране путем манипуляций мышью.

И раз уж мы упомянули о видео, не будем также заставлять пользователя строить диаграммы вручную через машину проекций. Ему будет удобнее достать записи из таблицы обычным SELECT, а после STYLING указать тип диаграммы (график, гистограмма, "торт", "японские свечи", "векторное поле") и какие колонки таблицы для каких параметров диаграммы предназначены. Однако вернемся к визуализации строковых и числовых данных.

Другой подход к экономии сил, принципиально отличный от виртуальных колонок, проистекает из схемы IDEF1 для полей экрана в любом ПО. Эта схема полностью посторяет схему хранилища. Желая видеть, например, департаменты и их сотрудников, но не желая видеть отделы, из которых департаменты состоят, мы можем писать "SELECT Departments.Persons FROM Departments.Sections.Persons". В декартовом произведении машины 'join' невозможно различить, какие записи таблицы "Departments" каким записям таблицы "Persons" соответствуют, т.е. часть информации из хранилища при извлечении теряется. Из-за этого далают не один запрос, а столько, сколько записей в таблицах. Нотация оказывается загаженной циклами и вспомогательными операторами, время выполнения - бóльшим. Но выражение типа "tablename1.tablename2" позволяет унести ранее терявшуюся информацию. Расположение на экране структур "Departments" и "Persons" относительно друг друга, вычисляемое машиной демонстраций эвристически, можно переопределить после служебного слова STYLING.

Иначе можно отобразить две таблицы конструкцией "SELECT ... ALSO SELECT ...". Если таблицы главная и подчиненная, то в подчиненной видны только записи, ссылающиеся на текущую запись главной. Если несколько главных и одна подчиненная - то в подчиненной только записи, ссылающиеся на текущие записи главных. Если подчиненная "s" несколькими своими колонками ссылается на главную "m", то "SELECT ... INFLUENCE m(m3) TO s(s5), ..." снимает неопределенность. Визуализация на сервере устраняет необходимость размещать на клиенте программу, написанную на Delphi и прочих компилирующих RAD, или runtime библиотеку Oracle Developer и ему подобных, подключение которых - целая проблема для пользователя; устраняет необходимость компилировать программу под JVM, функциональность которой не в тандеме с оконным сервером, или транслировать под интерпретатор JS, который дублирует оконный сервер и даже с Web SQL Database, AJAX и SVG не расчитан подо все use-case-ы. Кроме того загрузка одного и того же сегмента кода в память многих клиентов (вместо памяти одного сервера) требует увеличения совокупной памяти вычислительной среды, а необходимость отвечать в реальном времени - тиражирования ядер и кэшей сервера на клиентах и их простаивания в течение больших пауз. И все это в эпоху повсеместного проникновения Интернет. Кто-то и на этих искусственных барьерах вытаскивает из нас деньги. За то, что мы не совместили X11 и SQL. И не устранили искусственное разделение на окно прикладной программы и окно терминала сегодняшних СУБД.

На языке запросов должен выполняться и просмотр таблицы как гиперкуба. Одна колонка содержит значения в узлах многомерного куба ("a" на рисунке ниже), другие колонки представляют координатные оси: их комментарии (а при отсутствии таковых - названия) отображаются как названия осей ("покупатель", "товар", "город" и т.д. - "H" и "h" на рисунке), их значения демонстрируются как отсчёты по этим осям (имена покупателей, названия товаров, названия городов - "V" и "v" на рисунке). В запросе "SELECT ... HEADER ... SIDER ... FROM" колонку для значений в узлах укажем после SELECT, колонки для горизонтальных осей (сверху вниз) перечислим через запятую после HEADER, для вертикальных (слева направо) - после SIDER. Названия осей можно перетаскивать мышью, как изменяя порядок осей, так и перемещая их из горизонтальных в вертикальные и обратно. Кроме того, по кнопке "Columns" можно получить список еще незадействованных колонок этой таблицы и выбрать какую-либо из них для значений в узлах или в качестве еще одной оси. Аналогично обычному просмотру таблицы, "LIMIT 9, 5" укажет, что большая часть данных не сразу отображается на экране, а подгружается по мере скроллинга; размер горизонтального и вертикального слайдеров машина демонстраций вычислит сама.

Чтобы увидеть таблицу с "медвежьим ухом" (внешний ключ ссылается на физический ключ той же таблицы) в виде дерева, а не как плоскую таблицу, а заодно чтобы разрешить неопределенность в случае нескольких "ушей", придется указать, какой из внешних ключей имеется ввиду ("SELECT ... TREE ... FROM", колонка после SELECT содержит текст для узла дерева, а колонка после TREE определяет используемый внешний ключ). ORDER BY упорядочивает узлы одного уровня вложенности с общим родителем. Пользователь может на экране открывать и закрывать, добавлять и удалять, перемещать и копировать узлы, в т.ч. и между двумя SELECT TREE, выполненными в одну таблицу - действия мышью автоматически пораждают SQL-запросы машины демонстраций в хранилище.

Параметры визуализации дерева могут быть следующими. IMAGE определяет BLOB-колонку с картинкой для узла или колонку-внешний-ключ, ссылающуюся на такой BLOB. SWITCH - булевскую колонку, указывающую открыт или закрыт узел. Следующие параметры сами по себе являются булевскими. ONELINE указывает продемонстрировать узлы одного уровня вложенности в одной строке, а не друг под другом. CENTER - расположить дочерние узлы по центру родительского, а не сбоку (слева для арабского письма, справа для всех остальных; если одни узлы на арабском, а другие нет, то они находятся по разные стороны от родительского). MARKER - отображать маркер в узле вместо плюсика и картинки, как в ненумерованном маркированном списке (как меняются фигурки маркера в зависимости от глубины узла рассказано ниже в разделе "Наблюдение текста"). COWPATH - демонстрировать дерево не традиционным образом, а показывать для узла, на котором находится фокус, только непосредственно вложенные в него узлы, но не более далеких его потомков; все родительские узлы вместе с тем, на котором находится фокус, не отображать, а перечислять через слэш в качестве заглавия всего дерева подобно тому, как путь отображается в программе TotalCommander. Ну и наконец, параметр PAPER определяет текстовую колонку; если она определена, дерево отображается как документ, названия узлов - как названия подразделов документа, а содержимое текстовой колонки - как содержимое подразделов.

Электронная таблица a-la Excel похожа на двумерную сетку, задающую границы объемных фигур (sql5.19.3.pdf , с.229): в первой колонки, содержащие контент ячейки, отмечены как "VARCHAR CELL", во второй - колонки, содержащие точки, отмечены как "NUMBER ARRAY[3] DOT"; в первой в дереве на один уровень выше расположены строки электронной таблицы, во второй - четырехугольники. В обеих записи остальных уровней не визуализируются. Отображаются электронная таблица и объемная фигура одним и тем же SELECT.

С незапамятных времен во всех кодировках текста есть непечатные символы для управления этим самым текстом. Будем использовать их для вставки в текст маркированных списков, электронных таблиц, гипер-ссылок, а также - если мы имеем дело с формой - элементов управления. Все эти объекты находятся в отдельных полях хранилища, а не содержатся посреди текста, что является результатом перехода от файлового мировозрения к структурному. Непечатные символы ограничивают SELECT, извлекающий эти объекты. Если пользователь в оконном сервере отредактирует маркированный список или электронную таблицу, машина демонстраций скопирует изменения в хранилище, если установит чекбокс или переключит радиобокс - изменит состояние соответствующего поля хранилища, если нажмет кнопку - напрямую скомандует запустить её триггер. В отличие от применяемой сегодня последовательной передачи переменной по траектории "стандартная библиотека ввода-вывода - специальная часть прикладной программа для пересылки между двумя библиотеками - библиотека для подключения к хранилищу" (причем процедуры библиотек для передачи в прямом и обратном направлениях разные), наш SELECT скорее является просто адресом объекта в хранилище. Такой способ действительно доступен массам населения - в отличие от применяемого сегодня. Контраст становится особенно разительным при выходе в интернет. Кроме прочего, хранилище может быть изменено и внешним агентом, что позволяет динамически расширять списки и деревья.

По поводу представления в хранилище нужно заметить, что выпадающий список с единственным выбором функционально совпадает с радио-группой, а с множественным выбором - с группой чекбоксов. А значит расставлять их надо не по одному каждый раз вручную в собственной программе, а переложить эту работу на машину демонстраций. У нас горизонтальное направление письма, а не вертикальное; поэтому боксы с легендами, вытянутые в горизонтальную линию, не дают визуального ощущения их группового единства; кроме того, очень малое количество легенд можно расположить в один ряд. Поэтому боксы располагают вертикально. Достаточно после STYLING или с помощью STYLED BY определить способ отображения - выпадающим списком или группой боксов. Пусть за это отвечает параметр ANGLE (подробнее о нем ниже), равенство его NULL означает выпадающий список, равенство нулю или любому другому числу - группу боксов. С чек-ситуацией всё понятно - нужно запросить булевскую колонку таблицы. Для радио-варианта в качестве механизма извлечения из хранилища будем использовать "SELECT ... FROM a ALSO SELECT ... FROM b FILLING a(a1) BY b(b1) STYLING angle=0", где поле 'a1' в результате действий пользователя должно принять одно из значений колонки 'b1', показ единственного поля 'a1' достигается запросом единственной строки таблицы 'a', одна из таблиц самостоятельно не демонстрируется вообще - 'b' при 'angle=null', 'a' при 'angle=0'. В последнем случае запрос более одной строки таблицы 'a' является ошибкой, ибо поле-приемник неизвестно.

Применительно к хранению также нужно заметить, что в некоторых случаях логика приложения такова, что гипер-ссылка более естественно выглядела бы в виде кнопки. Таким образом структуре в хранилище, отображаемой как кнопка, иногда соответствует триггер, иногда - адрес. Пометим её поля или одно поле, содержащее массив чисел с размерами кнопки, визуальным типом BUTTON, чтобы структура не отобразилась как строка таблицы. Все остальные числа структуры содержат адреса одного и того же документа; их много на случай, если некоторые адреса окажутся битыми. Машина демонстраций перепроверяет их автоматически один за другим при нажатии структуры (кнопки или ссылки) в оконном сервере и извлекает документ с первым действительным адресом. Не будем требовать, чтобы были возможны либо триггеры, либо адреса. Но если хоть один адрес есть, структура отображается как ссылка даже если установлен триггер. Единственное ограничение целостности, налагаемое хранилищем на BUTTON-структуру - текствое поле в ней должно быть одно.

Итого, при выводе любых данных в три оператора CREATE TABLE, INSERT, SELECT выводим в три оператора и "Hello, world". Stdin и stdout, как мы помним, отсутствуют - существует только копирование записей или обновление полей.

Итоги архитектуры ОС

Print-SELECT в составе ОС наконец-то позволяет простому смертному не реализовывать интерфейс, а получать его даром; модуль транзакций, триггеров и прав на структуру вместо обмена сообщениями IPC и прав на файл позволяет удалить второй барьер "Vendor lock-in"; этот же модуль вместе с оконным сервером - навязать совместимый формат передачи данных и убрать драйвера; машины проекций и демонстраций изымают существенные компоненты коммерческого ПО и некачественного бесплатного, облегчая компьютер и обеспечивая вынужденную совместимость.

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

Если Microsoft погибнет в мире настольных приложений, то только через понижение цены производства программ на другом API. А для этого тот другой должен быть крайне легким в применении. Например таким, как было описано выше.

Дмитрий Тюрин (DmitryTurin.narod.ru):
dima.turin@centrum.cz (все письма из домена .ru попадают в спам), dima.turin@gmail.com