Re[45]: Снова о Nemerle или профанация не пройдет :)
От: VladD2 Российская Империя www.nemerle.org
Дата: 25.02.06 15:21
Оценка:
Здравствуйте, IT, Вы писали:

IT>Они это делают в рамках разработки компилятора?


Пока что в рамках мечтаний. У них то и дело на сайте и в статьях проскакивет фраза, что мол мы планируем заняться рантайм-кодогенерацией.

VD>>Обе этих возможности выходят за пределы современной реальности но через много лет они тоже будут востребованны.


IT>Ну ты блин сказанул


Статус форума обязывает.

IT> Сейчас для многих и compile-time генерация выходит за пределы современной реальности


Согласен. Но интерес к compiletime-кодогенерации уже нешуточен. Причем многие из тех кому нажна эта фича вынуждены заниматься runtime-кодгенерацией в следствии отсуствия compiletime-кодгенерации и сложностей с ее организацией.

IT>А если без смеха, то хотелось бы услышать хотя бы гепотетически реальный пример, не высосанный из пальца.


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

В общем, как я уже сказал зависимость от runtime-данных. Случай на сегодня весма редкий и в общем-то учитывая сложности появлющиеся при реализации runtime-кодогенерации обычно уступающий compiletime-решениям с качественным исполенением (а-ля Нэемерел). Но в будущем, с приходом многопроцессорной техники в каждый дом и runtime-решения могут оказаться очень даже востребованны. Хотя и тут в 99% случаев спасет installtime-кодогенерация.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[36]: Снова о Nemerle или профанация не пройдет :)
От: VladD2 Российская Империя www.nemerle.org
Дата: 25.02.06 15:21
Оценка:
Здравствуйте, Sinclair, Вы писали:

VD>>Ненужны для реализации хэш-таблиц никакие внешние модули. T-SQL — это полноценный императивный язык. Единственная его проблема — это ограничение на рекурсию в процедурах. Но это не является фатальной проблемой. На многих языках (если не ошибаюсь даже на Фортране) тоже первое время нельзя было делать рекурсию. Но писать программы это не мешало.

S>Извини, Влад, но все-таки — не вполне.

Не извиню.

S> В нем нет поддержки никаких динамических структур: нет ни массивов, ни указателей.


Указатели просто ненужны. Массивы с легкосью заменяются временными таблицами.

ЗЫ

Я не спорю, что T-SQL убог. Пригоден для написания машины Тьюринга, а значит для любой задачи. Вопрос только в эффективности и удобстве.
Вот на обычном SQL в следствии отсутсвия рекурсии, циклов и переменных писать универсальный код невозможно в принципе.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[23]: Снова о Nemerle или профанация не пройдет :)
От: vdimas Россия  
Дата: 25.02.06 21:00
Оценка:
Здравствуйте, Геннадий Васильев, Вы писали:

ГВ>В каком смыле эффективных? В смысле скорости работы? Сомневаюсь, если в конечном итоге код интерпретатора не будет компилироваться в тот же MSIL.


Мне именно такой способ и нужен — без генерации в байт-код. Последний не столь управляем. Я не смогу динамически загружать/выгружать подменять байт-код во время работы, в то время как с програмой-данными — никаких проблем. Скажем, в работающей 24х7 учетной системе мы сможем легко подменять код, исполняющий формулы при вычислении проводок. Мы сможем развивать систему не останавливая ее. Управляемость очень высока всего этого дела. Мы, например, сможем подгрузить некий код (редкого применения, для закрытия периода, скажем), выполнить его, потом выгрузить за ненадобностью.

Насчет эффективности — речь идет о некоем "клее" высокого уровня, на котором проигрыш в эффективности в 2-5 раз по сравнению с байт-кодом не существеннен. 1С успешно живет уже десяток лет, а их скрипт-машина отстает по скорости от Лиспо-подобной машины.

V>>Мне нужно именно последнее, плюс возможность использовать этот интерпретатор в дотнете (не обязательно с Лиспа, почитай ниже мой ответ eao197).


ГВ>Я почитал, но, ИМХО, это неудачное использование Lisp. Я думаю, что лучше было бы генерировать Lisp-машиной исходный текст интерпретатора, который потом скармливать тому же C# и деплоить сборку с готовым к употреблению интепретатором.


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

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


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

Вариант #1 — это использоватьбиблиотеку ядра Лиспа и наследовать свои замыкания от объектов Лиспа.
Вариант #2 — просто указывать интересующие классы и методы (у этого варианта куча опций на любой вкус, + немного аттрибуттов кому надо). Сейчас на вариант #2 генерятся делегаты на интересующие методы, и я получаю 2 лишних уровня индирекции... Когда утрясется, можно будет сделать генерацию кода-связки и обойтись практически без индирекции.

ГВ>Что до концепции ограниченного окружения, так никто же не запрещает ограничить в целевом языке набор разрешённых имён сборок/объектов/методов и т.п.


Не вопрос ограничить в языке. Я как раз хотел бы избежать такого подхода. Ограничения должны поступать из хоста. Например, в скрипт-коде ГУИ-формы мне доступен один набор библиотек и ср-в. В обработчике событий сервера — другой и т.д.

ГВ>Притом обрати внимание, если ты озабочен защитой интерпретатора и среды исполнения от несанкционированого воздействия/чтения и т.п., то всё равно придёшь к необходимости генерировать объектный код в том или ином виде.


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


В общем, у меня не стоит задача использовать Лисп и только Лисп. Мне просто нравится его механика. Она гораздо более надежная, чем механика Форта, например и VM.Net очень здорово подходит для ее реализации. Если уж упомянул о форте, то озвучу свои эксперименты 2-х годичной давности. Существует серьезное препятствие для реализации Форта на дотнете. Дело в том, что JIT-загрузчик верифицирует код, и банально не пропускает код, в котором указатель стека на выходе метода не равен указателю стека на входе. Поэтому, существующие реализации Форта вместо работы с "родным" стеком работают с его эмулятором. В итоге получилась эффективность ниже плинтуса, и такой Форт серьезно проигрывает даже Лиспу-интерпретатору.

Есть еще одна известная альтернатива. У нас есть в распоряжении JavaScript.Net. Причем, он так же умеет сохранять программу как данные и прочее и прочее. Но у JavaScript.Net есть тонна недостатков:
1. недостаточная управляемость. MS сделало сильно закрытую реализацию, реальное применение через некий хост потребует массу секса.
2. катастрофически низкая скорость "компиляции" скриптов во внутреннее представление, существенно ниже, чем скорость компиляции Лиспа или Форта
3. тормознутость во время исполнения.

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

В общем, хочется убить сразу несколько зайцев:
— иметь "чистый" скриптинг, с возможностью "на лету" подменять код, запускать скрипты без страха "замусорить" рабочий домен.
— быструю компиляцию скриптов во внутреннее представление.
— желательно иметь возможность совместимого "повторного использования" наработок в разных слоях, т.е. системный программист может писать куски системы на C#, Схеме, а бухгалтер — на некоем "дружественном" диалекте VB-подобного языка. И все это должно иметь в итоге общие "кишки" и свободно уметь вызывать друг-друга (из ядра такой системы пусть вызываются бухгалтерские формулы и наоборот. бухгалтерские скрипты могут пользовать функциональность ядра).

Реально эксперимент уже поставлен. Т.е. уже есть вся механика работы для Схемы (нет только ничего относительно макросов пока, вернее не макросов, а специальных синтаксических фомр, частным видом которой являются макросы). Зато есть простой интерпретатор арифметических выражений (в обычном синтаксисе), использующий кишки этой Схемы. Причем, перед выполнением скрипта производится начальная оптимизация и предвычисления. Дело в том, что интерпретатору Схемы доступно все то, что доступно программе (в отличие от компилятора С++, например). Поэтому понятие compile-time вычислений необыкновенно расширяется. В общем, у меня происходит сейчас тривиальная оптимизация, в ходе которой все константные значения предвычисляются и тела процедур подменяются готовыми атомами (Этот процесс итеративный, но сходящийся, разумеется). Более того, вычисляются типы предвычисленных результатов, и многие арифметические операции переводятся в типизированный вид, что делает последующие вычисления весьма эффективными. Для примера, когда закончим, выложу результаты замеров этого подхода в сравнении с кодом, написанным на C#. На кодогенерации исходников С# пришлось отказаться всвязи с переносом задачи на серверную сторону — домен быстро засоряется в процессе пользования системой, т.к. юзвери постоянно меняют и запускают формулы. Каждый новый вариант формулы — это подгрузка вновь скомпиллированной сборки... В общем, такой способ не живет по принципиальным соображениям... А учитывая наше будущее в лице Сингулярити (где вообще нельзя будет подгружать код в процесс), мне кажется, что мы сейчас двигаемся в правильном направлении.

ГВ>Да и вообще, лучше сначала с самим языком "для бухгалтеров" определиться, бо здесь можно наступить на грабли с большими, тяжелыми и остро заточенными ручками. Такие аспекты, как синтаксис, грамматика и семантика языка по-любому придётся прорабатывать — и в случае DSL, интепретирумого Lisp-ом, и в случае, описанном мной.


Ну... я думаю об этом уже примерно лет 5 Т.е. примерно представляю себе, что мне хотелось видеть. К тому же в разные годы написал тонны кода на VB/VBA/VBScript. Если этот язык немного доработать напильником, то получится примерно оно.
Если говорить о VB.Net, то он стал весьма мощным (и даже умеет кое-что недоступное из C#), но потерял всякую "дружественность" перед бедными бухгалтерами.
Re[24]: Снова о Nemerle или профанация не пройдет :)
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 25.02.06 21:27
Оценка:
Здравствуйте, vdimas, Вы писали:

V>Мне именно такой способ и нужен — без генерации в байт-код. Последний не столь управляем. Я не смогу динамически загружать/выгружать подменять байт-код во время работы, в то время как с програмой-данными — никаких проблем.


Ты же прекрасно знаешь что можешь, если используешь домены. Да, при этом получаем тормоза на пересечении его границы сложными структурами данных, но по сравнению с тормозами интерпретатора это копейки. Коме того, в случае домена, ты получаешь возможность контроллировать весь поток обмена домена с основной программой и более удобно настраивать CAS.
Если же у тебя сервер полностью stateless, то можно воспользоваться стратегией ASP.NET.

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


Можем. Сколько таких формул появится до рестарта системы? 100? 200? Для современных серверов это ничтожные копейки.

V>3. тормознутость во время исполнения.


Я так думаю этот "тормознутый" JScript.NET на порядок быстрее любого интерпретатора.
... << RSDN@Home 1.2.0 alpha rev. 642 on Windows XP 5.1.2600.131072>>
AVK Blog
Re[37]: Снова о Nemerle или профанация не пройдет :)
От: Sinclair Россия https://github.com/evilguest/
Дата: 26.02.06 05:40
Оценка:
Здравствуйте, VladD2, Вы писали:
VD>Указатели просто ненужны. Массивы с легкосью заменяются временными таблицами.

А, ну в таком варианте — конечно. Правда, в нем совершенно неясно, зачем делать какие-то хэш-таблицы — табличка уже и есть контейнер. Но в принципе да, если разрешить себе пользовать таблицы, то автоматически получается turing-completeness.
1.1.4 stable rev. 510
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[37]: Новая версия макроса
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 26.02.06 06:08
Оценка:
Здравствуйте, Oyster, Вы писали:

O>Вообще в исходной задаче надо сгенерить класс на C#, но пусть лучше на Ruby — интересно посмотреть, как у Ruby именно с метапрограммированием.


Вот
Автор: eao197
Дата: 26.02.06
(решил код на Ruby разместить в исходной теме).


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[46]: Снова о Nemerle или профанация не пройдет :)
От: Sinclair Россия https://github.com/evilguest/
Дата: 26.02.06 11:00
Оценка:
Здравствуйте, VladD2, Вы писали:
VD>Еще ты можешь иметь медленный алгоритм который можно резко ускорить захардкодив его для частных случаев. Причем количество частных случаев при каждом выполнении прилоежния не вилеко, но они заранее не известны. Имея runtime-кодогенерацию ты можешь прекомпилировать алгоритм при появлении нового варианта.
Хы-хы-хы! И этот человек месяц назад спорил против Hotspotting
1.1.4 stable rev. 510
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[47]: Снова о Nemerle или профанация не пройдет :)
От: VladD2 Российская Империя www.nemerle.org
Дата: 26.02.06 14:09
Оценка:
Здравствуйте, Sinclair, Вы писали:

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

S>Хы-хы-хы! И этот человек месяц назад спорил против Hotspotting

Hotspotting занимается не своим делом. Он решает в рантайме проблемы которые можно решать в компайлатйме. К тому же он пока что туп как пробка и не в состоянии делать анализ данных меня на базе него алгоритмы. А человек может.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[38]: Снова о Nemerle или профанация не пройдет :)
От: VladD2 Российская Империя www.nemerle.org
Дата: 26.02.06 14:09
Оценка: -1
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, VladD2, Вы писали:

VD>>Указатели просто ненужны. Массивы с легкосью заменяются временными таблицами.
S>
S>А, ну в таком варианте — конечно. Правда, в нем совершенно неясно, зачем делать какие-то хэш-таблицы — табличка уже и есть контейнер.

Бесспорно не ясно. Просто АВК захотелось вставить свои 5 копеек и он выдумал совершенно бессмысленный пример.

Кстати, о массивах... Их можно эмулировать на базе varbinary и функций типа SUBSTRING(). В свое время на Васике я еще не такое вытворял одной лишь функцией memcpy().

S> Но в принципе да, если разрешить себе пользовать таблицы, то автоматически получается turing-completeness.


Ага. Как говориться ни в чем себе не отказывай.

Я вообще не понял цель замечания АВК. Я приводил пример расширения DSL-я (SQL) императивными корструкциями чтобы позволить людям решать широкий круг задач. И делал это отвечая на вопрос "Т.е. SQL тоже неполноценный язык программирования?
". Тут влез АВК, ни чего не понимая начал заниматься "сливами".
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[25]: Снова о Nemerle или профанация не пройдет :)
От: vdimas Россия  
Дата: 26.02.06 15:06
Оценка:
Здравствуйте, AndrewVK, Вы писали:

V>>Мне именно такой способ и нужен — без генерации в байт-код. Последний не столь управляем. Я не смогу динамически загружать/выгружать подменять байт-код во время работы, в то время как с програмой-данными — никаких проблем.


AVK>Ты же прекрасно знаешь что можешь, если используешь домены.


Теоретически могу, практически — нет. Вот задача, если предложишь решение — будет тебе большой thanks:
— в памяти кешируется большое число данных, собственно сервак для того и нужен, чтобы зачитать в память и затем производить вычисления над огромными массивами данных (под гиг и более)
— мы предоставляем возможность "играть" с формулами, т.е. юзверь системы составляет формулы, а затем тут же натравливает их на данные, и так далее постоянно, суть именно в этом.

Вопрос — каким образом я смогу натравить байт-код формул из одного домена на данные из другого, не потеряв при этом бастродействие в 10-ки раз.

Сейчас это все работает на уровне десктопного приложения. В памяти размещаются данные, потом происходит "игра" с формулами. В процессе игры мы динамически компилируем и подгружаем формулы. Через несколько часов работы можно просто перезапустить десктопное приложение. А что делать с серверным 24х7?

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


Это зависит от объема данных и скорости интерпретатора. Лисп-интерпретатор НЕ ОБРАБАТЫВАЕТ исходный текст, он работает по внутреннему представлению (как и Форт), и работает очень быстро.

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


Ты можешь себе представить сериализацию одного гига данных? Каждый раз мы производим вычисления над ВСЕМ объемом.

AVK>Если же у тебя сервер полностью stateless, то можно воспользоваться стратегией ASP.NET.


Полностью stateless, потому как полностью readonly

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

AVK>Можем. Сколько таких формул появится до рестарта системы? 100? 200? Для современных серверов это ничтожные копейки.


В конкретном случае о котором идет речь — десятки тысяч вариантов формул до рестарта. Разумеется, подменить сотню формул м/у рестартами — это не означает засорить домен. А я говорил именно о засорении.

V>>3. тормознутость во время исполнения.


AVK>Я так думаю этот "тормознутый" JScript.NET на порядок быстрее любого интерпретатора.


Ну, поэкспериментируй с его eval — увидишь кто реально быстрее на порядок... и нам нужно именно такое применение — без компиляции в байт-код.
Re[34]: Макрос заместо TypeAccessorBuilder
От: Oyster Украина https://github.com/devoyster
Дата: 26.02.06 17:49
Оценка: 67 (2)
Здравствуйте, IT, Вы писали:

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

Прежде всего, кода получилось меньше и он получился более интуитивным, чем код, используюший emit. Это, в общем-то, неудивительно, поскольку практически везде для описания генерируемых классов/методов/полей используются quoted declaration, т.е. фактически код на Nemerle в <[ ]>.

Это всё, конечно, говорит о мощности языка, но для кодинга "прямо сейчас" Nemerle, возможно, не очень подходит. Перечислю то, что мне не понравилось больше всего:


Остаётся надеяться, что всё это только возрастные проблемы и что они пройдут к релизу какой-нибудь версии — всё-таки язык ещё очень молод.


Теперь собственно макрос. Использование его выглядит следующим образом:

using Nemerle.Utility;
using BLToolkit.Nemerle;
using BLToolkit.Reflection;

public class MyFactory : IObjectFactory
{
  public CreateInstance(_ : TypeAccessor, _ : InitContext) : object implements IObjectFactory.CreateInstance
  {
    null
  }
}

[TypeAccessorHolder]
[ObjectFactory(typeof(MyFactory))]
public class MyBO
{
  [Accessor]
    _name : string = "Hello there!";
    
    mutable public HeyHey : int = 1;
}

_ = MyBO().GetTypeAccessor()

Перед тем, как привести код самого макроса, хочу заметить, что в текущем состоянии он не заработает из-за присутствия этого замечательного бага: http://nemerle.org/bugs/view.php?id=626. Для того, чтобы макрос заработал, надо или скачать пофиксаную версию компилятора (когда исправят баг), или закомментировать регион "CreateInstance()" в самом макросе, или закомментировать (переименовать) следующие методы в классе TypeAccessor:

public static T CreateInstance<T>();
public static T CreateInstance<T>(InitContext context);

Проблема в том, что из-за бага Nemerle не может override методы CreateInstance() из-за того, что в классе присутствуют статические generic-методы с таким же именем и сигнатурой.

Для компиляции макроса и примера необходимо подключить сборку BLToolkit.2.dll (что, в общем-то, очевидно).

А вот и код макроса:

using System;
using System.Reflection;
using Nemerle;
using Nemerle.Collections;
using Nemerle.Compiler;
using BLToolkit.Reflection;

namespace BLToolkit.Nemerle
{
  // BO will automatically implement this interface using macro TypeAccessorHolder
  public interface ITypeAccessorHolder
  {
    // Retrieve singleton TypeAccessor instance for given class
    GetTypeAccessor() : TypeAccessor;
  }
  
  
  // We must add interface to BO on this stage
  [MacroUsage(MacroPhase.BeforeInheritance, MacroTargets.Class, Inherited = true)]
  macro TypeAccessorHolder(t : TypeBuilder)
  {
        t.AddImplementedInterface(<[ ITypeAccessorHolder ]>);
  }
  
  // Here code for TypeAccessor is actually generated
  [MacroUsage(MacroPhase.WithTypedMembers, MacroTargets.Class, Inherited = true)]
  macro TypeAccessorHolder(t : TypeBuilder)
  {
    // Some oftenly used values
    def tname = t.ParsedTypeName;
    
        // Will hold TypeAccessorImpl constructor code
    mutable nestedCtorCode = [ <[ base(); ]> ];
    
    #region Accessor$
    
    // Function which generates Accessor$ class for all members in collection
    def genMemberAccessor['a](members : list['a], getMemberParam, genGetter, genSetter) where 'a: IMember {
      // Function which returns field/property accessing AST
      def useMember(m) { <[ (o :> $(tname)).$(m.Name : usesite) ]> };
      
      foreach (m in members) {
        def mname = m.Name;
        def accTypeName = Macros.UseSiteSymbol("Accessor$" + mname);
        
        // Define Accessor$ class with constructor
        def accType = t.DefineNestedType(<[ decl:
          class $(accTypeName : name) : MemberAccessor
          {
            public this(ta : TypeAccessor, mi : MemberInfo)
            {
              base(ta, mi)
            }
          } ]>);
        
        // Add code into TypeAccessorImpl ctor which will create Accessor$
        nestedCtorCode ::= <[ AddMember($(accTypeName : name)(this, GetMember($(getMemberParam : int), $(mname : string)))); ]>;
        
        // Add getter/setter methods
        when (genGetter(m)) {
          accType.Define(<[ decl: override public GetValue(o : object) : object { $(useMember(m)) } ]>);
          accType.Define(<[ decl: override public HasGetter : bool { get { true } } ]>);
        }
        
        when (genSetter(m)) {
          accType.Define(<[ decl:
            override public SetValue(o : object, v : object) : void
            {
              $(useMember(m)) = v :> $(Macros.TypedChoose(m.GetMemType()))
            } ]>);
          accType.Define(<[ decl: override public HasSetter : bool { get { true } } ]>);
        }
        
        accType.Compile();
      }
    };
    
    // Generate accessors
    genMemberAccessor(
      t.GetFields(BindingFlags.Public | BindingFlags.Instance),
      1,
      fun(_) { true },
      fun(m) { m.IsMutable });
    genMemberAccessor(
      List.Filter(t.GetProperties(BindingFlags.Public | BindingFlags.Instance), fun(m) { !m.IsIndexer }),
      2,
      fun(m) { m.GetGetter() != null },
      fun(m) { m.IsMutable });
    
    #endregion Accessor$
    
    #region IObjectFactory
    
    // Add factory initialization code if according attribute is present
    when (t.HasAttribute(Macros.GetIfIsType(t.GlobalEnv, <[ ObjectFactoryAttribute ]>).Value)) {
      nestedCtorCode ::= <[ ObjectFactory = (TypeHelper.GetFirstAttribute(typeof($tname), typeof(ObjectFactoryAttribute)) :> ObjectFactoryAttribute).ObjectFactory ]>
    }
    
    #endregion IObjectFactory
    
    // Define nested TypeAccessor implementation
    def nested = t.DefineNestedType(<[ decl:
      class TypeAccessorImpl : TypeAccessor
      {
        public this() { ..$(nestedCtorCode.Reverse()) }
      } ]>);
    
    #region CreateInstance()
    
    // Determine if BO class contains constructors in required form
    mutable defCtor = null;
    mutable initCtor = null;
    foreach (ctor in t.GetConstructors(BindingFlags.Public | BindingFlags.Instance)) {
      match (ctor.GetParameters()) {
        | [] => defCtor = ctor
        | [ param ] => when (param.SystemType.Equals(typeof(InitContext))) initCtor = ctor
      }
    }
    
    // Check obtained constructors
    when (defCtor == null && initCtor == null) {
      Message.FatalError($"The $t type must have public default or init constructor.")
    }
    
    // Declare CreateInstance() override
    def ctorParams = if (defCtor != null) [] else [ <[ null ]>] ;
    nested.Define(<[ decl: override public CreateInstance() : object { $(t.ParsedTypeName)( .. $ctorParams ) } ]>);
    
    // Declare CreateInstance(InitContext) override
    def ctorParams = if (initCtor != null) [ <[ ctx ]> ] else [];
    nested.Define(<[ decl: override public CreateInstance(ctx : InitContext) : object { $(t.ParsedTypeName)( .. $ctorParams ) } ]>);
    
    #endregion CreateInstance()
    
    #region Type/OriginalType
    
    // In this macro Type == OriginalType for me
    def defineTypeProp(n) { nested.Define(<[ decl: override public $(n : usesite) : Type { get { typeof($(t.ParsedTypeName)) } } ]>); };
    defineTypeProp("Type");
    defineTypeProp("OriginalType");
    
    #endregion Type/OriginalType
    
    // TypeAccessor implementor is ready - compile it
    nested.Compile();
    
    // Define static field which holds TypeAccessor implementation instance and method which returns it
    t.Define(<[ decl: static _typeAccessor : TypeAccessor = TypeAccessorImpl(); ]>);
    t.Define(<[ decl:
      public GetTypeAccessor() : TypeAccessor implements ITypeAccessorHolder.GetTypeAccessor
      {
        _typeAccessor
      } ]>)
  }
}

На всякий случай привожу ещё код, сгенерированный макросом на основании примера (регион "CreateInstance()" был закомментирован; код получен с помощью Reflector.FileDisassembler):

using BLToolkit.Nemerle;
using BLToolkit.Reflection;
using Nemerle.Internal;
using System;
using System.Reflection;

[ObjectFactory(typeof(MyFactory)), Macro("BLToolkit.Nemerle.TypeAccessorHolder:type:postscan", 0, "")]
public class MyBO : ITypeAccessorHolder
{
    static MyBO()
    {
        MyBO._typeAccessor = new TypeAccessorImpl();
    }

    public MyBO()
    {
        this._name = "Hello there!";
        this.HeyHey = 1;
    }

    public sealed override TypeAccessor GetTypeAccessor()
    {
        return MyBO._typeAccessor;
    }


    public string Name
    {
        get
        {
            return this._name;
        }
    }


    [Immutable]
    private string _name;
    [Immutable]
    private static TypeAccessor _typeAccessor;
    public int HeyHey;


    private class Accessor$HeyHey : MemberAccessor
    {
        public Accessor$HeyHey(TypeAccessor ta, MemberInfo mi) : base(ta, mi)
        {
        }

        public override object GetValue(object o)
        {
            return ((MyBO) o).HeyHey;
        }

        public override void SetValue(object o, object v)
        {
            ((MyBO) o).HeyHey = (int) v;
        }


        public override bool HasGetter
        {
            get
            {
                return true;
            }
        }

        public override bool HasSetter
        {
            get
            {
                return true;
            }
        }

    }

    private class Accessor$Name : MemberAccessor
    {
        public Accessor$Name(TypeAccessor ta, MemberInfo mi) : base(ta, mi)
        {
        }

        public override object GetValue(object o)
        {
            return ((MyBO) o).Name;
        }


        public override bool HasGetter
        {
            get
            {
                return true;
            }
        }

    }

    private class TypeAccessorImpl : TypeAccessor
    {
        public TypeAccessorImpl()
        {
            base.AddMember(new MyBO.Accessor$HeyHey(this, base.GetMember(1, "HeyHey")));
            base.AddMember(new MyBO.Accessor$Name(this, base.GetMember(2, "Name")));
            this.ObjectFactory = ((ObjectFactoryAttribute) TypeHelper.GetFirstAttribute(typeof(MyBO), typeof(ObjectFactoryAttribute))).ObjectFactory;
        }


        public override System.Type OriginalType
        {
            get
            {
                return typeof(MyBO);
            }
        }

        public override System.Type Type
        {
            get
            {
                return typeof(MyBO);
            }
        }

    }
}
Re[30]: Снова о Nemerle или профанация не пройдет :)
От: Andir Россия
Дата: 26.02.06 23:22
Оценка:
Здравствуйте, Воронков Василий, Вы писали:

ВВ>Например, SharpDevelop и отпочковавшийся от него в свое время MonoDeveloper, позицируемый как среда под юникс-системы.


Откуда это взято? Я вроде как слежу за развитием монодевелопера, но ни разу не слышал о том, что он отпочковался от SharpDevelop ...

C Уважением, Andir!
... << RSDN@Home 1.2.0 alpha rev. 643>>
Re[26]: Снова о Nemerle или профанация не пройдет :)
От: VladD2 Российская Империя www.nemerle.org
Дата: 26.02.06 23:39
Оценка:
Здравствуйте, vdimas, Вы писали:

V>Теоретически могу, практически — нет. Вот задача, если предложишь решение — будет тебе большой thanks:

V>- в памяти кешируется большое число данных, собственно сервак для того и нужен, чтобы зачитать в память и затем производить вычисления над огромными массивами данных (под гиг и более)
V>- мы предоставляем возможность "играть" с формулами, т.е. юзверь системы составляет формулы, а затем тут же натравливает их на данные, и так далее постоянно, суть именно в этом.

V>Вопрос — каким образом я смогу натравить байт-код формул из одного домена на данные из другого, не потеряв при этом бастродействие в 10-ки раз.


Я бы сделал так.
Если данных очень много, а код изменяется редко, то:
1. Работу с данными осуществлял в том же домене куда грузится код.
2. После перекомпиляции кода старый не выгружал бы.
3. Создал бы теневой поток который бы отслеживал сколько ресурсов уходит на неиспользуемые сборки. Если много, то перезагружал бы домен.

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

В общем, решение сильно зависит от того насколько быстрыми должны быть формулы. Интерпретация минимум в 10 раз медленеее компиляции. Так что если формулы участвуют в интенсивных вычислениях, то и думать не очем. С другой стороны если сами формулы вызываются не часто, но оперируют высокоуровневыми абстракциями (например, приводят к запросам к БД), то можно обойтись интерпретацией, так как при этом затраты на нее будут минимальными.

Короче, профайлер и здесь может помочь.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[35]: Макрос заместо TypeAccessorBuilder
От: VladD2 Российская Империя www.nemerle.org
Дата: 26.02.06 23:39
Оценка:
Здравствуйте, Oyster, Вы писали:

1. Не плохо было бы объяснить суть генерируемого кода. Это не очевидно.
2. Нужно все же переносить код. Он же у тебя на экнан не влезает.
3. Хорошо бы следовать правилам форматирования RSDN. Это упрощает восприятие.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[35]: Макрос заместо TypeAccessorBuilder
От: IT Россия linq2db.com
Дата: 27.02.06 00:33
Оценка:
Здравствуйте, Oyster, Вы писали:

ITypeAccessorHolder или другой механизм по добычи TypeAccessor из типа нужно переносить в BLToolkit. Тогда это всё будет доступно стандартным средствам библиотеки, а следовательно и её пользователям. Т.е. можно будет делать то, о чём я говорил — использовать Nemerle для задач генерации, а другие части приложения делать на других языках.
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
Re[36]: Макрос заместо TypeAccessorBuilder
От: IT Россия linq2db.com
Дата: 27.02.06 00:44
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>1. Не плохо было бы объяснить суть генерируемого кода. Это не очевидно.


Можно я? Спасибо.

В случае необходимости, для какого-либо типа BLToolkit генерирует и создаёт экземпляр наследника от TypeAccessor. Этот объект предназначен для создания экземпляров изсходного типа и доступа к его членам в обход Reflection. Т.е. в TypeAccessor'е генерируется пара методов CreateInstance, а для каждого члена исходного типа наследник MemberAccessor, который содержит сгенерированные методы GetValue, SetValue и ещё некоторые служебные методы. Далее это используется для маппинга, валидации, баиндинга и прочих вещей, которые требуют динамическое создание и доспут к членам какого-либо объекта.
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
Re[36]: Макрос заместо TypeAccessorBuilder
От: Oyster Украина https://github.com/devoyster
Дата: 27.02.06 07:25
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>2. Нужно все же переносить код. Он же у тебя на экнан не влезает.


Ок

VD>3. Хорошо бы следовать правилам форматирования RSDN. Это упрощает восприятие.


В смысле правилам форматирования кода? Я, в общем-то, старался использовать правила форматирования кода на Nemerle от создателей Не всегда получалось, правда...
Re[36]: Макрос заместо TypeAccessorBuilder
От: Oyster Украина https://github.com/devoyster
Дата: 27.02.06 07:25
Оценка:
Здравствуйте, IT, Вы писали:

IT>ITypeAccessorHolder или другой механизм по добычи TypeAccessor из типа нужно переносить в BLToolkit. Тогда это всё будет доступно стандартным средствам библиотеки, а следовательно и её пользователям. Т.е. можно будет делать то, о чём я говорил — использовать Nemerle для задач генерации, а другие части приложения делать на других языках.


Ну да. Я, правда, всё-таки рассматривал этот макрос как не очень рабочий код — первый блин всегда комом. Да и использовать его нельзя до тех пор, пока баг не пофиксают, т.е. сейчас он фактически нерабочий.

Кстати, а в BLToolkit SVN небось не все имеют право записи?
Re[26]: Снова о Nemerle или профанация не пройдет :)
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 27.02.06 09:14
Оценка:
Здравствуйте, vdimas, Вы писали:

V>Вопрос — каким образом я смогу натравить байт-код формул из одного домена на данные из другого, не потеряв при этом бастродействие в 10-ки раз.


Суммарное? Да ничего специального не делать. В десятки раз замедляется собственно вызов, а не все. Остается еще время работы самого алгоритма. Если ты хочешь там затюнить все насмерть, то достаточно не мучаться танцами с бубном вокруг выгрузки, а просто изредка рециклить основной домен вместе с сервером, возможно сохраняя твой драгоценный кеш. Но вобще то лучше оцени потери памяти на твои формулы за любой разумный промежуток времени, а потом сравни со своим многогигабайтным кешем.
И вобще смешно слышать разговоры о быстродействии после предложения использовать интерпретатор и рассказов об 1С.

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


V>Это зависит от объема данных и скорости интерпретатора. Лисп-интерпретатор НЕ ОБРАБАТЫВАЕТ исходный текст, он работает по внутреннему представлению (как и Форт), и работает очень быстро.


Может ты не в курсе, но даже бейсик БК-0010 в замшелые времена тоже работал подобным образом. Только это не спасает интерпретатор от чудовищной разности в производительности по сравнению с компилятором. На ее фоне кроссдоменные потери не заметны под микроскопом.

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


V>Ты можешь себе представить сериализацию одного гига данных?


Да. А еще я представляю сколько времени с этим гигом будет ковыряться любой интерпретатор.

AVK>>Можем. Сколько таких формул появится до рестарта системы? 100? 200? Для современных серверов это ничтожные копейки.


V>В конкретном случае о котором идет речь — десятки тысяч вариантов формул до рестарта.


Мощные у тебя юзеры, набить десятки тысяч формул, я таких не встречал. В любом случае это все равно копейки.
... << RSDN@Home 1.2.0 alpha rev. 642>>
AVK Blog
Re[27]: Снова о Nemerle или профанация не пройдет :)
От: vdimas Россия  
Дата: 27.02.06 09:31
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>В общем, решение сильно зависит от того насколько быстрыми должны быть формулы. Интерпретация минимум в 10 раз медленеее компиляции.


Да, чистая интерпретация всяко медленнее компиляции. Однако, представим себе, что многие операторы и ф-ии уже вшиты в систему. Реально проигрыш примерно в 2-3 раза у нас (субъективно, точно пока не замеряли).

Схема вычислений примерно такая (лишнее выкинуто, посмотри и прикинь, действительно ли происходит такой провал в производительности):

    public interface IExpression<T> {
        T Value { get; }
    }
        
    public struct Constant<T> : IExpression<T> {
        T _value;

        public Constant(T value) { _value = value; }

        public T Value { get { return _value; } }
    }
        
    public abstract class UnaryFunction<RetT, ArgT, ExpT> : Function<RetT>
                    where ExpT : IExpression<ArgT> {

        ExpT _exp;

        protected UnaryFunction(ExpT exp) { _exp = exp; }

        public ExpT Exp { get { return _exp; } }
    }

    public abstract class BinaryFunction<RetT, ArgT1, ExpT1, ArgT2, ExpT2>
        : Function<RetT>
        where ExpT1 : IExpression<ArgT1>
        where ExpT2 : IExpression<ArgT2> 
    {
        ExpT1 _exp1;
        ExpT2 _exp2;

        protected BinaryFunction(ExpT1 exp1, ExpT2 exp2) {
            _exp1 = exp1;
            _exp2 = exp2;
        }

        public ExpT1 Exp1 { get { return _exp1; } }
        public ExpT2 Exp2 { get { return _exp2; } }
    }


А теперь пара "вшитых" ф-ий:
    public sealed class BinaryPlus_Int32<ExpT1, ExpT2>
        : BinaryFunction<int, int, ExpT1, int, ExpT2>
        where ExpT1 : IExpression<int>
        where ExpT2 : IExpression<int> 
    {
        public BinaryPlus_Int32(ExpT1 arg1, ExpT2 arg2) : base(arg1, arg2) { }

        public override int Value {
            get { return Exp1.Value + Exp2.Value; }
        }
    };

    public sealed class Factorial<ExpT> 
        : UnaryFunction<int, int, ExpT>
        where ExpT : IExpression<int> {

        public Factorial(ExpT arg) : base(arg) {}

        public override int Value {
            get { 
                int prod = 1;
                for (int i = 2; i <= Exp.Value; i++)
                    prod *= i;

                return prod;
            }
        }
    };


Наша формула — это "дерево" таких вот вшитых ф-ий. По-сути, формула задает "архитектуру", то бишь параметризированный тип этой прикольной взаимной сцепки.

Обрати внимание на sealed в классах и override возле Value, ИМХО они должны нивелировать друг-друга. Когда это происходит мы ВООБЩЕ НИЧЕГО НЕ ТЕРЯЕМ в плане производительности. И тем не менее программа продолжает оставаться... данными, а не кодом.

Вот на такую простейшую формулу: ((Filed1)! + Field2) * min(Field3, Field4)
Парсер должен создать в памяти экземпляр примерно такого типа:
BinaryProd_Int32<
        BinaryPlus_Int32<
                Factorial<GetterField1>, 
                GetterField2
        >, 
        BinaryMin_Int32<
                GetterField3, 
                GetterField4
        > 
> formula;



Далее, это еще не все, я тут говорил об оптимизации. Наша оптимизация предвычисляет константные выражения. Причем, сам парсер выражений этим не занимается (!!!). Выражения сами себя предвычисляют и при этом МЕНЯЮТ свою структуру и тип.

Вот кусок более полного варианта:

    public interface IExpression<T> {
        bool IsConstant { get; }
        T Value { get; }
        IExpression<T> Optimize();
    }
        
    public abstract class UnaryFunction<RetT, ArgT, ExpT> : Function<RetT>
                    where ExpT : IExpression<ArgT> {

                ...
                
        public override IExpression<RetT> Optimize() {
            if (IsConstant)
                return new Constant<RetT>(Value);

            return this;
        }
                
        public override bool IsConstant {
            get { return _exp.IsConstant; }
        }
    }


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


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


Нет, формулы вызываются часто, за каждый раз над миллионом записей в оперативной памяти, тем не менее мы решили попробовать построить эффективный интерпретатор.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.