Динамическая проверка констрейнтов
От: nikov США http://www.linkedin.com/in/nikov
Дата: 03.09.08 13:15
Оценка:
Вот такой код на C#:

using System.Collections;
using System.Reflection;

class Program
{
    static void Main()
    {
        Foo<IEnumerable>("");
    }

    static void Foo<T>(T x)
    {
        // if(T implements IEnumerable)
        if(typeof (IEnumerable).IsAssignableFrom(typeof (T)))
        {
            // Bar<T>(x);
            typeof (Program).GetMethod(
                "Bar", BindingFlags.NonPublic | BindingFlags.Static).MakeGenericMethod(typeof (T)).Invoke(
                null, new object[] {x});
        }
    }

    static void Bar<T>(T x) where T : IEnumerable
    {
    }
}


Видно, что хочется сделать: динамически (в runtime) проверить, удовлетворяет ли тип-параметр некоторому констрейнту, и если удовлетворяет, то передать его в качестве типа-аргумента в другой метод, статически требующий этого констрейнта. Насколько я понимаю, система типов в C# не позволяет сделать такой трюк, и вывернуться можно только с использованием рефлексии.
Вопрос: есть ли языки (не обязательно под .NET), которые поддерживают такое на уровне системы типов? Насколько это вообще здравая идея?
Re: Динамическая проверка констрейнтов
От: Курилка Россия http://kirya.narod.ru/
Дата: 03.09.08 13:24
Оценка: 13 (1)
Здравствуйте, nikov, Вы писали:

[cut]
N>Вопрос: есть ли языки (не обязательно под .NET), которые поддерживают такое на уровне системы типов? Насколько это вообще здравая идея?

Возможно не совсем то, но смотрел Typed Contracts for Functional Programming? И Well-typed programs can’t be blamed?
Re: Динамическая проверка констрейнтов
От: samius Япония http://sams-tricks.blogspot.com
Дата: 03.09.08 13:30
Оценка:
Здравствуйте, nikov, Вы писали:

N>Вот такой код на C#:


[Skip]

N>Видно, что хочется сделать: динамически (в runtime) проверить, удовлетворяет ли тип-параметр некоторому констрейнту, и если удовлетворяет, то передать его в качестве типа-аргумента в другой метод, статически требующий этого констрейнта. Насколько я понимаю, система типов в C# не позволяет сделать такой трюк, и вывернуться можно только с использованием рефлексии.

Думаю, что с помощью DynamicMethods(Emit) тоже можно выкрутиться. Делегат можно объявить статическим у класса Program<T> и из метода Foo<T> делегировать Program<T>.BarMethod(T x)

N>Вопрос: есть ли языки (не обязательно под .NET), которые поддерживают такое на уровне системы типов? Насколько это вообще здравая идея?

Собственно на вопрос ответ не знаю
Re[2]: Динамическая проверка констрейнтов
От: samius Япония http://sams-tricks.blogspot.com
Дата: 03.09.08 13:56
Оценка:
Здравствуйте, samius, Вы писали:

S>Думаю, что с помощью DynamicMethods(Emit) тоже можно выкрутиться. Делегат можно объявить статическим у класса Program<T> и из метода Foo<T> делегировать Program<T>.BarMethod(T x)

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

N>>Насколько это вообще здравая идея?

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

P.S. Почти таким макаром я изобразил generic-метод для проверки флагов Enum-а (http://sams-tricks.blogspot.com/2008/08/systemenum.html). Баловство, конечно. Но должно быть более серьезное применение.
Re: Динамическая проверка констрейнтов
От: mkizub Литва http://symade.tigris.org
Дата: 03.09.08 14:40
Оценка:
Здравствуйте, nikov, Вы писали:

N>Видно, что хочется сделать: динамически (в runtime) проверить, удовлетворяет ли тип-параметр некоторому констрейнту, и если удовлетворяет, то передать его в качестве типа-аргумента в другой метод, статически требующий этого констрейнта. Насколько я понимаю, система типов в C# не позволяет сделать такой трюк, и вывернуться можно только с использованием рефлексии.

N>Вопрос: есть ли языки (не обязательно под .NET), которые поддерживают такое на уровне системы типов? Насколько это вообще здравая идея?

Вопрос, а система типов в C# позволяет делать что-то вроде

if (a instanceof String)
  return a.Length();
return 0;


То есть проблема не отслеживании типов параметров функции, а в трекинге информации о типах вообще.
Если для локальных переменных, для sealed полей и пр. в С# отслеживается информация о типах
(при instanceof, при присваивании) — то добавить трекинг информации о типах — тривиально.
Само отслеживание информации о реальном типе — достаточно сильно тормозит компилер.
Плюс есть сложности с goto-back/loop-ами.
Плюс везде в коде компилятора нужно будет поменять var.getType() на var.getTypes()[],
потому как у переменных может быть несколько типов (интерфейсы). Или надо поддерживать
арифметику типов. И ещё надо будет делать картезианское произведение всех типов для
параметров методов при резолвинге...

Моя практика показывает, что такое отслеживание (в моём компиляторе оно работает) достаточно
удобно, так как делает код более понятным. С другой стороны, в случае изменяемых полей
этот трекинг типов делать нельзя (поля могут поменяться из другого треда), и надо
писать дополнительный код для копирования поля в локальную переменную — а это часто съедает
ту лаконичность кода, за которую и боролись. Тут лучше всего выглядит pattern matching,
который автоматом копирует поля в локальные переменные.
Работа конкретно с generic типами подобного рода мне как-то не понадобилась, потому
и не реализована... хотя если-б понадобилось — реализовал бы за день.

Эрго — идея здравая, но немного для другого места, потребует уйму работы по переписыванию
компилятора и всех средств программирования (IDE и пр.), заодно потянет за собой
уклон в функциональщину...
SOP & SymADE: http://symade.tigris.org , блог http://mkizub.livejournal.com
Re: Динамическая проверка констрейнтов
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 03.09.08 18:30
Оценка: 2 (2)
Здравствуйте, nikov, Вы писали:

N>
N>using System.Collections;
N>using System.Reflection;

N>class Program
N>{
N>    static void Main()
N>    {
N>        Foo<IEnumerable>("");
N>    }

N>    static void Foo<T>(T x)
N>    {
N>        // if(T implements IEnumerable)
N>        if(typeof (IEnumerable).IsAssignableFrom(typeof (T)))
N>        {
N>            // Bar<T>(x);
N>            typeof (Program).GetMethod(
N>                "Bar", BindingFlags.NonPublic | BindingFlags.Static).MakeGenericMethod(typeof (T)).Invoke(
N>                null, new object[] {x});
N>        }
N>    }

N>    static void Bar<T>(T x) where T : IEnumerable
N>    {
N>    }
N>}
N>


Не очень понятно, чем это лучше такого:

static void Foo<T>(T x)
{
        var e = x as IEnumerable;
        if(e != null)
                Bar<T>(e);
}
... << RSDN@Home 1.2.0 alpha 4 rev. 1106 on Windows Vista 6.0.6001.65536>>
AVK Blog
Re[2]: Динамическая проверка констрейнтов
От: nikov США http://www.linkedin.com/in/nikov
Дата: 03.09.08 18:39
Оценка:
Здравствуйте, AndrewVK, Вы писали:

AVK>Не очень понятно, чем это лучше такого:


AVK>
AVK>static void Foo<T>(T x)
AVK>{
AVK>        var e = x as IEnumerable;
AVK>        if(e != null)
AVK>                Bar<T>(e);
AVK>}
AVK>


Тем что твой вариант не скомпилируется.
Re[3]: Динамическая проверка констрейнтов
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 03.09.08 18:50
Оценка:
Здравствуйте, nikov, Вы писали:

N>Тем что твой вариант не скомпилируется.


Ну добавить class constraint
... << RSDN@Home 1.2.0 alpha 4 rev. 1106 on Windows Vista 6.0.6001.65536>>
AVK Blog
Re[2]: Динамическая проверка констрейнтов
От: samius Япония http://sams-tricks.blogspot.com
Дата: 03.09.08 18:51
Оценка:
Здравствуйте, AndrewVK, Вы писали:

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


N>>
...


AVK>Не очень понятно, чем это лучше такого:


AVK>
AVK>static void Foo<T>(T x)
AVK>{
AVK>        var e = x as IEnumerable;
AVK>        if(e != null)
AVK>                Bar<T>(e);
AVK>}
AVK>


        static void Foo<T>(T x)
        {
               var e = ((object)x) as IEnumerable;
               if (e != null)
               {
                   Bar(e);
               }
        }

        static void Bar<T>(T x)
            where T : IEnumerable
        {
        }


Это компилируется и работает
Re[3]: Динамическая проверка констрейнтов
От: nikov США http://www.linkedin.com/in/nikov
Дата: 03.09.08 19:22
Оценка:
Здравствуйте, samius, Вы писали:

S>
S>               var e = ((object)x) as IEnumerable;
S>               if (e != null)
S>               {
S>                   Bar(e);
S>


S>Это компилируется и работает


А это фактически

Bar<IEnumerable>(e)


что уже совсем другое. Теперь мы передаем не T, а базовый тип. Если внутри Bar создается массив T[], то это будет уже IEnumerable[]. Если у Bar возвращаемое значение T, то будет возвращен IEnumerable и т.д.
Re[4]: Динамическая проверка констрейнтов
От: samius Япония http://sams-tricks.blogspot.com
Дата: 03.09.08 19:27
Оценка:
Здравствуйте, nikov, Вы писали:

N>А это фактически


N>
N>Bar<IEnumerable>(e)
N>


N>что уже совсем другое. Теперь мы передаем не T, а базовый тип. Если внутри Bar создается массив T[], то это будет уже IEnumerable[]. Если у Bar возвращаемое значение T, то будет возвращен IEnumerable и т.д.


Да, действительно, это совсем другое. Особенно, если T — struct.
Re[3]: Динамическая проверка констрейнтов
От: nikov США http://www.linkedin.com/in/nikov
Дата: 20.09.08 09:57
Оценка:
Здравствуйте, samius, Вы писали:

S>Это компилируется и работает


Еще можно рассмотреть вариант с несколькими констрейнтами:

where T : IEnumerable, ICloneable
Re[4]: Динамическая проверка констрейнтов
От: samius Япония http://sams-tricks.blogspot.com
Дата: 20.09.08 10:25
Оценка:
Здравствуйте, nikov, Вы писали:

N>Еще можно рассмотреть вариант с несколькими констрейнтами:


N>
N>where T : IEnumerable, ICloneable


Если рассматривать эти констрейнты в контексте вызова Bar<[IEnumerable+ICloneable]>(e), то можно объявить интерфейс, расширяющий оба интерфейса, и в некоторых случаях (когда есть возможность поддержать объектом новый интерфейс) свести задачу к задаче с одним интерфейсом.

Bar<T>() вызвать куда интереснее.
Re: Динамическая проверка констрейнтов
От: MxKazan Португалия  
Дата: 20.09.08 14:24
Оценка:
Здравствуйте, nikov, Вы писали:

N>Видно, что хочется сделать: динамически (в runtime) проверить, удовлетворяет ли тип-параметр некоторому констрейнту, и если удовлетворяет, то передать его в качестве типа-аргумента в другой метод, статически требующий этого констрейнта. Насколько я понимаю, система типов в C# не позволяет сделать такой трюк, и вывернуться можно только с использованием рефлексии.

N>Вопрос: есть ли языки (не обязательно под .NET), которые поддерживают такое на уровне системы типов? Насколько это вообще здравая идея?

По первому вопросу, к сожалению, ничего не могу сказать. По второму, интересно какую задачу решаем? Если в Bar<T> наложен constraint, что страшного в том, что, например, "будет возвращен IEnumerable". По констрейнту получится, что оно так и задумывалось
Re[2]: Динамическая проверка констрейнтов
От: samius Япония http://sams-tricks.blogspot.com
Дата: 20.09.08 20:44
Оценка:
Здравствуйте, MxKazan, Вы писали:

MK>По первому вопросу, к сожалению, ничего не могу сказать. По второму, интересно какую задачу решаем? Если в Bar<T> наложен constraint, что страшного в том, что, например, "будет возвращен IEnumerable". По констрейнту получится, что оно так и задумывалось


Если бы так и задумывалось, то метод Bar был бы оформлен как
static void Bar(IEnumerable x)

Кроме этого, похоже на то, что nikov интерфейс IEnumerable привел просто как первый подвернувшийся под руку.
Вот пример из реальной жизни:
private static Comparer<T> Comparer<T>.CreateComparer()

анализирует тип T, после чего возвращает специфичный для него компарер, а именно один из GenericComparer<T>, NullableComparer<T>, ObjectComparer<T>, где GenericComparer<T> и NullableComparer<T> с разными констрейнтами.

Пример из той же области. Ведь здесь не столько важно передать аргумент типа T или получить результат, завязанный на тип T, сколько вызвать generic метод именно с типом T.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.