Открыть список
Как стать автором
Обновить

Метапрограммирование

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

UPD: Более новая статья: Метапрограммирование с примерами на JavaScript. И лекция по статье на Youtube (лекция записана в Киевском политехническом институте 18 апреля 2019 года в рамках курса «100 видео-лекций по программированию»):



Итак, попробую дать альтернативное определение: метапрограммирование — это парадигма построения кода информационной системы с динамическим изменением поведения или структуры в зависимости от данных, действий пользователя или взаимодействия с другими системами. Задачи метапрограммирования: повышение абстракции кода и его гибкости, повторное использование, ускорение разработки, упрощение межсистемной интеграции. На самом деле, все мы в той или иной мере используем метапрограммирование, я даже сейчас могу вспомнить, как использовал его 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>
Теги:метапрограммированиеметаданныеметамодельинтраспекцияинтерпретациякомпиляцияeval
Хабы: Программирование
Всего голосов 43: ↑33 и ↓10 +23
Просмотры31.5K

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

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

Похожие публикации

Программист C/C++ (протоколы мобильной связи 4G/5G)
до 150 000 ₽ПроВайд ЛабсНовосибирскМожно удаленно
Senior Blockchain Developer
от 150 000 до 200 000 ₽Relictum ProМожно удаленно
Reverse Engineer
от 3 500 до 4 000 $Hand2NoteМожно удаленно

Лучшие публикации за сутки