анонимные классы, proof of concept
От: Vermicious Knid  
Дата: 26.03.07 14:23
Оценка: 35 (4)
Очень далекий от идеала вариант, не вся необходимая функциональность реализована, есть несколько шероховатых моментов. На правах чернового наброска(код без комментариев и довольно неопрятный).

Для начала код теста:
using System.Console;
using Nemerle.Extensions;

[Record] public class Foobar[T]
{
    public mutable n : T;
    public override ToString() : string { $"Foobar($n)" }
    public override GetHashCode() : int { n.GetHashCode() }
}

def foo = new { Foo = 1; Bar = "foobar" + 2.ToString(); Baz =  Foobar(22) };
def foo2 = new { Foo = 1; Bar = "foobar2"; Baz = Foobar(22) };
def bar = new { It = 1; Is = 2.0; Crazy = new { Really = 1; Crazy = "2"; Stuff = 3.0 } };

def dump(k, v : object)
{
    WriteLine($"$(k.PadRight(40, ' ')) : $v")
}
dump("foo", foo);
dump("foo.GetHashCode() == foo2.GetHashCode()",foo.GetHashCode()==foo2.GetHashCode());
dump("foo.GetType() == foo2.GetType()", foo.GetType().Equals(foo2.GetType()));
dump("bar.__Fields", bar.__Fields);
dump("bar.__Values", bar.__Values);
dump("bar.Crazy.__Fields", bar.Crazy.__Fields);
WriteLine();

def test_stuff(x : Anonymous[int*string*Foobar[int]])
{
    WriteLine($"calling 'test_stuff' on $x");
    match(x)
    {
        | (__Fields = [a,b,c], __Values = (x,y,z)) =>
            WriteLine($"$a = $x");
            WriteLine($"$b = $y");
            WriteLine($"$c = $z");
        | _ => ()
    }
}
test_stuff(foo)


Вот это выводится в консоль при его выполнении:
foo                                      : [Foo, Bar, Baz] = (1, foobar2, Foobar(22))
foo.GetHashCode() == foo2.GetHashCode()  : True
foo.GetType() == foo2.GetType()          : True
bar.__Fields                             : [It, Is, Crazy]
bar.__Values                             : (1, 2, [Really, Crazy, Stuff] = (1, 2, 3))
bar.Crazy.__Fields                       : [Really, Crazy, Stuff]

calling 'test_stuff' on [Foo, Bar, Baz] = (1, foobar2, Foobar(22))
Foo = 1
Bar = foobar2
Baz = Foobar(22)


Теперь сам код макроса. Предупреждаю сразу, что у меня не самая новая версия компилятора, но значительно новее последнего публичного релиза. Возможно будут проблемы. Используется indentation based syntax, поэтому возможно лучше(во избежание проблем с компиляцией) скачать в виде файла(ссылка на архив в начале сообщения).
#pragma indent
using System
using Nemerle.Compiler
using Nemerle.Collections
using Nemerle.Utility

set namespace Nemerle.Extensions
    
public abstract class Anonymous[T]
    public abstract __Fields : list[string] { get; }
    public abstract __Values : T { get; }

    public override GetHashCode() : int
        __Values.GetHashCode() ^ __Fields.GetHashCode()

    public override ToString() : string
        $"$__Fields = $__Values"

module StringFilterExt
    public Filter(this str : string, pred : char->bool) : string
        def sb = Text.StringBuilder(str.Length)
        foreach(c when pred(c) in str)
            _ = sb.Append(c)
        sb.ToString()

macro CreateAnonymousClass(body) syntax("new", body)
    def typer = Macros.ImplicitCTX()

    def exprs =
        match(body)
            | <[{.. $exprs }]> => exprs
            | _ => Message.FatalError(body.Location, $"expected new { Name1 = expr1, ... }; got $body")

    def exprs = exprs.Map(
        _ => {
            | <[ $(field : name) = $value ]> => (field, value)
            | expr => Message.FatalError(expr.Location, $"expected Name = expr1, got $expr")})
    
    def (fields, values) = List.Split(exprs)

    def values = <[ (.. $values) ]> // build a tuple expression
    def tvalues = typer.TypeExpr(values)

    def buildAnonymousClass(fail_loud)
        match (tvalues.Type.Hint)
            | Some(Tuple(args) as t) =>
                def t = t.DeepFix()


                def members = List.Combine(fields, args).Map(
                    (field, typ) =>
                        <[ decl: [Accessor(flags=WantSetter)] mutable $(field.NewName("_" + field.Id) : name) : $(typ : typed)]>)

                def fields_strings = fields.Map(f => <[ $(f.Id : string) ]>)
                def members = <[ decl: public override __Fields : list[string] { get { [.. $fields_strings] } } ]> :: members


                def fields_access_exprs = fields.Map(f => <[ $(f : name) ]>)
                def members = <[ decl: public override __Values : $(t : typed) { get { (.. $fields_access_exprs) }  } ]> :: members

                def mangled_args = t.ToString().Filter(c => Char.IsLetterOrDigit(c) || c == '_')
                def mangled_name = $"Anonymous_$(mangled_args)_$(t.ToString().GetHashCode())"

                unless(typer.Env.LookupType([mangled_name]) is Some(_))
                    def anonyclass =
                        <[ decl: [Record] public class $(mangled_name : usesite) : Anonymous[$(t : typed)] { .. $members }]>
                    def tbuilder = typer.Env.Define(anonyclass)
                    unless(typer.InErrorMode) tbuilder.Compile()

                Some(<[$(mangled_name : usesite)($values)]>)

            | _ =>
                when(fail_loud)
                    Message.Error(body.Location, "compiler was unable to analyze types of expressions inside anonymous type")
                None()
                    
    typer.DelayMacro(buildAnonymousClass)


P.S. Единственная замеченная серьезная проблема с этим кодом — он не совсем правильно работает с отложенным выводом типов, хотя я изначально использовал DelayMacro и надеялся, что этого будет достаточно. Оказалось недостаточно — если тип полей анонимного класса на момент выполнения макроса известен не полностью, то вместо неизвестных type variables он подставляет System.Object, что конечно неприятно. Единственный выход это наверное откладывать выполнение макроса до тех пор пока все типы полей анонимного класса не станут известны. Как это сделать надо еще подумать или спросить у Москаля/Скальски.
Re: анонимные классы, proof of concept
От: ie Россия http://ziez.blogspot.com/
Дата: 26.03.07 15:00
Оценка:
Здравствуйте, Vermicious Knid, Вы писали:

VK>Очень далекий от идеала вариант, не вся необходимая функциональность реализована, есть несколько шероховатых моментов. На правах чернового наброска(код без комментариев и довольно неопрятный).




А есть какие-нибудь идеи, как это дело развернуть на многосборочные проекты?
... << RSDN@Home 1.2.0 alpha rev. 655>>
Превратим окружающую нас среду в воскресенье.
Re: анонимные классы, proof of concept; версия 3
От: Vermicious Knid  
Дата: 27.03.07 01:07
Оценка: 42 (3)
Вторую версию я даже выкладывать сюда боюсь, кому интересно ее можно найти на багтрекере Nemerle(хотя она в принципе работает ). Проблема предыдущего подхода в том, что даже если предусмотреть поддержку отложенного вывода типов, то все равно в самых сложных случаях типы выводиться не будут, так как для этого о существовании свойств анонимного класса Nemerle должно быть известно, а тип их еще должен быть неопределен. А неопределен тип может быть только у локальных переменных, ну и конечно у параметров обобщенных типов(aka дженерики).

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

Пример использования макроса:
using Nemerle.Extensions;
def x = new { Foo = []; Bar = System.Collections.Generic.Dictionary() };
x.Foo = [1,2,3];
x.Bar.Add("crazy", "stuff");
System.Console.WriteLine(x);

В данном примере сгенерируется анонимный класс с "названием" Anonymous_2_FooBar_-2095137078[T0, T1]. Переменная x будет иметь тип Anonymous_2_FooBar_blabla[list[int],Dictionary[string,string]. Все анонимные классы наследуются от абстрактного класса Anonymous[T](его можно посмотреть в коде макроса), в качестве параметра используется тип кортежа всех полей, или если поле единственное — тип этого самого поля. Абстрактный класс Anonymous можно использовать для передачи анонимного типа во внешний код итд итп.

Код анонимного класса из рефлектора:
public class Anonymous_2_FooBar_-2095137078<T0, T1> : Anonymous<Tuple<T0, T1>>
{
      // Methods
      public Anonymous_2_FooBar_-2095137078(T0 _Foo, T1 _Bar);

      // Properties
      public override list<string> __Fields { get; }
      public override Tuple<T0, T1> __Values { get; }
      public T1 Bar { get; set; }
      public T0 Foo { get; set; }

      // Fields
      private T1 _Bar;
      private T0 _Foo;
}

Вот сам макрос. На этот раз я пожалел людей, которые привыкли к фигурным скобкам, но в целом код не менее ядреный(хотя и концептуально проще), так как он состоит в основном из кусков разного старого кода. Мне пока только важно, чтобы он работал.
using System;
using Nemerle.Compiler;
using Nemerle.Collections;
using Nemerle.Utility;
using PT = Nemerle.Compiler.Parsetree;

namespace Nemerle.Extensions
{    
    public abstract class Anonymous[T]
    {
        public abstract __Fields : list[string] { get; }
        public abstract __Values : T { get; }

        public override GetHashCode() : int
        {
            __Values.GetHashCode() ^ __Fields.GetHashCode()
        }

        public override ToString() : string
        {
            $"{ ($(__Fields.ToString(\", \"))) : $__Values }"
        }
    }

    module StringFilterExt
    {
        public Filter(this str : string, pred : char->bool) : string
        {
            def sb = Text.StringBuilder(str.Length);
            foreach(c when pred(c) in str)
                _ = sb.Append(c);
            sb.ToString()
        }
    }

macro CreateAnonymousClass(body) syntax("new", body)
{
    def typer = Macros.ImplicitCTX();

    def exprs =
        match(body)
        {
            | <[{.. $exprs }]> => exprs
            | _ =>
                Message.FatalError(body.Location, $"expected new { Name1 = expr1, ... }; got $body")
        }

    def exprs = exprs.Map(
        _ => {
            | <[ $(field : name) = $value ]> => (field, value)
            | expr =>
                Message.FatalError(expr.Location, $"expected Name = expr1, got $expr")});
    
    def (fields, values) = List.Split(exprs);

    def one_or_tuple(exprs)
    {
        | [expr] => expr
        | exprs => <[ (.. $exprs) ]>
    }

    def values = one_or_tuple(values);

    def types = $[$"T$i" | i in [0..fields.Length-1]].Map(id => <[ $(id : usesite) ]>);

    def members = List.Combine(fields, types).Map(
        (field, typ) =>
            <[ decl: 
                [Accessor(flags=WantSetter)]
                mutable $(field.NewName("_" + field.Id) : name) : $typ
            ]>);

    def fields_strings = fields.Map(f => <[ $(f.Id : string) ]>);
    def members = 
        <[ decl: public override __Fields : list[string]
                {
                    get
                    {
                        [.. $fields_strings]
                    }
                }
        ]> :: members;

    def tuple_type = match(types)
    {
        | [typ] => <[ $typ ]>
        | types => <[ @*(.. $types) ]>
    }

    def fields_access_exprs = fields.Map(f => <[ $(f : name) ]>);
    def members = 
        <[ decl: 
            public override __Values : $tuple_type
            { 
                get
                {
                    $(one_or_tuple(fields_access_exprs))
                }
            }
        ]> :: members;

    def mangled_args = fields.ToString()
        .Filter(c => Char.IsLetterOrDigit(c) || c == '_');

    def mangled_name = $"Anonymous_$(fields.Length)_$(mangled_args)_"
        + fields.ToString().GetHashCode().ToString();

    unless(typer.Env.LookupType([mangled_name]) is Some(_))
    {
        // TODO: eliminate this ugly hack, dunno even why it works
        // make type names for splicing as type parameters
        def type_parms = types.Map( n => 
                            { | <[$(n : name)]> => PT.Splicable.Name(n) });

        def anonyclass = 
            <[ decl:
                [Record] public class $(mangled_name : usesite)[.. $type_parms]
                    : Anonymous[$tuple_type]
                {
                    .. $members
                }
            ]>;
        def tbuilder = typer.Env.Define(anonyclass);
        unless(typer.InErrorMode) tbuilder.Compile();
    }

    <[$(mangled_name : usesite)($values)]>
}

}
Re[2]: анонимные классы, proof of concept
От: Vermicious Knid  
Дата: 27.03.07 01:34
Оценка:
Здравствуйте, ie, Вы писали:

ie>А есть какие-нибудь идеи, как это дело развернуть на многосборочные проекты?

Вообще-то первоначальная задумка была такой — типы сгенерированных анонимных классов локальны для сборки. Но у них есть супертип Anonymous. Он определен в библиотеке, которая используется во всех сборках(может быть даже стандартная библиотека), где используются анонимные типы. Его можно использовать для передачи анонимного типа куда угодно и он содержит ряд вещей, которые позволяют работать с анонимными типами как с обычными кортежами + можно придумать кучу разных полезных утилит.

Но можно придумать что-то и более интересное. В этом proof of concept пока генерируются публичные типы в глобальном пространстве и он их кэширует(то есть имя создается такое, чтобы для совпадающих имен и типов полей генерировались одинаковые имена, и проверяется нет ли уже такого класса — если есть, то он использует его). Если сборки в момент компиляции знают друг о друге(каждая новая сборка подключает все предыдущие), то по идее дубликатов создаваться не должно. Проблемы будут когда несколько сборок, некоторые из которых используют идентичные анонимные типы, будут компилираться независимо друг от друга. И без поддержки со стороны фрэймворка эти проблемы очень сложноразрешимы. Поэтому проще спрятать генерируемые анонимные типы и кэшировать их только на уровне одной сборки.
Re[3]: анонимные классы, proof of concept
От: ie Россия http://ziez.blogspot.com/
Дата: 27.03.07 03:59
Оценка:
Здравствуйте, Vermicious Knid, Вы писали:

ie>>А есть какие-нибудь идеи, как это дело развернуть на многосборочные проекты?

VK>Вообще-то первоначальная задумка была такой — типы сгенерированных анонимных классов локальны для сборки. Но у них есть супертип Anonymous. Он определен в библиотеке, которая используется во всех сборках(может быть даже стандартная библиотека), где используются анонимные типы. Его можно использовать для передачи анонимного типа куда угодно и он содержит ряд вещей, которые позволяют работать с анонимными типами как с обычными кортежами + можно придумать кучу разных полезных утилит.

+1

VK>Но можно придумать что-то и более интересное. В этом proof of concept пока генерируются публичные типы в глобальном пространстве и он их кэширует(то есть имя создается такое, чтобы для совпадающих имен и типов полей генерировались одинаковые имена, и проверяется нет ли уже такого класса — если есть, то он использует его). Если сборки в момент компиляции знают друг о друге(каждая новая сборка подключает все предыдущие), то по идее дубликатов создаваться не должно.


Да, эта ситуация разруливается на ура.

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


У меня вот такая идея появилась, а что если генерить идентичным анонимным типам операторы имплисит приведения, как это делается для всяких int, char, etc. Конечно, тут нужна поддержка со стороны компилятора, да и тормозов добавит, но все же проблему это может решить.
... << RSDN@Home 1.2.0 alpha rev. 0>>
Превратим окружающую нас среду в воскресенье.
Re: анонимные классы, proof of concept; версия 4
От: Vermicious Knid  
Дата: 27.03.07 15:07
Оценка: 137 (9)
В этой версии наконец реализована вся функциональность анонимных типов, которую я нашел в спецификации C# 3.0 (если я что-то пропустил — дайте знать). Плюс несколько дополнительных возможностей. Уже есть ряд вещей, в которых они превосходят шарповские анонимные типы. Осталось только добавить несколько утилитарных фич и шарповские анонимные типы будут казаться жалким подобием того, что реализует этот макрос.

Код теста, демонстрирующий синтаксис:
using Nemerle.Extensions;

def x = new { Foo = []; Bar = System.Collections.Generic.Dictionary() };
x.Foo = [1,2,3];
x.Bar.Add("crazy", "stuff");

def y = new { x.Foo; x.Bar };

def Foo = y.Foo;
def Bar = y.Bar;
def z = new { Foo; Bar };

def xyz = new { FooBar = x; x.Foo; Bar; Baz : int }

// make sure x, y and z all have the same type
assert(x.GetType().Equals(y.GetType()) && y.GetType().Equals(z.GetType()));

def xfoo = new { Foo : int; Bar : string };
xfoo.Foo = 42;
xfoo.Bar = "abc";

assert(!xfoo.GetType().Equals(x.GetType()));

def tuple = (43, "cde");
def yfoo = new tuple : { Foo; Bar };
def zfoo = new (44, "cde") : { Foo; Bar };

assert(xfoo.GetType().Equals(yfoo.GetType()) && yfoo.GetType().Equals(zfoo.GetType()));

Кроме того синтаксиса, который предусмотрен в C# 3.0, здесь есть еще поля вида Identifier : type и выражение преобразования кортежа в анонимный тип( new sometuple : { Foo; Bar} равнозначно new { Foo = sometuple[0]; Bar = sometuple[1] } ).

using System;
using Nemerle.Compiler;
using Nemerle.Collections;
using Nemerle.Utility;
using PT = Nemerle.Compiler.Parsetree;

namespace Nemerle.Extensions
{    
    public abstract class Anonymous[T]
    {
        public abstract __Fields : list[string] { get; }
        public abstract __Values : T { get; set; }

        public override GetHashCode() : int
        {
            __Values.GetHashCode() ^ __Fields.GetHashCode()
        }

        public override ToString() : string
        {
            $"{ ($(__Fields.ToString(\", \"))) : $__Values }"
        }
    }

    module StringFilterExt
    {
        public Filter(this str : string, pred : char->bool) : string
        {
            def sb = Text.StringBuilder(str.Length);
            foreach(c when pred(c) in str)
                _ = sb.Append(c);
            sb.ToString()
        }
    }

macro CreateAnonymousClass(body) syntax("new", body)
{
    def one_or_tuple(exprs) { | [expr] => expr | exprs => <[ (.. $exprs) ]> }
    def extractFieldNamesAndValues(_expr)
    {
        | <[ { .. $exprs } ]> =>
            def exprs = exprs.Map(
                _ => {
                    // Id = expr
                    | <[ $(field : name) = $value ]>
                    // Id : type
                    | <[ $(field : name) : $typ ]> with value = <[ DefaultValue($typ) ]>
                    // Id
                    | <[ $(field : name) ]> as value
                    // expr . Id
                    | <[ $_ . $(field : name)]> as value =>
                        (field, value)
                    | expr =>
                        Message.FatalError(expr.Location, $"expected Id = expr | expr.Id | Id | Id : type, got $expr")
                    });
            def (fields, values) = List.Split(exprs);
            (fields, one_or_tuple(values))
        | <[ ($tuple_expr) : { .. $exprs }]> =>
            def fields = exprs.Map(
                _ => {
                    | <[ $(field : name) ]> => field
                    | expr => 
                        Message.FatalError(expr.Location, $"expected Id, got $expr")
                    });
            (fields, tuple_expr)
        | _ =>
            // TODO: use Error instead of FatalError, improve error messages
            Message.FatalError(body.Location, $"expected new { Id = expr  | expr.Id | Id, ... } | new (expr, ...) : {Id, ...}; got $body")
    }
    
    def (fields, values) = extractFieldNamesAndValues(body);

    def mangled_args = fields.ToString()
        .Filter(c => Char.IsLetterOrDigit(c) || c == '_');

    def mangled_name = $"Anonymous_$(fields.Length)_$(mangled_args)_"
        + fields.ToString().GetHashCode().ToString();

    def typer = Macros.ImplicitCTX();
    unless(typer.Env.LookupType([mangled_name]) is Some(_))
    {
        def types = $[$"T$i" | i in [0..fields.Length-1]]
                .Map(id => <[ $(id : usesite) ]>);

        def members = List.Combine(fields, types).Map(
            (field, typ) =>
                <[ decl: 
                    [Accessor(flags=WantSetter)]
                    mutable $(field.NewName("_" + field.Id) : name) : $typ
                ]>);

        def fields_strings = fields.Map(f => <[ $(f.Id : string) ]>);
        def members = 
            <[ decl: public override __Fields : list[string]
                {
                    get
                    {
                        [.. $fields_strings]
                    }
                }
            ]> :: members;

        def tuple_type = match(types)
        {
            | [typ] => <[ $typ ]>
            | types => <[ @*(.. $types) ]>
        }

        def fields_access_exprs = fields.Map(f => <[ $(f : name) ]>);
        def members = 
            <[ decl: 
                public override __Values : $tuple_type
                { 
                    get
                    {
                        $(one_or_tuple(fields_access_exprs))
                    }
                    set
                    {
                        $(one_or_tuple(fields_access_exprs)) = value
                    }
                }
            ]> :: members;

        def type_parms = types.Map( n => 
                            { | <[$(n : name)]> => PT.Splicable.Name(n) });

        def anonyclass = 
            <[ decl:
                [Record] internal class $(mangled_name : usesite)[.. $type_parms]
                    : Anonymous[$tuple_type]
                {
                    .. $members
                }
            ]>;
        def tbuilder = typer.Env.Define(anonyclass);
        unless(typer.InErrorMode) tbuilder.Compile();
    }

    <[$(mangled_name : usesite)($values)]>
}

}
Re[2]: анонимные классы, proof of concept; версия 4
От: Сергей Туленцев Россия http://software.tulentsev.com
Дата: 27.03.07 17:47
Оценка:
Здравствуйте, Vermicious Knid, Вы писали:

VK>В этой версии наконец реализована вся функциональность анонимных типов, которую я нашел в спецификации C# 3.0 (если я что-то пропустил — дайте знать).



Думаю, что не одинок во мнении, что этот макрос неплохо бы иметь в поставке компилятора.
Почему бы тебе его не закоммиттить туда?
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
--
Re[2]: анонимные классы, proof of concept; версия 4
От: VladD2 Российская Империя www.nemerle.org
Дата: 27.03.07 22:05
Оценка:
Здравствуйте, Vermicious Knid, Вы писали:

Два вопроса.
1. Как объявить фукнцию возрващающую анонимный тип?
2. Как это дело поведет себя при взваимодействие между сборками. Ну, еслио объявить фунцию возвращающую анонимный тип, то как ее использовать в другой сборке?
3. Зачем делать изменяемые поля?
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[3]: анонимные классы, proof of concept; версия 4
От: Vermicious Knid  
Дата: 27.03.07 22:53
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Два вопроса.

Исходить надо из того, что моя задача минимум состояла в том, чтобы сделать "как в C# 3.0". Честно говоря понятия не имею для чего могут быть полезны анонимные типы — пока не попробуешь не узнаешь. Но это конечно не код для практического применения, это эксперимент. Еще была мысль попробовать подумать над специальной поддержкой для Linq в Nemerle, возможно анонимные типы там пригодятся.

В принципе получилось немного лучше, чем в C# 3.0, и я вполне этим доволен. Стопроцентно концептуальные проблемы анонимных типов решить все равно не получиться, но сделать их более юзабельными можно. Если будут у тебя или кого-нибудь еще есть какие-то идеи по поводу твоих вопросов в частности и анонимных типов вообще — буду рад их услышать.

VD>1. Как объявить фукнцию возрващающую анонимный тип?

Это можно сделать, но информация о именах свойств будет доступна только в рантайме, но зато не только через reflection. С типами свойств и их значениями все ok, абстрактный класс Anonymous позволяет получить их кортеж.
SomeMethod() : Anonymous[int*int]
{
  new {Foo = 1; Bar = 2}
}

Еще можно легко сделать что-то для приведения и проверки соответствия анонимных типов друг другу. Например:
// при несоответствии имен в анонимном типе возвращаемом SomeMethod() бросит исключение
def x = new SomeMethod() : { Foo; Bar };

VD>2. Как это дело поведет себя при взваимодействие между сборками. Ну, еслио объявить фунцию возвращающую анонимный тип, то как ее использовать в другой сборке?
Все анонимные типы генерируются как internal. Для передачи между сборками можно использовать их общий супертип — Anonymous. Использовать можно его или преобразовывать в конкретный анонимный тип на месте.

VD>3. Зачем делать изменяемые поля?

Чтобы было в точности как в C# 3.0. Ну и ради разнообразия — какой в них тогда смысл если в Nemerle есть куча иммутабельных по умолчанию и вполне удобных структур данных.

Там если ты еще не успел заметить еще пара сомнительных идей прямиком из дизайна анонимных типов C# 3.0 взята. Так уж если реализовывать изначально странную структуру данных, то надо быть последовательным до конца.
Re[3]: анонимные классы, proof of concept; версия 4
От: Vermicious Knid  
Дата: 27.03.07 23:08
Оценка:
Здравствуйте, Сергей Туленцев, Вы писали:

СТ>Думаю, что не одинок во мнении, что этот макрос неплохо бы иметь в поставке компилятора.

СТ>Почему бы тебе его не закоммиттить туда?
Это пока не планируется. Во-первых я точно знаю, что многим эта идея не понравится(по поводу Влада была уверенность 100%; Москаль/Cкальски тоже непростые люди). Во-вторых для включения в поставку компилятора его надо еще довести его до ума. Ну и потом я сам не вполне уверен в полезности такой штуки в составе компилятора.
Re[4]: анонимные классы, proof of concept; версия 4
От: VladD2 Российская Империя www.nemerle.org
Дата: 28.03.07 01:47
Оценка:
Здравствуйте, Vermicious Knid, Вы писали:

VK>Исходить надо из того, что моя задача минимум состояла в том, чтобы сделать "как в C# 3.0". Честно говоря понятия не имею для чего могут быть полезны анонимные типы — пока не попробуешь не узнаешь. Но это конечно не код для практического применения, это эксперимент.


В таком ключе это безусловно интересно.

VK> Еще была мысль попробовать подумать над специальной поддержкой для Linq в Nemerle, возможно анонимные типы там пригодятся.


А вот для этого все же надо обеспечить поддержку декларации анонимных типов. Иначе нельзя будет даже декомпозицию сделать. Да и криво это.
Хотя конечно в Немерле будет чуль лучше, так как есть локальные фкнции с выводом типов. Они уже позволят делать декомпозицию. Но все равно мне не нравится идея "специальных недотипов". Ну, а что если вывод типов глюкнет и надо будет подсказать чутка?

VK>В принципе получилось немного лучше, чем в C# 3.0, и я вполне этим доволен. Стопроцентно концептуальные проблемы анонимных типов решить все равно не получиться, но сделать их более юзабельными можно. Если будут у тебя или кого-нибудь еще есть какие-то идеи по поводу твоих вопросов в частности и анонимных типов вообще — буду рад их услышать.


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

VD>>1. Как объявить фукнцию возрващающую анонимный тип?

VK>Это можно сделать, но информация о именах свойств будет доступна только в рантайме,

Хм. Это не красиво.

VK>Еще можно легко сделать что-то для приведения и проверки соответствия анонимных типов друг другу. Например:

VK>
VK>// при несоответствии имен в анонимном типе возвращаемом SomeMethod() бросит исключение
VK>def x = new SomeMethod() : { Foo; Bar };
VK>


Хотелось бы, чтобы подобная декларация была доступна в люом контексте где доступны объявления типов.

VD>>2. Как это дело поведет себя при взваимодействие между сборками. Ну, еслио объявить фунцию возвращающую анонимный тип, то как ее использовать в другой сборке?

VK>Все анонимные типы генерируются как internal. Для передачи между сборками можно использовать их общий супертип — Anonymous. Использовать можно его или преобразовывать в конкретный анонимный тип на месте.

Тоже не красиво. Хотя конечно лучше чем полное отсуствие возможности их декларации в методах.

VD>>3. Зачем делать изменяемые поля?

VK>Чтобы было в точности как в C# 3.0.

Это косяк в C# 3.0. Сам понимаешь, котрежи делали неизменяемыми не для смеха. Это дает возможность оперировать ими как вэлью-типами. Например, ты вот говорил о реализации GetHashCode() и т.п. Это нужно для поддержки хэш-таблиц и других сложных контейнеров. Но ведь если объект изменяемый, то хранить его в хэш-таблице будет опасно. Это скрытые грабли. Да и сложно придумать разумный план для использования изменяемости совйст по делу.

VK>Ну и ради разнообразия — какой в них тогда смысл если в Nemerle есть куча иммутабельных по умолчанию и вполне удобных структур данных.


Толк в общем то есть. Это действительно сделало бы некоторый код более безопасным и позволило бы извлечь выгоду через интелисенс. Для реализации ЛИНК-а опять же хорошо бы подошло. А незименяемость была бы эдаким интеллектуальным взносом и данью традициям ФП.

VK>Там если ты еще не успел заметить еще пара сомнительных идей прямиком из дизайна анонимных типов C# 3.0 взята. Так уж если реализовывать изначально странную структуру данных, то надо быть последовательным до конца.


Чесно говоря у меня сейчас забот выше крыши и серьезно я в код реализации не глядел. Поглядел только применение.

ЗЫ

Подумай все же над полноценной поддержкой грамотной версии анонимных типов. Если получится, то возможно успеем остановить Хейльсберга от ошибки.
Думаю, что можно не заморачиваться на макросную реализауию. Если идеи лбудут качественными, то можно было бы и компилятор подправить. Ведь все в наших руках!
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[4]: анонимные классы, proof of concept; версия 4
От: VladD2 Российская Империя www.nemerle.org
Дата: 28.03.07 01:47
Оценка:
Здравствуйте, Vermicious Knid, Вы писали:

VK>Это пока не планируется. Во-первых я точно знаю, что многим эта идея не понравится(по поводу Влада была уверенность 100%; Москаль/Cкальски тоже непростые люди). Во-вторых для включения в поставку компилятора его надо еще довести его до ума. Ну и потом я сам не вполне уверен в полезности такой штуки в составе компилятора.


Если решение будет полноценное, а не как в C# 3.0, то я тебе гарантирую, что все будут только за. А так это действительно введение сырой концепции в язык.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re: анонимные классы, proof of concept
От: IT Россия linq2db.com
Дата: 30.03.07 19:53
Оценка: +1
Здравствуйте, Vermicious Knid, Вы писали:

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

def foo = new MyType { Foo = 1; Bar = "foobar" + 2.ToString(); Baz =  Foobar(22) };


Такой класс будет вложенным в использующий его тип и его имя будет легко доступно из-вне. Единственная проблема — разруливать пересечения таких типов. Т.е. макрос должен проверять если такой тип уже объявлен, то использовать его, если он совпадает по объявлению. В противном случае ошибка компиляции.
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
Re[2]: анонимные классы, proof of concept
От: VladD2 Российская Империя www.nemerle.org
Дата: 30.03.07 23:04
Оценка:
Здравствуйте, IT, Вы писали:

IT>Вот тут подумалось. Какую проблему решают анонимные типы? По идее их задача состоит в возможности краткого объявления типа по месту. Почему бы тогда опционально не сделать это объявление по месту именованным:


IT>
IT>def foo = new MyType { Foo = 1; Bar = "foobar" + 2.ToString(); Baz =  Foobar(22) };
IT>


И как его использовать при описании возрващаемого значения фукнции/метода?
Мы же не можем выводить тип фукнции из ее тела?
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[3]: анонимные классы, proof of concept
От: IT Россия linq2db.com
Дата: 31.03.07 00:45
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>И как его использовать при описании возрващаемого значения фукнции/метода?

VD>Мы же не можем выводить тип фукнции из ее тела?

class MyClass
{
  MyMethod() : MyType
  {
    def foo = new MyType { Foo = 1; Bar = "foobar" + 2.ToString(); Baz =  Foobar(22) };
    foo
  }
}


или полный вариант:

class MyClass
{
  MyMethod() : MyClass.MyType
  {
    def foo = new MyType { Foo = 1; Bar = "foobar" + 2.ToString(); Baz =  Foobar(22) };
    foo
  }
}


или совсем полный вариант:

class MyClass
{
  MyMethod() : MyClass.MyType
  {
    def foo = new public MyType : MyBaseClass { Foo = 1; Bar = "foobar" + 2.ToString(); Baz =  Foobar(22) };
    foo
  }
}


... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
Re[4]: анонимные классы, proof of concept
От: VladD2 Российская Империя www.nemerle.org
Дата: 31.03.07 17:02
Оценка: +1
Здравствуйте, IT, Вы писали:

IT>
IT>class MyClass
IT>{
IT>  MyMethod() : MyType
IT>  {
IT>    def foo = new MyType { Foo = 1; Bar = "foobar" + 2.ToString(); Baz =  Foobar(22) };
IT>    foo
IT>  }
IT>}
IT>


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

В общем, чудес не бывает.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[5]: анонимные классы, proof of concept
От: IT Россия linq2db.com
Дата: 31.03.07 19:47
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Это не приемлемо с технической точки зрения. Типы должны быть извесны до разбора тел функций.


А блин, про это я забыл.

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


Это не факт.
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
Re[6]: анонимные классы, proof of concept
От: VladD2 Российская Империя www.nemerle.org
Дата: 31.03.07 20:12
Оценка:
Здравствуйте, IT, Вы писали:

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


IT>Это не факт.


Факт, факт. Я сам бы первый поубевал бы тех кто типы описывает в глубинах методов. Это как же код читать то?
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[7]: анонимные классы, proof of concept
От: IT Россия linq2db.com
Дата: 31.03.07 20:39
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Факт, факт. Я сам бы первый поубевал бы тех кто типы описывает в глубинах методов. Это как же код читать то?


Тогда убийства надо начинать с Хейлсберга.
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
Re[8]: анонимные классы, proof of concept
От: VladD2 Российская Империя www.nemerle.org
Дата: 31.03.07 22:05
Оценка:
Здравствуйте, IT, Вы писали:

IT>Тогда убийства надо начинать с Хейлсберга.


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

Анонимный тип вполне может быть описан по месту. Как-то так:
x : { Name : string; Age : DateTime; }

По уму можно было бы даже ему и имена давать:
type someType = { Name : string; Age : DateTime; };
...
Func(x : someType) : someType
{
  ...
}
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.