Партия, дай порулить
От: IT Россия linq2db.com
Дата: 07.02.06 02:33
Оценка:
Навеяно Nemerle рулит! :)
Автор: VladD2
Дата: 02.02.06


Интересный получился топик и довольно познавательный. Сразу видно, что за дело профессионально взялись розовые береты
В связи с этим у меня к вам, господа нимерлисты (или как вас теперь ), имеется вопрос.

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

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

Суть вопроса. Как я уже сказал выше, меня интересуют прежде всего возможности языка по генерации кода. Вот что я могу сегодня делать на C# с помощью подручных средств. Возьмём, например, такой класс:

public abstract class Test : EditableObject
{
  public abstract int    ID   { get; set; }
  public abstract string Name { get; set; }
}

Этого объявления вполне достаточно, чтобы run-time генератор BLToolkit создал на основании его примерно вот такой код:

namespace EditableObjects.EditableObjectTest.BLToolkitExtension
{
    using BLToolkit.EditableObjects;
    using BLToolkit.Reflection;
    using BLToolkit.TypeBuilder;
    using EditableObjects;
    using System;
    using System.Collections;
    using System.Reflection;

    public class Test : EditableObjectTest.Test, IEditable, IMemberwiseEditable, IPrintDebugState
    {
        // Fields
        private EditableValue<int>    _ID;
        private EditableValue<string> _name;
                
        private static PropertyInfo _ID_$propertyInfo;
        private static PropertyInfo _name_$propertyInfo;

        // Methods
        static Test()
        {
            _ID_$propertyInfo   = TypeHelper.GetPropertyInfo(typeof(EditableObjectTest.Test), "ID",   typeof(int),    Type.EmptyTypes);
            _name_$propertyInfo = TypeHelper.GetPropertyInfo(typeof(EditableObjectTest.Test), "Name", typeof(string), Type.EmptyTypes);
        }

        public Test()
        {
            this._name = new EditableValue<string>("");
        }

        public Test(InitContext)
        {
            this._name = new EditableValue<string>("");
        }

        public override int ID
        {
            get { return this._ID.Value; }
            set
            {
                this._ID.Value = value;
                ((IPropertyChanged)this).OnPropertyChanged(_ID_$propertyInfo);
            }
        }

        public override string Name
        {
            get { return this._name.get_Value(); }
            set
            {
                this._name.set_Value((string) value);
                ((IPropertyChanged)this).OnPropertyChanged(_name_$propertyInfo);
            }
        }

        void IEditable.AcceptChanges()
        {
            this._ID.AcceptChanges();
            this._name.AcceptChanges();
        }

        bool IEditable.IsDirty
        {
            get
            {    
                bool flag1 = this._ID.IsDirty;
                        
                if (!flag1)
                    flag1 = this._name.IsDirty;

                return flag1;
            }
        }

        void IEditable.RejectChanges()
        {
            this._ID.RejectChanges();
            this._name.RejectChanges();
        }

        bool IMemberwiseEditable.AcceptMemberChanges(PropertyInfo propertyInfo, string memberName)
        {
            bool flag1 = this._ID.AcceptMemberChanges(_ID_$propertyInfo, memberName);

            if (!flag1)
                flag1 = this._name.AcceptMemberChanges(_name_$propertyInfo, memberName);

            return flag1;
        }

        void IMemberwiseEditable.GetDirtyMembers(PropertyInfo propertyInfo, ArrayList list)
        {
            this._ID.  GetDirtyMembers(_ID_$propertyInfo,   list);
            this._name.GetDirtyMembers(_name_$propertyInfo, list);
        }

        bool IMemberwiseEditable.IsDirtyMember(PropertyInfo propertyInfo, string memberName, ref bool isDirty)
        {
            bool flag1 = this._ID.IsDirtyMember(_ID_$propertyInfo, memberName, ref isDirty);

            if (!flag1)
                flag1 = this._name.IsDirtyMember(_name_$propertyInfo, memberName, ref isDirty);

            return flag1;
        }

        bool IMemberwiseEditable.RejectMemberChanges(PropertyInfo propertyInfo, string memberName)
        {
            bool flag1 = this._ID.RejectMemberChanges(_ID_$propertyInfo, memberName);

            if (!flag1)
                flag1 = this._name.RejectMemberChanges(_name_$propertyInfo, memberName);

            return flag1;
        }

        void IPrintDebugState.PrintDebugState(PropertyInfo propertyInfo, ref string str)
        {
            this._ID.  PrintDebugState(_ID_$propertyInfo,   ref str);
            this._name.PrintDebugState(_name_$propertyInfo, ref str);
        }
    }
}

Т.е. мы имеем следующие вещи:

1. Внутренную реализацию свойств как EditableValue<int> и EditableValue<string>.
2. Реализацию интерфейса IEditable, включая методы AcceptChanges, RejectChanges и флаг IsDirty.
3. Генерацию события OnPropertyChanged при изменении свойств.
4. Ещё кое-что по мелочи.

Естественно, всё это дело не захардкожено. Генератор берёт информацию о том что и где генерировать из атрибутов, которыми по уши обвешан базовый класс EditableObject. В результате мы имеем почти 100% декларацию и, как результат, минимум работы.

Вопрос. Можно ли достичь подобного уровня декларативности на Nemerle?

Если это принципиально возможно, то не исключено, что в BLToolkit в скором времени может появится соответствующая поддержка
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
Re: Партия, дай порулить
От: Сергей Туленцев Россия http://software.tulentsev.com
Дата: 07.02.06 09:09
Оценка:
Здравствуйте, IT, Вы писали:

IT>Естественно, всё это дело не захардкожено. Генератор берёт информацию о том что и где генерировать из атрибутов, которыми по уши обвешан базовый класс EditableObject. В результате мы имеем почти 100% декларацию и, как результат, минимум работы.


IT>Вопрос. Можно ли достичь подобного уровня декларативности на Nemerle?


IT>Если это принципиально возможно, то не исключено, что в BLToolkit в скором времени может появится соответствующая поддержка

Принципиально возможно. В стандартной поставке идет пример макроса, который создает прокси методы. Например, так:

public interface IMath
{
    Add(...) : void;
    Mul(...) : void;
}

public class Math : IMath
{

}

public class SomeClass
{
    [DesignPatters.Proxy(IMath)]
    private _math : Math;

}

public module MathApp()
{
   public Main() : void
   {
       def m = SomeClass();
       
       Console.WriteLine(m.Add(5, 6));
    }
}

Писал по памяти, мог напутать, но идея, надеюсь, понятна.

Ссылки по теме

Macros tutorial
Defining types from inside macros
Design patterns
--
Re[2]: Партия, дай порулить
От: IT Россия linq2db.com
Дата: 07.02.06 12:55
Оценка:
Здравствуйте, Сергей Туленцев, Вы писали:

СТ>Принципиально возможно. В стандартной поставке идет пример макроса, который создает прокси методы. Например, так:


Прокси я посмотрел. Коряво это всё как-то
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
Re[2]: Партия, дай порулить
От: IT Россия linq2db.com
Дата: 07.02.06 13:16
Оценка: +1
Здравствуйте, Сергей Туленцев, Вы писали:

Может какой-нибудь новый модификатор для класса можно забульбенить?
В общем, надо разбираться как у них компилятор генераторы подхватывает. Их MacroUsage для атрибутов явно не достаточно.
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
Re[3]: Партия, дай порулить
От: Сергей Туленцев Россия http://software.tulentsev.com
Дата: 07.02.06 13:24
Оценка:
Здравствуйте, IT, Вы писали:

IT>Здравствуйте, Сергей Туленцев, Вы писали:


СТ>>Принципиально возможно. В стандартной поставке идет пример макроса, который создает прокси методы. Например, так:


IT>Прокси я посмотрел. Коряво это всё как-то


В смысле, коряво?
А как тебе атрибут Singletone? Практически вообще ничего не надо писать.
В ближайшие пару дней попробую набросать код для генерации наследников (от абстрактных классов) с какой-то функциональностью.
--
Re[3]: Партия, дай порулить
От: Сергей Туленцев Россия http://software.tulentsev.com
Дата: 07.02.06 13:28
Оценка:
Здравствуйте, IT, Вы писали:

IT>Здравствуйте, Сергей Туленцев, Вы писали:


IT>Может какой-нибудь новый модификатор для класса можно забульбенить?

IT>В общем, надо разбираться как у них компилятор генераторы подхватывает. Их MacroUsage для атрибутов явно не достаточно.

Акхм. Не уверен в своей правильной интерпретации выражения "компилятор генераторы подхватывает".
Я так понимаю, ты хочешь узнать, насколько легко можно будет повторить функциональность BLTookit на
немерловских макросах? И чем тебя не устроил MacroUsage?
--
Re[4]: Партия, дай порулить
От: IT Россия linq2db.com
Дата: 07.02.06 13:35
Оценка:
Здравствуйте, Сергей Туленцев, Вы писали:

СТ>Акхм. Не уверен в своей правильной интерпретации выражения "компилятор генераторы подхватывает".


Я имею ввиду, в каких точках он вызывает макросы. С явным их вызовом и с вариантом на атрибутах всё понятно. Можно ли как-то указать Nemerle, что макрос надо применять ко всем наследникам какого-либо класса.

СТ>Я так понимаю, ты хочешь узнать, насколько легко можно будет повторить функциональность BLTookit на немерловских макросах? И чем тебя не устроил MacroUsage?


Надо писать две лишних строчки в каждом классе, вместо одного названия базового класса. Две лишних строчки очень легко забыть написать.
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
Re: Партия, дай порулить
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 07.02.06 13:45
Оценка:
Здравствуйте, IT, Вы писали:

IT>Суть вопроса. Как я уже сказал выше, меня интересуют прежде всего возможности языка по генерации кода. Вот что я могу сегодня делать на C# с помощью подручных средств.


А что понимается под подручными средствами (извини, я не совсем в курсе BLToolkit)?


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[5]: Партия, дай порулить
От: Сергей Туленцев Россия http://software.tulentsev.com
Дата: 07.02.06 13:57
Оценка: 22 (1)
Здравствуйте, IT, Вы писали:

IT>Здравствуйте, Сергей Туленцев, Вы писали:


СТ>>Акхм. Не уверен в своей правильной интерпретации выражения "компилятор генераторы подхватывает".


IT>Я имею ввиду, в каких точках он вызывает макросы. С явным их вызовом и с вариантом на атрибутах всё понятно. Можно ли как-то указать Nemerle, что макрос надо применять ко всем наследникам какого-либо класса.


Пока еще нет. Но я подкинул авторам такую идею. Работа уже идет.


СТ>>Я так понимаю, ты хочешь узнать, насколько легко можно будет повторить функциональность BLTookit на немерловских макросах? И чем тебя не устроил MacroUsage?


IT>Надо писать две лишних строчки в каждом классе, вместо одного названия базового класса. Две лишних строчки очень легко забыть написать.


Это да, Согласен
--
Re[2]: Партия, дай порулить
От: IT Россия linq2db.com
Дата: 07.02.06 14:20
Оценка:
Здравствуйте, eao197, Вы писали:

IT>>Суть вопроса. Как я уже сказал выше, меня интересуют прежде всего возможности языка по генерации кода. Вот что я могу сегодня делать на C# с помощью подручных средств.


E>А что понимается под подручными средствами (извини, я не совсем в курсе BLToolkit)?


BLToolkit и понимается. Вот ещё вариант генерации кода — Генерация вызова сохранённых процедур. PersonAccessor — класс, полностью генерируемый библиотекой в рантайм. Содержимое методов генерируется на основе их сигнатуры.
Если нам не помогут, то мы тоже никого не пощадим.
Re[3]: Партия, дай порулить
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 07.02.06 14:30
Оценка:
Здравствуйте, IT, Вы писали:

IT>BLToolkit и понимается. Вот ещё вариант генерации кода — Генерация вызова сохранённых процедур. PersonAccessor — класс, полностью генерируемый библиотекой в рантайм. Содержимое методов генерируется на основе их сигнатуры.


Я про то, чем ты сигнатуры получаешь? Через Reflection? Если так, то Nemerle -- это же .NET, там должно быть доступно все то, что доступно для C#. Может быть те же инструменты подойдут и для Nemerle?


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[6]: Партия, дай порулить
От: IT Россия linq2db.com
Дата: 07.02.06 14:30
Оценка:
Здравствуйте, Сергей Туленцев, Вы писали:

IT>>Я имею ввиду, в каких точках он вызывает макросы. С явным их вызовом и с вариантом на атрибутах всё понятно. Можно ли как-то указать Nemerle, что макрос надо применять ко всем наследникам какого-либо класса.


СТ>Пока еще нет. Но я подкинул авторам такую идею. Работа уже идет.


Вау, круто Может им ещё каких идей наподкидывать?
Например, для обхода торомозов рефлекшин, полезна возможность генерации класса аксессора для указанного класса. Т.е. нужно сгенерировать совершенно отдельный набор классов не трогая исходный.
Если нам не помогут, то мы тоже никого не пощадим.
Re[7]: Партия, дай порулить
От: IT Россия linq2db.com
Дата: 07.02.06 14:32
Оценка:
Здравствуйте, IT, Вы писали:

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


Впрочем, это можно спокойно и атрибутами сделать. Либо даже лучше отдельный класс. Или вот. Можно ли в макросе просканировать все типы, которые есть в компилируемой сборке?
Если нам не помогут, то мы тоже никого не пощадим.
Re[7]: Партия, дай порулить
От: Сергей Туленцев Россия http://software.tulentsev.com
Дата: 07.02.06 14:34
Оценка:
Здравствуйте, IT, Вы писали:

IT>Здравствуйте, Сергей Туленцев, Вы писали:


IT>>>Я имею ввиду, в каких точках он вызывает макросы. С явным их вызовом и с вариантом на атрибутах всё понятно. Можно ли как-то указать Nemerle, что макрос надо применять ко всем наследникам какого-либо класса.


СТ>>Пока еще нет. Но я подкинул авторам такую идею. Работа уже идет.


IT>Вау, круто Может им ещё каких идей наподкидывать?

Ну, например? Чуваки вполне вменяемые.

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


И опять я тебя не понимаю.
Я за rfd давно не следил, поэтому от терминологии отвык.

Что касаемо тормозов рефлекшона (при генерации наследников), то эти затраты перенесутся на этап компиляции.
--
Re[7]: Партия, дай порулить
От: IT Россия linq2db.com
Дата: 07.02.06 14:38
Оценка:
Здравствуйте, IT, Вы писали:

IT>Вау, круто Может им ещё каких идей наподкидывать?


И ещё. Может это конечно уже реализовано.

В генерации класса может участвовать несколько макросов. В том числе для генерации одного и того же метода. В качестве примера можно взять аспекты. Их можно применять пачками как к одному методу, так и к параметрам. При этом возможно, что между ними придётся каким-то образом передавать некоторые параметры. В принципе, одной глобальной внутренней хешь таблицы на все такие вещи было бы достаточно.
Если нам не помогут, то мы тоже никого не пощадим.
Re[8]: Партия, дай порулить
От: IT Россия linq2db.com
Дата: 07.02.06 14:42
Оценка:
Здравствуйте, Сергей Туленцев, Вы писали:

IT>>Вау, круто Может им ещё каких идей наподкидывать?

СТ>Ну, например? Чуваки вполне вменяемые.

Это хорошо, у меня тут фантазия уже разыгралась
С ними как-то можно пообщаться?

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


СТ>И опять я тебя не понимаю.


Я тоже пока не очень
Если нам не помогут, то мы тоже никого не пощадим.
Re[4]: Партия, дай порулить
От: IT Россия linq2db.com
Дата: 07.02.06 14:46
Оценка:
Здравствуйте, eao197, Вы писали:

E>Я про то, чем ты сигнатуры получаешь? Через Reflection? Если так, то Nemerle -- это же .NET, там должно быть доступно все то, что доступно для C#. Может быть те же инструменты подойдут и для Nemerle?


Это как раз не проблема. Единственное что интересно, как ребята сделали доступ к метаданным классов компилируемой сборки. Рефлекшина то никакого ещё нет, нужен полностью свой API.

Меня сейчас интересует где и когда будут вызываться макросы и как будут разруливаться конфликты.
Если нам не помогут, то мы тоже никого не пощадим.
Re[8]: Партия, дай порулить
От: Сергей Туленцев Россия http://software.tulentsev.com
Дата: 07.02.06 14:49
Оценка:
Здравствуйте, IT, Вы писали:

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


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


IT>Впрочем, это можно спокойно и атрибутами сделать. Либо даже лучше отдельный класс. Или вот. Можно ли в макросе просканировать все типы, которые есть в компилируемой сборке?

Да, можно конечно. Над ними еще всякие штуки можно вытворять. Например, менять дерево наследования.
--
Re[9]: Партия, дай порулить
От: Сергей Туленцев Россия http://software.tulentsev.com
Дата: 07.02.06 14:54
Оценка:
Здравствуйте, IT, Вы писали:

IT>Здравствуйте, Сергей Туленцев, Вы писали:


IT>>>Вау, круто Может им ещё каких идей наподкидывать?

СТ>>Ну, например? Чуваки вполне вменяемые.

IT>Это хорошо, у меня тут фантазия уже разыгралась

IT>С ними как-то можно пообщаться?

Ага
--
Re[5]: Партия, дай порулить
От: Сергей Туленцев Россия http://software.tulentsev.com
Дата: 07.02.06 14:56
Оценка:
Здравствуйте, IT, Вы писали:

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


E>>Я про то, чем ты сигнатуры получаешь? Через Reflection? Если так, то Nemerle -- это же .NET, там должно быть доступно все то, что доступно для C#. Может быть те же инструменты подойдут и для Nemerle?


IT>Это как раз не проблема. Единственное что интересно, как ребята сделали доступ к метаданным классов компилируемой сборки. Рефлекшина то никакого ещё нет, нужен полностью свой API.


IT>Меня сейчас интересует где и когда будут вызываться макросы и как будут разруливаться конфликты.

Можно использовать компилятор, как библиотеку. В частности, один чувак делает автокомплишен по такой методике.

Насчет "где и когда" — есть три точки выполнения макросов. Процитирую,,,

5.1.1 Stages of class hierarchy building
Analysing object-oriented hierarchy and class members is a separate pass of
the compilation (as mentioned in section 3.1). First, it creates inheritance
relation between classes, so we know exactly all base types of given type.
After that, every member inside of them (methods, fields, etc.) is being
analysed and added to the hierarchy. After that also the rules regarding
implemented interface methods are checked.

For the needs of macros we have decided to distinguish three moments
in this pass at which they can operate on elements of class hierarchy. Every
macro can be annotated with a stage, at which it should be executed.

• BeforeInheritance stage is performed after parsing whole program and
scanning declared types, but before building sub-typing relation between them. It gives macro a freedom to change inheritance hierarchy
and operate on parse-tree of classes and members
• BeforeTypedMembers is when inheritance of types is already set. Macros can still operate on bare parse-trees, but utilize information about
subtyping.
• WithTypedMembers stage is after headers of methods, fields are already analysed and in bound state. Macros can easily traverse entire
class space and reflect type constructors of fields, method parameters,
etc. Original parse-trees are no longer available and signatures of class
members cannot be changed.
Later on we refer to these stages when describing class level macros.

--
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.