Архитектор
0,0
рейтинг
2 февраля 2012 в 04:46

Разработка → Метапрограммирование

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

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

Какие мы знаем техники метапрограммирования (я надеюсь, что меня дополнят):

1. Компилируемые техники
1.1. Шаблоны, макросы и параметрический полиморфизмом. Для одного блока кода на этапе прекомпиляции генерируется несколько вариантов для разных типов параметров, это самый древний тип матапрограммирования.
1.2. Оптимизирующая прекомпиляция. Вычисление и оптимизация всех выражений и целых функций, которые не содержат переменных, на этапе компиляции или прекомпиляции.
1.3. Генераторы исходного кода для компилируемых языков. Перед компиляцией, на этапе сборки проекта, пакетным образом, по метаданным или без их, генерируются файлы, функции, классы, формы и т.д.
1.4. Написание своего предметно-ориентированного компилируемого языка (под определенный круг задач) — DSL (Domain-specific Programming Language).

2. Интерпретируемые техники
2.1. Эвалуациея кода (eval) из строковых переменных в интерпретируемых языках или языках, поддерживающих позднюю компиляцию в байт-код во время исполнения.
2.2. Написание своего специализированного интерпретируемого императивного языка под задачу (LOP — Language Oriented Programming).
2.3. Создание или использование декларативного языка, формата сериализации, специального синтаксиса или подмножества таких синтаксисов как XML, JSON, XAML и др.
2.4. Динамическое формирование и исполнение кода на языках запросов, например: SQL, XQuery, LINQ и др.

3. Гибридные техники
3.1. Интроспекция — предоставление доступа к внутренним структурам языка, типам данных, классам, функциям и т.д. Получение метаданных о структурах и обход их в цикле, или получение параметров функции как массива с возможностью анализировать класс каждого.
3.2. Динамическая интерпретация метамоделей. Специальная терминология для этого типа еще не разработана, точнее, она не устоялась, поэтому я опишу его подробнее ниже. А тут же дам приведу основную особенность: метамодель (модель предметной области на мета-языке) содержит как императивные так и декларативные компоненты влияющие друг на друга, а приложение становится «виртуальной машиной» для запуска метамодели.
3.3. Распределенная информационная система с динамическим связыванием на базе интерпретации метамоделей. Это применение пункта (3.2) к клиент-серверному или межсистемному взаимодействию.

Динамическая прикладная среда для бизнес-объектов

Теперь подробнее о динамической интерпретации мета-моделей. Как же императивные и декларативные конструкции влияют друг на друга? Понятно, мы анализируем декларативные описатели — некий формализованный формат данных, регулярную грамматику, которую можно без труда отпарсить регулярными выражениями, строковыми операциями или берем уже готовый парсер (например, JSON или XML). Таким образом, императивный алгоритм может менять структуру и последовательность исполнения кода. Но более того, мета-модель содержит еще и интерпретируемые скрипты для выражения бизнес-логики, скрипты получают уже развернутую в памяти декларативную часть мета-модели в свое полное распоряжение и могут обращаться к библиотеками. Тут все подумали про JavaScript конечно. Замечу, что скрипты из мета-модели не имеют доступа к интраспекции «виртуальной машины», а только к данным и метаданным, т.е. к модели предметной области. Такой скрипт может не знать даже, где он запущен, на клиенте или на сервере. Прикладная среда — это прослойка между прикладной моделью и операционной системой, позволяющая виртуализировать прикладное приложение, отвязывая его от системы и платформы.

Облачные технологии и виртуализация сейчас поднялись только до уровня инфраструктуры и платформы, следующий же, прикладной уровень практически не затронут. Все более четко проявляется потребность в новой архитектуре прикладных приложений. Сервисная архитектура (SOA), несомненно позволяет удовлетворить большинство нужд корпоративных систем, однако, в силу своей тяжеловесности, статичности и сложности разработки, только крупные компании могут себе позволить разработку ПО в этой технологии. Это обусловлено сложным технологическим циклом создания ПО, включающем проектирование, программирование, отладку, тестирование, сборку, доставку пользователю, инсталляцию, настройку и поддержку. Каждая новая версия прикладной системы требует множества шагов для того, чтобы стать доступной на рабочем месте пользователя. А переход между версиями сопряжен со сложностями конвертации данных и обратной совместимости форматов обмена данными и форматов файлов. Все это является результатом следующих особенностей архитектуры программных систем:
1. Смешивание в программном коде абстракций разного уровня внутри одного класса или модуля. Например, реализация чтения/записи из БД, бизнес-логики и визуализации в одном классе.
2. Высокая связанность кода двух смежных абстрактных слоев приложения. С односторонней или двухсторонней зависимостью слоев. Например, отдельный оконный АРМ или веб-интерфейс жестко привязаны к набору функций серверного API, их параметрам, типам данных и классам, а серверное приложение привязано жестко к структуре таблиц в базе данных. Часто, высокая связанность выражена в наличии зашитых в коде идентификаторов классов и функций, интерфейсов и параметров, таблиц и полей.
3. Сборочно-ориентированный жизненный цикл и компиляция бизнес-моделей в машинный код или в байт-код с последующий ручным развертыванием на сервере.
4. Жесткая фиксация интерфейсов между модулями системы и сетевых интерфейсов (по структуре, параметрам вызова и типам данных).

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

Теперь инвертируем 4 причины негибкости систем с помощью различных техник метапрограммирования и получим рецепт для систем с динамической интерпретацией метамодели:
1. Разделение абстракций разного уровня в программном коде. Например, уровень визуальных компонентов, уровень сетевого транспорта, уровень библиотеки прикладных алгоритмов и уровень бизнес-модели могут не быть связаны друг с другом на этапе компиляции среды, однако, связи будут динамически построены на основе метаданных в момент запроса к соответствующему функционалу и закешированы до момента изменения метамодели.
2. Отсутствие прямых и обратных зависимостей в абстрактных слоях приложения, использование техник метапрограммирования, интраспекции, декларативных и активных языков для описания бизнес-объектов. При этом, внутренняя связанность классов внутри бизнес-модели может быть повышена.
3. Использование компиляции для уровня прикладной среды и принципа интерпретации для уровня бизнес-моделей. Для среды жизненный цикл остается сборочным, а вот бизнес-модель может изменяться хоть каждую минуту без повторного развертывания.
4. Введение динамических интерфейсов между модулями (описываемых декларативными языками) и сетевых интерфейсов поддерживающих интраспекцию на уровне сетевого протокола для взаимодействия приложений с динамической структурой и параметрами.

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

Распределенная информационная система с динамическим связыванием

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

Десерт

Ну и напоследок, ради того, чтобы потешить эстетическую сторону уважаемой аудитории, покажу маленький пример использования метапрограммирования для анимации в JavaScript. Недавно была статья про impress.js и как-то так совпало с тем, что я думал о подобном решении в разрезе своего помешательства на метапрограммировании и вот что получилось, см. живой пример. А вот код от него:

Императивная часть:

$(function() {
	var Images = $("#sky div"), Timers = [];
	Images.each(function() {
		fly = function(Id,Range,Duration,Direction) {
			var Way1 = {}, Way2 = {};
			Way1[Direction] = '+='+Range;
			Way2[Direction] = '-='+Range;
			$('#'+Id).animate(Way1,Duration).animate(Way2,Duration);
		}
		var Image = $(this),
			Id = Image.attr('id'),
			Range = Image.attr('data-fly-range'),
			Duration = parseInt(Image.attr('data-fly-duration')),
			Direction = Image.attr('data-fly-direction');
		Timers[Id] = setInterval('fly("'+Id+'","'+Range+'",'+Duration+',"'+Direction+'")',Duration*2);
		fly(Id,Range,Duration,Direction);
	});
});


Декларативная часть:

<div id="sky">
<div id="castle-white" class="castle"
	style="background:url(images/castle-white.png) no-repeat; left:350px; top:-10px; width: 287px; height: 698px;"
	data-fly-range="10" data-fly-duration="3000" data-fly-direction="top"></div>
<div id="castle-indigo" class="castle"
	style="background:url(images/castle-indigo.png) no-repeat; left:560px; top:235px; width: 471px; height: 397px;"
	data-fly-range="15" data-fly-duration="2500" data-fly-direction="top"></div>
<div id="fog-03" class="fog"
	style="background:url(images/fog-03.png) no-repeat; left:500px; top:325px; width: 599px; height: 310px;"
	data-fly-range="15" data-fly-duration="4000" data-fly-direction="left"></div>
<div id="castle-blue" class="castle"
	style="background:url(images/castle-blue.png) no-repeat; left:690px; top:350px; width: 521px; height: 288px;"
	data-fly-range="10" data-fly-duration="2000" data-fly-direction="top"></div>
<div id="fog-02" class="fog"
	style="background:url(images/fog-02.png) no-repeat; left:50px; top:280px; width: 1279px; height: 375px;"
	data-fly-range="20" data-fly-duration="3500" data-fly-direction="left"></div>
<div id="castle-green" class="castle"
	style="background:url(images/castle-green.png) no-repeat; left:470px; top:60px; width: 353px; height: 621px;"
	data-fly-range="15" data-fly-duration="1500" data-fly-direction="top"></div>
<div id="castle-yellow" class="castle"
	style="background:url(images/castle-yellow.png) no-repeat; left:210px; top:50px; width: 267px; height: 630px;"
	data-fly-range="5" data-fly-duration="500" data-fly-direction="top"></div>
<div id="fog-01" class="fog"
	style="background:url(images/fog-01.png) no-repeat; left:100px; top:435px; width: 580px; height: 194px;"
	data-fly-range="25" data-fly-duration="3000" data-fly-direction="left"></div>
<div id="castle-orange" class="castle"
	style="background:url(images/castle-orange.png) no-repeat; left:-110px; top:-30px; width: 524px; height: 736px;"
	data-fly-range="20" data-fly-duration="1000" data-fly-direction="top"></div>
</div>
Timur Shemsedinov @MarcusAurelius
карма
160,5
рейтинг 0,0
Архитектор
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (28)

  • +2
    Очень изящный пример
    • +1
      это для метапрограммирования вообще характерно. мы так думаем — поэтому оно проще и наглядней, чем втискивание нестандартной задачи в рамки (будь то императивный код, ООП или еще что)
  • +3
    Доктор, а откуда у вас такие картинки?

    (из примера, в смысле).
    • +1
      Картинки из одного из проектов, который у нас в разработке. Это интерактивные детские книжки с анимацией и играми.
  • 0
    За этим будущее.
    • +1
      Только нужно теперь более простым языком и в примерах все разложить, а то меня уже ругают за сложную лексику.
  • 0
    Я думаю, было бы не плохо если вместо многих страниц с пояснением вы бы сделали демо видео.
  • 0
    Если кто может литературу по этому вопросу насоветовать, то кидайте названия и ссылки, а то обнаружилось, что по этому вопросу очень мало написано. Точнее, по шаблонам, макросам и генерации кода — много чего есть, но интересное же не в этом.
  • +1
    за такой javascript — стрелять.
    • 0
      А что конкретно Вам в нем не нравится?
      • +4
        eval вместо замыканий, например так: pastebin.com/69WJ5ds4
        • +1
          Действительно, так более доступно для восприятия, но суть то не меняется.
        • 0
          Мои извинения, я сначала было подумал, что Вы имеете что-то против обхода мета-модели и получения из нее параметров поведения. А этого моя гордыня бы не пережила.

          Хотя использование эвала может пригодиться даже в этих замках из примера. Представим, что на каждый замок нужно написать свой эффект, тогда этот эффект на js можно поместить в атрибут data-animation- fx и оттуда делать eval. Хотя, если аллергия на эвал, то можно каждый эффект проименовать и писать в атрибут только его имя, это уже на любителя. В других языках я тоже стараюсь без эвала, но на js это как-то естественно.
          • 0
            если у замка будет свой эффект отличный от реализованного во fly,
            то не поможет и 'fly("'+Id+'","'+Range+'",'+Duration+',"'+Direction+'")' и все data- атрибуты.
            евал не решает эту задачу, метамодель негодная.

            ну или делали бы уж сразу пример, демонстрирующий пользу евала и использование кода в метамодели.
  • +13
    Брр, какая-то смесь метапрограммирования, метамоделирования, процесса выполнения программы и облачных вычислений. Всё это хорошо по отдельности, но зачем всё собирать под одну гребёнку?
    «Программирование» — означает создание компьютерных программ. «Мета» — означает абстракцию над каким-то набором понятий. Метапрограммирование — это программирование над символами самого языка. Ключевая концепция здесь заключается в том, что вычислитель — это тоже программа.
    Тип вычислителя — интерпретатор или компилятор — это ортогональная к метапрограммированию концепция, относящаяся по большей части к реализации.
    То же самое с императивностью/декларативностью. Common Lisp включает в себя как императивную парадигму, так и декларативную, в обоих смыслах этого слова. И при этом метапрограммирование может использоваться вместе с любой из них: с императивной и функциональной (т.е. декларативной в первом значении) — это оперирование символами (через quote и eval), в описательной (т.е. декларативной во втором смысле) — это CLOS.
    Метаданные — это данные о данных. То, что описывает конкретные значения. Например, названия таблиц в реляционной БД, теги записи в блоге и т.д. Метапрограммирование НЕ относится к метаданным так же, как программирование к данным. В качестве «данных» в метапрограммировании чаще всего выступают символы (хотя это уже некоторое сужение понятия метапрограммирования и более точное название в данном случае — символьное программирование). Есть, правда, пара исключений: например в Clojure любой объект может иметь метаданные, но это уже локальная терминология, которая, тем не менее, не нарушает общую идею — для обычного объекта в Clojure метаданные по сути являются просто дополнительными свойствами.
    Метамодель — это, грубо говоря, формальная система для описания некоторых концепций. Она тесно связана с базами знаний и онтологиями и, опять же, опирается на формальный язык, состоящий из символов и правил вывода.
    Облачные вычисления, протоколы связи и вообще всё, что связано с физическим местом проведения вычисления к метапрограммирования вообще никакого отношения не имеют. В каком-то конкретном случае — может быть, но в целом — ничего общего.
    • 0
      Согласен со всем кроме «Метапрограммирование НЕ относится к метаданным». Как раз самые интересные решения в метапрограммированим появляются, когда происходит интерпретация метаданных. В ближайшее время опубликую отдельные статьи с примерами и обоснованием.
      • 0
        В данном случае что именно вы подразумеваете под метаданными?
        • 0
          Метаданные это и данные, которые мы получаем из интраспекции (а это напрямую относится к метапрограммированию) и данные, которые мы получаем из модели предметной области, например, вот в примере модель декларативная и мы из нее получаем частоту, амплитуду и направление движения слоев (это тоже чистое метапрограммирование). Еще пример: храним в базе данных служебную таблицу с регулярными выражениями для валидации полей ввода или храним регулярные выражения в комментариях к полям, потом считываем их и используем при построении формы — это прямое применение метаданных в метопрограммировании.
          • +1
            Ну нет, опять не согласен. Метаданные — это по определению данные о данных. Какие данные описывают регулярные выражения? Регэкспы — это вообще часть программы, где бы вы их не хранили: хоть в коде, хоть в БД, хоть в комментариях. Вот считывание комментариев из самой программы — это да, пример метапрограммирования. Именно из самой программы, потому что, например, для компилятора комментарии, выражения, да и вообще всё AST — это всё ещё просто данные, над которыми оперирует программа-компилятор. Важно понимать, что метапрограммирование на одном уровне абстракции (для одной программы) может не являтся метапрограммированием на другом, более высоком уровне (компилятор).

            То же самое с примером: декларативная модель, описанная в HTML, подаётся на вход JS скрипту в виде данных, то есть просто в виде набора каких-то сторонних объектов, не имеющих к JS никакого отношения. Сравните с CLOS: вы также описываете модель будущего объекта декларативно, однако делаете это на самом языке и подаёте на вход макроса список символов, то есть элементов самого этого языка.

            Судя по всему, вы хотите показать что-то вроде аннотаций в Java, где сами аннотации являются метаданными по отношению к конструкциям языка, а механизмы рефлексии позволяют получить к ним доступ. Но это всё ещё частный случай, где сходятся метаданные и метапрограммирование. В целом метаданные не ограничиваются аннотациями, а метапрограммирование — рефлексией. И если программа без данных по сути не имеет смысла, то метапрограмма прекрасно может существовать и без метаданных (равно как метаданные без метапрограммы).
            • 0
              Регулярные выражения описывают тип данных для поля базы. Это метаданные о типе поля. Пока они находятся в комментариях к полю, то они хранятся как строка, а при попадании в программу они интерпретируются и начинают влиять, например: на валидацию в формах. Если бы я писал регулярные выражения как комментарий к строковой переменной так: s = "+=10"; // [-+]=?[0-9]*
              и потом бы проверял при каждом присвоении значения в s, удовлетворяет ли значение регулярному выражению и если нет, то оставлял бы старое значение. В этом случае Вы бы согласились, что это можно это считать метапрограммированием и метаданными одновременно?

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

              Вот-вот, мета-анотация в Java, пожалуй самый близкий случай. Согласен, это частный случай метапрограммирования, поэтому я и отношу его к гибриду, где пересекаются метаданные и метапрограммирование.
              • +1
                > В этом случае Вы бы согласились, что это можно это считать метапрограммированием и метаданными одновременно?

                Месье знает толк в извращениях ;)

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

                Во-первых, вы снова смешиваете разные уровни абстракции. Понятия метапрограммирования и метаданных имеют смысл только в каком-то конкретном контексте. Регулярные выражения, описывающие поле, являются метаданными только в контексте самого этого поля. В контексте базы данных (файла на диске, памяти программы, комментария самого по себе — не важно) то же самое регулярное выражение является просто данными. Это именно вопрос разных абстракций над одними и теми же (или пересекающимися) объектами реального мира.
                Во-вторых, я и не говорил, что метаданные не могут быть использованы в метапрограммировании. Но точно так же в метапрограммировании могут использоваться строки, переменные, open source и покрышка от колеса. То есть непосредственной связи между метапрограммированием и метаданными нет, и единственная общее у них — это приставка «мета».
      • 0
        Интерпретация того что вы называете «метаданными», как в вашем примере — это называется Data Driven Programming.

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

        Метапрограммирование — это программирование программ :) Когда программы создают сами себя. В вашем примере этого нет, к сожалению.
        • 0
          Понятие Data-driven programming еще не настолько устоялось, чтобы все понимали его одинаково.
          Чтобы его проиллюстрировать, обычно приводят пример, что на всход программы идет поток инструкций из заранее определенного набора, например: UP, DOWN, UP, LEFT, DOWN, RIGHT… И мы заранее не знаем, какой эта последовательность будет. Это по сути управление поведением программы с помощью потока данных. Но это же разновидность метапрограммирования.

          Что до моего маленького примера — в нем неявно используется eval, функция setInterval вызывает eval для переданной строки с вызовом. И даже в том коде, который предложил наш коллега выше, происходит нечто очень похожее. Разве это не напоминает Вам шаблоны или макросы? А если они относятся к метапрограммированию, то почему бы не относить к нему и этот подход и тот же Data-driven programming.
        • 0
          Размер и глубина цвета для картинки, как метаданные о картинке, ни чем принципиально не отличаются от частоты и направления движения слоя, как метаданные о слое.
          • +2
            принципиальное отличие:

            картинка обладает глубиной цвета независимо от того, указан он или нет — это мета данные об уже существующих данных.

            а у слоя направление движения не существует, пока не указано — это обычные данные, привязанные к слою. это просто параметры создаваемой скриптом анимации.
            метаданными они бы стали, еслибы вы саму анимацию (например, замыкание) передали в другую часть программы вместе с этими параметрами.
  • 0
    Оффтопик —
    что подразумевается под «доставкой приложения пользователю»?
  • 0
    Спасибо автору. Видно, что много работы проделано. По теме Фабрики разработки прогам, Порождающее программирование

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.