Дмитpий Hecтepук

Блог о программировании — C#, F#, C++, архитектура, и многое другое

Posts Tagged ‘dsl

Кодогенерация на практике

7 комментариев

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

У меня например на практике очень хорошо «срослось» с кодогенерацией. Настолько хорошо, что думаю стоило бы рассказать эту историю.

Как все начиналось

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

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

Вместо этого, я решил сделать по-другому, и написал custom tool для Visual Studio (тогда еще 2005 версии), который просто-напросто применял к файлам XML трансформации XSLT (используя возможности .NET которые, в плане XML, весьма убоги) и добавлял сгенерированные файлы в проект.

Эта тула, которую я даже попробовал распространить между другими участниками проекта, использовалась мной (а в последствии, когда я «ушел в менеджмент», моим коллегой) для описания пользовательского интерфейса в формате XML (ну почти как XAML, не правда ли?) и последующей генерации C# файлов (в т.ч. метода InitializeComponent()) для создания собственно интерфейса.

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

  • Сущности
  • Хранимые процедуры для базы данных
  • Пользовательский интерфейс (WPF)

Примерно в то же время я модифицировал свой custom tool для того, чтобы он создавал несколько файлов одновременно и использовал вместо .NET’ного движка Altova XML и язык XQuery соответственно. Язык этот был выбран потому, что в то время Microsoft оправдывали отсутсвие поддержки XSLT 2.0 в .NET идеей о том что «XQuery все равно лучше» и что будем поддерживать его. Сейчас 2012 год, а воз и ныне там.

Проблемы восприятия

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

Впрочем, лично меня это не смутило и с тех времен я постоянно генерю код то тут то там. Например, реализация мнемоников для ReSharper (и не только — они рано или поздно у меня будут для всей линейки IDEA) — это чистой воды кодогенерация, которая просто создает в XML 600+ шаблонов, которые разворачиваются в различные программные конструкты.

Со временем у меня появилось немного измененное восприятие кодогенерации: вместо того чтобы просто генерировать ORMы для баз данных (привет LLBLGen!), я понял что кодогенерация может быть использована для доменных трансформаций, то есть для генерации готового к компилированию кода из какого-то другого представления — представления, которое является «родным» для той или иной предметной области.

Проект MathSharp: генерация кода из математической нотации

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

Посудите сами: есть у вас например формула вроде

image

а вам нужно конвертировать ее в код вроде

p = rho*R*T + (B_0*R*T-A_0-((C_0) / (T*T))+((E_0) / (Math.Pow(T, 4))))*rho*rho +
    (b*R*T-a-((d) / (T)))*Math.Pow(rho, 3) +
    alpha*(a+((d) / (t)))*Math.Pow(rho, 6) +
    ((c*Math.Pow(rho, 3)) / (T*T))*(1+gamma*rho*rho)*Math.Exp(-gamma*rho*rho);

А теперь представьте что у вас есть сотни формул…

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

  1. Берем формулу в Word и копируем ее в формате MathML. Это такой XML-образный формат как раз для формул.
  2. Парсим MathML и создаем в F# дерево как набор алгебраических типов. F# используем потому что pattern matching+algebraic data types намного легче анализировать.
  3. По созданному дереву генерим код.

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

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

Проект X2C: генерация кода из таблиц Excel

Excel — это двигатель прогресса. Когда кому-то нужно что-то подсчитать, то они вряд ли сразу открывают Matlab или Mathematica: если вычисления простые, то их можно вписать в Excel’евскую табличку и сразу получить как расчеты так и красивое форматирование.

Генерация кода из Excel — задачка посложнее. Ведь сначала нужно понять что генерировать, и для этого я ввел концепцию мэппингов. С помощью них клетки в таблице могут «мэпиться» на переменную, функцию, сущность (т.е. класс), вектор или матрицу. Соответственно все формулы которые используются в Excel нужно поддерживать в том или ином языке программирования. На текущий момент, я использую:

  • C++ и библиотеку Boost.Math
  • C# и (коммерческую) библиотеку NMath

Сам проект называется X2C и реализован в виде плагина для Excel (спасибо Студии и ее интуитивности при разработке VSTO решений) который предоставляет Ribbon UI для разметки клеток и кодогенерации. Этот проект тоже писался быстро т.к. большинство сложных компонентов (например, парсер для формул) взяты со стороны (спасибо компаниям за MVP лицензии!). Мне лишь осталась только задача кодогенерации которая, в данном случае, весьма непростая но по крайней мере понятная. Кстати, если интересно как выглядит вся «кухня», у меня есть скринкаст который как раз все это иллюстрирует.

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

Генерация пользовательского интерфейса из Excel

Давайте начистоту: визуальный дизайнер того же WPF — ни ахти насколько полезный. Я даже не помню как он выглядит т.к. никогда им толком не пользовался. Мораль в том, что большинству разработчиков проще писать руками XAML и потом только эпизодически смотреть на то, получился ли нужный UI или нет.

Задумался я об этом, и тут меня осенило. Ведь если взять скажем Metro-style UI (да-да, я буду использовать слово Metro, мне так удобней), то это фактически большой набор прямоугольников. А знаете что еще набор прямоугольников? Правильно — таблицы в Excel.

И тут вывод напросился сам собой — зачем мэпить только данные, если можно еще с тем же успехом мэпить простые элементы пользовательского интерфейса? Такой подход фактически убивает двух зайцев, а именно:

  • Реализация UI упрощается до уровня Excel. Понятное дело что это устроит не всех, но большинство LoB приложений — это как раз лейблы, текст-боксы и кнопки.
  • Можно автоматически привязать данные и UI, без лишних телодвижений.

Вывод тут один: Excel можно использовать для разработки приложений, в которых используются в основном вычисления и простые контролы. При этом data binding а также проблемы отсутствующих значений (data binding контролов на поля типа Nullable<T> и сопутствующая головная боль) также разрулены на уровне генератора.

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

CPS и формализация Control Flow

Continutation Passing Style (CPS) — это тривиальная, как в танке, практика в которой сложный алгоритм бъется на несколько частей которые вызываются последовательно, и каждая часть возвращает результат вызова следующей. То есть алгоритм из 3-х частей это функция A() которая возвращает результат вызова B(), и так далее. При этом предпочтительно не иметь общего состояния для A() и B(), а вместо этого передавать в функцию только нужные на момент вызова состояния.

Не все конечно любят CPS, но я его успешно использую в тех случаях, когда алгоритм по своему размеру переходит все допустимые рамки. Я также пользуюсь очень «красочными» названиями для составных методов, а также стараюсь применять separation of concerns, так что каждый метод выполняет одну, понятную задачу.

Почему я завел разговор про CPS? Потому что, фактически, поток выполнения программы в этому случае является, по сути… отдельной, маленькой но предметной таки областью. И формальное опередение этой области — «конечный автомат». Соответственно, вместо того чтобы просто включать механизм создания конечных автоматов в программу как код (пойди разберись как там API Stateless работает), в очередной раз можно прибегнуть к подходу с отдельным редактором. Который может выглядеть например вот так:

image

Пример выше содержит 5 методов, один из которых даже вызывается асинхронно. На практике же, алгоритм описаный в подобном стиле может содержать любое количество методов, но при этом оставаться понятным на концептуальном уровне. Сгенерированный же код уже может быть сполне себе непонятным — например в этой статье я генерирую код на основе механизма Pulse & Wait а это, согласитесь, не самая интуитивная конструкция в .NET-е.

Что следующее?

Вообще все эти разработки наводят на мысли что возможно я копаю в правильном направлении, но не копаю достаточно глубоко. Например, я прекрасно могу в математической нотации выразить подсчет Sharpe ratio но когда дело касается, например, формулы Блэка-Шоулза, то внезапно оказывается что для подсчета кумулятивного нормального распределения N(x), не существует аналитического решения а вместо этого существует формула для приблизительных расчетов. И соответственно парадигма автоматической конверсии рассыпается как карточный домик.

Думаю что основной задачей на данный момент должно стать создание «этически правильного» workflow редактора — некого аналога Workflow Foundation который вместо того чтобы пытаться убить сто зайцев сразу решал бы хотя бы основные задачи, такие как например создание из модели конечного автомата набора заглушек. Мне кажется что это не так сложно, другое дело что в отличии от прямолинейных конверсий, непонятно что добавлять а что нет.

У меня на сегодня все; сорри что стал постить так редко, но на то были причины — например, весь Октябрь я проездил по России с докладами про ReSharper. В плане докладов кстати планы тоже изменились — из за вышеперечесленных программ, меня теперь все повсюду зовут, так что плюсы в карму и плюшки в казну. До новых встреч!

Written by Dmitri

4 ноября 2012 at 0:57

Опубликовано в code generation

Tagged with , ,