metaclass: (Default)
metaclass ([personal profile] metaclass) wrote2010-08-12 11:49 am

Опять кодогенерация, шаблонизаторы vs AST

Кодогенератор-шаблонизатор T4 оказался весьма удобной вещью, на которой кодогенераторы пишутся и расширяются просто со страшной скоростью.

И поначалу это гораздо понятнее, чем кодогенератор с использованием AST целевого языка и претти-принтингом оного AST в исходники.
Но есть одно НО.
Результат без подсветки синтаксиса и автоформатирования к поддержке практически непригоден. Исходник представляет собой смесь C# кода, который выполняется и C# кода который генерируется, с разделителями вида <#,<#=,<#+ и #>. Без заточенного под это дело редактора после определенного уровня сложности кодогенератора в этом понять ничего невозможно.

Например, я, упившись крепким чаем, в припадке безумия таки реализовал свою старую идею - генерацию из простой модели иммутабельных сущностей и методов сохранения и загрузки оных сущностей в простой XML, где полям-значениям соответствуют атрибуты, полям-объектам - элементы, полям-спискам(List<>) - элементы с вложенными списками. Т.е. вариант XML, в котором это дело наиболее читабельно глазами. Если не боитесь пауков - можете глянуть на этот мрак.
По мере расширения этого кодогенератора (простые сущности-записи -> сущности с полями-списками -> поддержка enum -> поддержка полей-вложенных объектов -> поддержка memo-полей (строки в которых длинный текст с переносами)) это все понемногу превратилось в жопный ад. 10 кб текста, 300 строк, в которых даже я с трудом ориентируюсь уже.
Использовать это очень просто, расширять дальше - уже сложно.

Кроме того, во всей этой кодогенерации очень бесит необходимость постоянно расширять систему типов. Т.к. один и тот же string в целевой проге - это может быть и коротенькое имя, и текст SQL запроса и RTF-документ. А при показе поля с датой пользователю может быть необходимо использовать culture-invariant формат (например я для служебных данных и для себя пользуюсь везде только им, чтобы не вспоминать "на какой локали я сижу") а может и формат из текущей локали.
В общем, если делать классическими дотнетовскими средствами, как положено, то на каждое поле или проперть в POCO-сущности навешивается еще 5-10 атрибутов, и по всему коду расползается анализ этих атрибутов, т.е. фактически мало того, что мы расширяем систему типов дополнительными описаниями, так еще и всю их обработку делаем в рунтайме, как с какой-то динамической типизацией.



PS: Во, вспомнил еще идею, которая мне насчет T4 в голову пришла.
Частенько в C# не хватает ADT, но их можно сымитировать, сделав базовый класс и унаследовав от него все варианты ADT с нужными полями. Чтобы сделать pattern matching поверх этого, нужно написать в базовом классе метод, в который передается в качестве параметров по одному делегату на каждый вариант ADT и он анализирует текущий тип объекта, вызывая соответствующий делегат, подставляя поля варианта в качестве параметров делегату.

Руками такое писать очевидно влом. Но можно сделать на T4 мелкий шаблончик, который из описания ADT в привычном виде сгенерит все классы, конструктора, прототипы делегатов и метод для pattern matching.

[identity profile] berezovsky.livejournal.com 2010-08-12 12:09 pm (UTC)(link)
какэто чяем

[identity profile] metaclass.livejournal.com 2010-08-12 12:10 pm (UTC)(link)
Ну ложки три заварки, литр воды и 6 кубиков сахара.
Глаза в общем-то на лоб лезут :)

[identity profile] berezovsky.livejournal.com 2010-08-12 12:15 pm (UTC)(link)
а тащемта в том же чае дохуя кофеина, например, и от него вымывается кальций из организма, например
но три ложки это както по-божески
я хуярил полкружки заварки типа принцесса нури или ещё какаято и полкружки воды, потом приходилось пить кальций, потому что начали разваливаться зубы, например
а потом перешёл на пуэр, он вроде полегче и както очень доставляет
только правильный пуэр в чорных блинах, а не рассыпное тащетам гуано от ооо кофеин, например

[identity profile] metaclass.livejournal.com 2010-08-12 12:16 pm (UTC)(link)
А где покупается пуэр в черных блинах?
Хотя вообще всякие оолонги обычно пью.

Нащот кальция это чо-то да.

[identity profile] aamonster.livejournal.com 2010-08-12 12:18 pm (UTC)(link)
В чайных магазинах (думаю, в Минске есть) задорого или по интернету в Китае за нормальные деньги (но не знаю, насколько он пострадает от доставки обычной почтой).

[identity profile] berezovsky.livejournal.com 2010-08-12 12:19 pm (UTC)(link)
а тащемта такие блины можно набыть на тех же комарах, например, или в магазине Чайный лист, например
или заказывать вагонами у одного известного китайского товарища, например
wizzard: (Default)

[personal profile] wizzard 2010-08-12 12:27 pm (UTC)(link)
>> один и тот же string в целевой проге - это может быть и коротенькое имя, и текст SQL запроса и RTF-документ

А если заворачивать в прокси-обьекты, которые для работы с внешними API умеют implicit operator string(RTFText text) { return text.value; }
?

[identity profile] paranoekk.livejournal.com 2010-08-12 12:27 pm (UTC)(link)
И что делать, кодогенерация тоже тупиковый путь?

[identity profile] metaclass.livejournal.com 2010-08-12 12:39 pm (UTC)(link)
Если делать такое вручную - дорога в ад. Мелкие объекты с одним полем, засоряют код, ничего не делая.
С кодогенератором завернуть не проблема, но в итоге это выглядит как натягивание совы на глобус, то бишь обход ограничений ООП еще большим ООП.

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

[identity profile] metaclass.livejournal.com 2010-08-12 12:43 pm (UTC)(link)
Не, не тупиковый. Главное вовремя остановится и не уйти в дебри.

Тут по самой сути задачи безумие - в одной модели хранятся описания данных и описания маппингов этих данных на другие представления. Тут это "удобный" XML и юзерочитабельное представление, а у меня ж еще предстоит генерить схему БД, маппинги в нее, желательно GUI и что там еще, бинарную сериализацию какую-нибудь. Но это все я уже делаю на F#, с AST и прочими заморочками, т.к. C# из-за отсутствия ADT не хватает для некоторых сложных случаев.

[identity profile] zelanton.livejournal.com 2010-08-12 12:54 pm (UTC)(link)
Эх, я себе давно уже такой написал и много лет уже пользую. Такими жабами оброс - мама дорогая. Например не только в XML и БД серилизация, но в ini-шки (и это древовидные полиморфные структуры).

[identity profile] metaclass.livejournal.com 2010-08-12 12:57 pm (UTC)(link)
Во-во, обрастает жабами, натурально.

[identity profile] zelanton.livejournal.com 2010-08-12 12:59 pm (UTC)(link)
ребе, я потом этих жаб подрезал аккуратненько и сейчас оно прекрасно, пользуюсь с удовольствием.
Одно плохо - всё это счастье теперь на C# переписать, оно ж у меня на делфи. Но перепишу обязательно, универсальный же инструмент получился.

[identity profile] zamotivator.livejournal.com 2010-08-12 01:03 pm (UTC)(link)
И чем это отличается от Template Haskell?

P.S. Упс, походу я этим комментом шаблоны порву

[identity profile] zamotivator.livejournal.com 2010-08-12 01:06 pm (UTC)(link)
Ну или от camlp4.
Тоже самое по сути.

Выглядит пиздато, писать заебёшься.

[identity profile] metaclass.livejournal.com 2010-08-12 01:09 pm (UTC)(link)
Попытайтесь этот T4 поюзать, весьма полезная штука, он в состав VS2008 и выше входит.
На дельфи у меня кодогенераторы попроще были, типа "сгенерить читалку CSV по заголовку" или "сгенерить обертку вокруг SQL запроса".

[identity profile] metaclass.livejournal.com 2010-08-12 01:11 pm (UTC)(link)
Ээээ, нетипизированностью?

У TH выход вроде AST как раз и тайпчекинг там на ходу как-то делается. А тут выход - текст.
Кстати, для отладки промежуточный текст очень сильно удобен, в отличие от генерации кода на ходу типа лиспов и TH.

[identity profile] metaclass.livejournal.com 2010-08-12 01:12 pm (UTC)(link)
Именно так.
Я, правда, camlp4 не смотрел, но подумываю, что неплохо было бы T4 на F# портировать :)

[identity profile] metaclass.livejournal.com 2010-08-12 01:13 pm (UTC)(link)
И, кстати, F# для кодогенераторов лучше подходит чем C# :)

[identity profile] norguhtar.livejournal.com 2010-08-12 03:12 pm (UTC)(link)
Товарищи объясните мне, зачем все это вуду таки надо?

[identity profile] metaclass.livejournal.com 2010-08-12 03:45 pm (UTC)(link)
Чтобы ничего не делать, а все работало :)

[identity profile] norguhtar.livejournal.com 2010-08-12 03:52 pm (UTC)(link)
Непокатит. Какой профит оно дает и за счет чего?

[identity profile] metaclass.livejournal.com 2010-08-12 04:18 pm (UTC)(link)
Ну размеры моделей сильно меньше чем размеры генеримого кода, кроме того, невозможно забыть сделать какой-нибудь кусок кода. Типа "добавили поле, но забыли маппинг".

[identity profile] norguhtar.livejournal.com 2010-08-12 04:21 pm (UTC)(link)
Посмотрел код. Это гм не связки ли с СУБД генерите?

[identity profile] metaclass.livejournal.com 2010-08-12 04:31 pm (UTC)(link)
Данные по ссылке пока набраны вручную, сгенерены от них только метаданные и код сохранения-загрузки этого XML. Но вообще и данные тоже будут генерироваться, только из более сложной модели.

Page 1 of 3