Русский | English   поискrss RSS-лента

Главная  → Документы и публикации  → Материалы конференций  → Материалы Международной конференции Sorucom-2017  → Забытое 40 лет назад новое и как оно может изменить нашу жизнь

Забытое 40 лет назад новое и как оно может изменить нашу жизнь

Постановка задачи

За последние десять лет вычислительная техника глубоко вошла в быт. Достаточно вспомнить мобильную связь, облачные технологии и IoT.

Вот краткий и совсем не полный перечень технологических прорывов, которые изменили повседневную жизнь и работу:

Замечу, что многие технологии и устройства так прочно вошли в нашу жизнь, что трудно представить себе, что совсем недавно их не было.

Как мы все понимаем, внедрение новых технологий потребовало существенного объема программирования. Любопытно (и грустно), что при этом технологии программирования использовались старые. По сути, технологии программирования существенно не менялись на протяжении двадцати лет. В конце прошлого века надежда на развитие еще была, достаточно вспомнить бурное развитие OOP, идеи COP (Component-oriented Programming), Generative Programming, DSL (Domain Specific Languages), продвижение в верификации программ. Все эти технологии и идеи по-прежнему как-то развиваются, но надежды на существенное улучшение благодаря им уже нет. Мы по-прежнему программируем долго, дорого и не надежно, а разработка программного обеспечения по-прежнему далека от нормального производства, настолько она плохо планируема и слабоуправляема.

Отсутствие существенного прогресса может быть объяснено принципиальной сложностью программирования (см. например [1]) или тем, что мы подошли настолько близко к совершенству, что развитие может идти только медленно.

По поводу принципиальной сложности, замечу, что сложность обязательно будет, если не ставить себе задачу упрощения, вспомним “Make it simple as possible”. Увы, но задача упрощения вышла из моды. Полагаю, что одна из причин этого – это трудность упрощения.

Что же касается аргумента о «медленности развития…», приведу еще одну аналогию. Мы можем двигаться медленно к цели, потому что идем не прямо к ней, а, под углом (например, 80 градусов). Поэтому КПД очень мал.

Используем прием, предложенный Раймондом Джоунсом в фантастическом рассказе "Уровень шума" . Предположим, что принципиально лучшие технологии программирования (быстрее, дешевле, надежней) существуют, и попробуем хотя бы одну воспроизвести.

Возникает вопрос – с чего начать?

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

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

Очевидно выделенным является b2c сектор, назовем его программированием приложений (см. например, Google Play Market). Для того чтобы приложения могли работать, необходимы обеспечивающие уровни (например, облака) и инфраструктура. Тем самым явно выделяются еще два слоя – производственный или корпоративный (b2b) и государственный (инфраструктурный).

Мы получаем следующие слои, что интересно, с разными приоритетами требований к программам:

Другой (сходный) взгляд на разделение программирования был предложен Русланом Богатыревым в обсуждении. Он предложил ввести в программирование понятие аналогичное сопромату. Очевидно, что требования к надежности программы управления АЭС и игрушке на смартфоне принципиально разные. По сути, «материал», из которого изготовлены эти программы, должен быть разным, так как он подвергается принципиально разным «нагрузкам». Следовательно, и средства разработки должны быть разными. Сейчас этого нет.

Для простоты начнем с самого верхнего слоя. Является ли слой приложений самым верхним? Безусловно, что он был верхним слоем до последнего времени, но развитие гаджетов и IoT привело к появлению еще одного слоя, который пока не выделен понятийно.

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

Назову такое программирование бытовым программированием.

Бытовое программирование

Бытовое программирование – программирование человеком своих устройств для решения бытовых задач. Бытовое программирование очевидно распространяется на решения семейных задач и (частично) задач индивидуальных предпринимателей и малых предприятий. К «своим» устройствам я отношу не только гаджеты и компьютеры, а также сервисы и ресурсы (например, Яндекс.Диск), которые могут использоваться для решения своих задач.

Главной особенностью бытового программирования является то, что программы делает не профессиональный программист.

Зачем я выделяю уровень бытового программирования?

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

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

Несколько опережая логику рассуждения, скажу, что бытовое программирование, на мой взгляд, — это не синтезирующее программирование в классификации А.П. Ершова [2]. Это, скорее, сборка некого «супер-приложения» из сервисов и компонентов разных производителей, работающих на разных устройствах.

Рассмотрим бытовое программирование на жизненном примере.

Ребенок идет домой из школы. Хочу:

Получить сигнал, когда он вышел из школы

Получить тревожный сигнал, если он вышел не вовремя

Посмотреть маршрут движения на карте – онлайн или позже

Получить сигнал, что он дошел до дома

Очевидное условие: у ребенка должно быть устройство с GSM/GPS (часы, браслет или телефон)

Что мне, как бытовому программисту, нужно для решения этой задачи:

Если все это у меня есть, я могу написать скрипты и расставить триггеры для запуска этих скриптов. Скрипты и триггеры в совокупности и составляют «супер-приложение», решающее задачу.

Набросаю часть решения (то, что нужно запрограммировать):

Очевидна похожесть супер-приложения на программу на визуальном языке Scratch (https://en.wikipedia.org/wiki/Scratch_(programming_language)). Программа на Scratch также есть совокупность скриптов, срабатывающих по триггерам, например, триггер начала работы, триггер касания объектов на экране и т.д. Разница в том, что супер-приложение является распределенным и меж-платформенным.

Рассмотрим теперь, что нужно бытовому программисту (родителю в данном случае), чтобы собрать супер-приложение:

Самое важное здесь, на мой взгляд, это возможность использование сервисов и компонентов (разных разработчиков, на разных устройствах). По сути, мы говорим здесь о создании объединяющей экосистемы, которая дает возможность доступа к существующим экосистемам (iOS, Android, Java, C++, Linux, Windows…). Замечу сразу, чтобы задача не выглядела слишком большой – дает возможность доступа в рамках нужных для бытового программирования.

Рассмотрим теперь принципиальный вопрос – а нужно ли вообще бытовое программирование?

Задачу с отслеживанием ребенка я могу решить, купив детские GPS/GSM часы, в комплекте которых я получу программу отслеживания для смартфона. Но что если мне надо еще включить мультиварку, чтобы подогреть обед, когда ребенок вышел из школы. Предусмотрит ли разработчик приложения всевозможные настройки для своей программы? В том числе и взаимодействие с устройствами, которые еще не появились на рынке?

Мы можем идти традиционным путем и иметь непрерывное обновление всех приложений (а для этого непрерывную работу разработчиков) или перейти к разработке отдельных компонент и сервисов, которые соединяются скриптовым «клеем». Если мы идем вторым путем, задача разбивается на отдельные (в идеале ортогональные) части, а это должно существенно сократить общие затраты на разработку. Результатом работы профессионального программиста должны быть, в первую очередь, компоненты и сервисы.

Если задуматься, то я не предлагаю ничего нового, например, мы можем вспомнить SLR (Single Responsibility Principle), а так же Unix Way:

Это нам и нужно, только на новом уровне:

О самоописываемых компонентах надо поговорить подробнее. В них также нет ничего принципиально нового, вот, например, рекомендации Microsoft для .NET: https://msdn.microsoft.com/en-us/library/xcd8txaw(v=vs.110).aspx

Каждая самоописываемая компонента (сервис) должна (по запросу) выдавать информацию двух видов:

  1. Документацию для человека – назначение компоненты, способы использования, шаблоны использования.

  2. Формальное описание для обращения к компоненте из скрипта: список функций, сообщений, сигналов, уведомление. Для функций: сигнатура функции (ABI , например, LLVM-type). Для потоков данных: название стандартного протокола или формальное описание протокола (для специфических протоколов).

Принципиально тут то, что эта информация «привязана» к компоненте. Документацию не надо брать (искать) извне. Тогда документация точно соответствует версии компоненты/сервиса.

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

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

Кроме запроса к компоненте/сервису, должна быть возможность сделать запросы к устройству, позволяющие:

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

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

Пример 1. Яндекс навигатор на карте Гугл.

Для бытового программиста Яндекс.Навигатор и Google Maps должны быть компонентами (как и Яндекс.Карты и многое другое). Так как Яндекс.Карты и Google Maps имеют аналогичное назначение, то естественно предположить что один из интерфейсов (разъемов) у них должен совпадать. И тогда, подключение навигатора к другой карте – это несколько строчек кода (или несколько визуальных блоков) в бытовом скрипте.

Замечание: «интерфейс» - это не интерфейс класса на некотором языке. Все интерфейсы бытового программирования должны быть языково-независимы, а типизация должна быть утиной (duck typing).

Пример 2. Объединенный календарь

Я хочу выбрать время для какого-то семейного действия. У меня есть:

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

Почему бы мне не сделать объединенный календарь, который показывает все пересечения?

Естественно, что говоря «объединенный календарь», я в действительности, говорю о двух разных сущностях:

  1. GUI часть календаря, позволяющая показать пересечения событий.

  2. Календарь-данные - это компонента (сервис), у которой можно запросить множество событий за определенное время.

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

На этом примере, мы видим еще одно важное следствие – бытовое программирование вынуждает нас определить Лексикон бытового программирования, по крайней мере, набор стандартных интерфейсов, таких, как «Календарь-данные» и «Календарь-отображение».

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

Предпосылки бытового программирования

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

Основными предпосылками являются уже упоминаемые мной гаджеты, облачные сервисы, IoT. Предпосылки другого рода это:

Предпосылки второго рода я бы назвал «психологическими», так как они упрощают понимание бытового программирования, хотя и не могут быть непосредственно использованы в бытовом программировании.

Но есть и непосредственные предпосылки, среди которых принципиальным для бытового программирования является технология разработки мульти-платформенных программ. Я использую термин «мульти-платформенный», а не «кроссплатформенный», так как речь идет не о «переносе» программы с одной платформы на другую, а об одновременной работе частей распределенной программы на разных платформах. Увы, на рынке нет ни одной (работающей) технологии и среды разработки мульти-платформенных программ.

Замечание, системы кроссплатформенной разработки существуют, достаточно упомянуть Xamarin, Unity, Marmalade, Lazarus, но они малопригодны для разработки мульти-платформенных программ по нескольким причинам, которые надо обсуждать отдельно.

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

Компилирующая система LLVM (http://llvm.org) является существенным и принципиальным заделом для реализации бытового программирования. На мой взгляд, если бы LLVM не было, разговор о реализации бытового программирования был бы невозможен.

Альтернативой могло бы быть использование виртуальных машин (a la Java и .Net), но я считаю этот путь принципиально неверным. Виртуальная машина добавляет еще один уровень сложности, причем взаимодействие кода, исполняемого в виртуальной машине и кода исполняемого за её пределами, как показывает практика, неизбежно. Таким образом, использование виртуальной машины увеличивает сложность разработки и уменьшает производительность, при этом, не приносит никаких существенных преимуществ.

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

Поколение 1. Исторически первым известным мне способом сделать компилятор, способный строить рабочую программу для нескольких процессоров (платформ) была генерация кода для абстрактной машины и перенос этой машины на разные платформы. Известный пример – P-code для Паскаля (1973 г.). Использование P-code было не первым использованием этого способа, достаточно вспомнить O-code для BCPL (1966 г.) и SILдля Snobol-4 (1967 г). Недостатки использования абстрактной машины хорошо известны на примере Java, а часть недостатков уже упомянуты выше.

Поколение 2. Начиная с 1980-х, компиляторы с языка Си стали доступны для всех платформ и появлялись на новых платформах в первую очередь. Это привело к идее использовать Си в качестве выходного языка (переносимого ассемблера ) для компилятора. Такой способ мы использовали в компиляторе Extacy (http://www.kronos.ru/literature/extacy, 1991 г). Extacy позволял строить Модула-2 и Оберон-2 программы для нескольких десятков различных платформ. Перенос на другую платформу не требовал реализации виртуальной машины, и, как правило, сводился к достаточно простым правкам в исполняемой среде (Run-Time System), написанной на Си. В отличие от способа с использованием виртуальной машины, потери производительности не было. Основные недостатки этого способа прямо вытекают из использования Си:

В Extacy на уровне языков Модула-2 и Оберон существенного семантического разрыва с Си не было. А вот среда исполнения Си существенно отличалась от среды исполнения в Excelsior IV (http://www.kronos.ru/documentation/sys), в которой принципиальным была динамическая загрузка модулей и расширяемость системы, и мы даже не пытались эмулировать её через Си на других платформах.

Поколение 3. Один язык, сменная генерация кода. Идея сменной генерации очевидна: делим компилятор на две части. Первая часть (front-end) строит синтаксическое дерево, выполняя необходимый анализ, вторая (back-end) порождает код для конкретной платформы. Вторую часть делаем сменной.

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

Недостатки: трудоемкость перехода на новую платформу. Условно половину компилятора надо написать заново.

Я не смог достоверно определить, в каком первом компиляторе в истории использовалась идея сменных генераторов кода, поэтому приведу в качестве примера компилятор OP2 (Oberon Portable Compiler) [3], с которым я познакомился в 1991 году в ETH Zurich. Знакомство с этим компилятором подтолкнуло меня к разработке XDS [4].

Поколение 4. Многоязыковые, многоцелевые компилирующие системы.

Очевидная мысль, которая привела к разработке компилирующих систем: «заменить трансляцию с m входных языков в n машинных языков трансляцией “из m в один” и “из одного в n”» [5].

Компилирующие системы строятся по схеме:

Любопытно, что компилирующие системы, в каком-то смысле, являются обратными по отношению к компиляторам 3-го поколения, так как оптимизаторы и генераторы кода является постоянными частями, а анализаторы языков программирования – сменными.

Не претендуя на полноту изложения, вкратце опишу две компилирующие системы: XDS и LLVM.

Первая версия XDS вышла в 1994 году, она поддерживала компиляцию языков Modula-2 и Oberon-2 в код x86 процессоров и трансляцию в Си. Тогда же была начата работа над машинно-независимым оптимизатором, с появлением которого XDS стала классической компилирующей системой, которая включала не менее 5 языков и примерно столько же генераторов кода.

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

На первый взгляд XDS и LLVM похожи, как близнецы братья: та же архитектура (front-ends, middle-end, back-ends), схожая оптимизация на основе SSA формы.

Существенным преимуществом LLVM является удачный выбор промежуточного языка. В XDS, как и в OP2 в качестве промежуточного языка (выхода front-end) использовалось синтаксическое дерево. Далее синтаксическое дерево переводилось в форму удобную для оптимизации и далее в форму, подходящую для генерации кода.

Использование синтаксического дерева накладывает ограничение на компилируемые языки программирования из-за возможного семантического разрыва. Для XDS это не имело большого значения, так как все компилируемые языки (Модула-2, Оберон-2, Java, диалект Паскаля (Mitel Networks), SL1 – proprietary Nortel Networks language) были достаточно близкими, но в общем случае, это существенное ограничение.

Промежуточный язык LLVM (LLVM IR) существенно ниже уровнем, это типизованный абстрактный ассемблером. При этом задача преодоления семантического разрыва ложится на разработчика front-end.

Еще одной принципиальной особенностью, является то, что для оптимизатора LLVM IR является и входным и выходным языком. А это позволяет компилировать части кода (компоненты, скрипты) в IR и хранить их в виде IR, а потом переводить в код нужной платформы в любой удобный момент, включая JIT.

И наконец, LLVM – это не монолитное приложение, а набор приложений и библиотек, что-то вроде конструктора «Сделай себе компилятор сам».

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

Реализация бытового программирования

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

Изложу кратко свое видение:

  1. Среда разработки с возможностями

    • Подключения устройств

    • Получения каталога компонентов и сервисов устройства

    • Поиск среди компонентов и сервисов устройства

    • Поиск в общем репозитории (Лексиконе бытового программирования)

    • Установка компоненты или сервиса из репозитория на устройство

    • Визуальная или текстовая разработка скрипта

    • Привязка скриптов к устройствам

    • Задание триггеров

  2. Среда выполнения скриптов (на каждом устройстве)      

    • Загрузка, обновление и удаление скриптов с устройства

    • Реализация триггеров

    • Реализация переходников к ОС, позволяющих использовать средства ОС стандартизированным образом

  3. Стандарт на компоненты, сервисы и способы подключения и взаимодействие
  4. Репозиторий – Лексикон бытового программирования, содержащий

    • Стандартные интерфейсы

    • Стандартизованные компоненты разного уровня и сервисы

    • «Обертки» для не стандартизованных компонентов и сервисов, которые реализуют стандартные интерфейсы

  1. Для того чтобы программисты готовили компоненты и сервисы в соответствии со стандартами нужны инструменты, позволяющие добавить нужные обертки к существующим компонентам или код.

  2. Создание курсов бытового программирования и внедрение обучения.

Заключение

Любопытно, что поиск лучшей технологии программирования привел нас к Лексикону, о котором А.П. Ершов написал в 1983 г. в провидческой статье «Предварительные соображения о лексиконе программирования» [2]:

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

Естественно, что бытовому программированию и использованию Лексикона надо учить, что приводит нас статье А.П. Ершова 1981 г.: "Программирование — вторая грамотность" [6], к новому воплощении идеи о всеобщем обучении программирования. Прошло чуть больше 40 лет, и мы пришли к реальной потребности в воплощении идей А.П. Ершова.

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

Предполагаю, что когда появится Лексикон бытового программирования, начнет наполняться и более общий Лексикон (программирования приложений и производственного программирования) - репозиторий стандартных языково-независимых и кросс-платформенных компонентов.

В настоящее время часть изложенных идей проверена для одной платформы, идет работа над мультиплатформенной средой разработки Вир-2 (http://алексейнедоря.рф/?p=186), основанной на LLVM.

Список литературы

  1. Booch, Grady (1997). Object-Oriented Analysis and Design with Applications. Addison-Wesley. ISBN 0-8053-5340-2.

  2. Ершов, А. П. Предварительные соображения о лексиконе программирования// Избранные труды. Новосибирск, 1994.

  3. Régis Crelier. OP2: A Portable Oberon-2 Compiler Presented at the 2nd International Modula–2 Conference, Loughborough, Sept 91

  4. Недоря, А. Е., Расширяемая переносимая система программирования, основанная на биязыковом подходе : диссертация кандидата физико-математических наук : 05.13.11. - Новосибирск, 1993. - 139 с.

  5. Любимский Э. З., Поттосин И. В., Шура-Бура М. Р., От программирующих программ к системам программирования // Становление Новосибирской школы программирования. Мозаика воспоминаний. - Новосибирск: Институт систем информатики им. А.П. Ершова СО РАН. - 2001. - С. 18-27.

  6. Ершов, А. П. Программирование — вторая грамотность// Избранные труды. Новосибирск, 1994.

Об авторе: к.ф.-м.н. Синергетик Лаб, ООО Санкт-Петербург, Россия
Материалы международной конференции Sorucom 2017
Помещена в музей с разрешения автора 24 Июня 2018

Проект Эдуарда Пройдакова
© Совет Виртуального компьютерного музея, 1997 — 2018