Re[4]: [Этюд, C#] Overload resolution
От: VladD2 Российская Империя www.nemerle.org
Дата: 05.02.09 05:59
Оценка: +1
Здравствуйте, nikov, Вы писали:

N>А ты посмотрел в 7.5.4.1?


N>

N>7.5.4.1 Identical simple names and type names
N>In a member access of the form E.I, if E is a single identifier, and if the meaning of E as a simple-name (§7.5.2) is a constant, field, property, local variable, or parameter with the same type as the meaning of E as a type-name (§3.8), then both possible meanings of E are permitted. The two possible meanings of E.I are never ambiguous, since I must necessarily be a member of the type E in both cases. In other words, the rule simply permits access to the static members and nested types of E where a compile-time error would otherwise have occurred.


И что ты узрел в выделенном?

Где ты видишь в данном примере, что у параметра тип совпадает с именем? Его тип не определен! Его просто не откуда вывести! То что кто-то там пытается из этого имени вызвать что-то еще ничего не значит.
Вот тебе простой пример демонстрирующий что будет если тип переменной выведется по другому:
Bar(new Action<S>(T => { T.GetType(); T.Foo(); })); // error CS0176: Member 'S.Foo()' cannot be accessed with an instance reference; qualify it with a type name instead

Что у нас изменилось? А только лишь тип параметра с именем "T".
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[6]: [Этюд, C#] Overload resolution
От: koandrew Канада http://thingselectronic.blogspot.ca/
Дата: 05.02.09 06:18
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Понятно только одно. Ты вообще не понял, что тут происходит.

VD>Попробуй обдумать простую мысль. T и S в приведенном коде — это не типы, а имена параметров лябд. Таким образом обращение к ним через точку должно приводить к поиску экзеплярных методов с именем Foo. Но таковых нет!
VD>Компилятор просто не имеет право делать предположений о типах параметров. Он или должен вывести их из инициализации, или сообщить о неоднозначности.

Я сам нашёл опровержения моей теории, но поскольку nikov уже прокомментировал мой пост, не посчитал нужным что-то постить дополнительно...
[КУ] оккупировала армия.
Re[5]: [Этюд, C#] Overload resolution
От: VladD2 Российская Империя www.nemerle.org
Дата: 05.02.09 06:47
Оценка:
Здравствуйте, Алексей., Вы писали:

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


Это шарпа то? Гы-гы. Ты видимо читал блоги каких-то других программистов. В Шарпе линейный вывод типов (только из инициализации).

Вот при выводе типов из использования (как в Немерле) разрешение перегрузок и правда является NP-полной задачей. Но и там можно применять эвристики делающие процесс разрешения перегрузок приемлемо бысрым.

А>За пример, спасибо, не думал что так просто отправить компилятор в спячку.


Это потому, что в компиляторы такие алгоритмы используются. Тот же пример на Немерле компилируется в мгновение ока. И интеграция на нем работает отлично:
using System;

module Program
{
    Main() : void
    {
      _ = Foo(a => Foo(_ => Foo(_ => Foo(_ => Foo(_ => Foo(_ => Foo(_ => Foo(_ => a))))))));
    }

    Foo(_ : Func[byte, byte]) : string { null }
    Foo(_ : Func[short, short]) : string { null }
    Foo(_ : Func[int, int]) : string { null }
    Foo(_ : Func[long, long]) : string { null }
    Foo(_ : Func[string, string]) : string { null }
}

Если подвести мышку к параметру "a", то молниеносно выведится "(function parametr) a : string".
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[3]: [Этюд, C#] Overload resolution
От: Алексей.  
Дата: 05.02.09 07:55
Оценка: 26 (1) +1 -1
Здравствуйте, VladD2, Вы писали:

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

7.5.4.1 так прямо и говорит выражение simple-name (в данном случае T), которое по сути всегда является идентификатором, в определенном случае может интерпретироваться как имя типа.

In a member access of the form E.I, if E is a single identifier, and if the meaning of E as a simple-name (§7.5.2) is a constant, field, property, local variable, or parameter with the same type as the meaning of E as a type-name (§3.8), then both possible meanings of E are permitted. The two possible meanings of E.I are never ambiguous, since I must necessarily be a member of the type E in both cases. In other words, the rule simply permits access to the static members and nested types of E where a compile-time error would otherwise have occurred.


VD>В данном же случае имеет место неверное предположение о том, что тип параметра с именх Е тоже Е. Но это не верно! Это просто не откуда взять.


Виноват, забыл подробно описать как же компилятор "выводит" что параметр лямбды T имеет тип T.
Цитирую раздел 6.5:

If D has a void return type and the body of F is an expression, when each parameter of F is given the type of the corresponding parameter in D, the body of F is a valid expression (wrt §7) that would be permitted as a statement-expression (§8.6).


Рассмотрим преобразование лямбды T => T.Foo() в делегат Action<T>.

Action определен как public delegate void Action<T>(T obj);

Вызов инстанцированного делегата будет выглядеть как: void (T obj), где T это имя класса T в примере nikov'а, а не тип-параметр делегата.
Декларацию лямбды можно записать как: void (? T)

Сопоставляя параметры делегата и параметры лямбды получаем что параметр T лямбды имеет тип T.
Re[6]: [Этюд, C#] Overload resolution
От: Алексей.  
Дата: 05.02.09 08:12
Оценка: +1
Здравствуйте, VladD2, Вы писали:

VD>Это шарпа то? Гы-гы. Ты видимо читал блоги каких-то других программистов. В Шарпе линейный вывод типов (только из инициализации).


Я так понимаю реализация 7.4.2.11 Inferred return type порождает комбинаторику аналогичную приведенную nikov'ым примером.

VD>Вот при выводе типов из использования (как в Немерле) разрешение перегрузок и правда является NP-полной задачей. Но и там можно применять эвристики делающие процесс разрешения перегрузок приемлемо бысрым.


Это значит что найти пример который отправит в спячку Nemerle несколько сложнее.
Re[5]: [Этюд, C#] Overload resolution
От: nikov США http://www.linkedin.com/in/nikov
Дата: 05.02.09 09:11
Оценка: 2 (1)
Здравствуйте, VladD2, Вы писали:

VD>Где ты видишь в данном примере, что у параметра тип совпадает с именем? Его тип не определен! Его просто не откуда вывести! То что кто-то там пытается из этого имени вызвать что-то еще ничего не значит.


Посмотри 6.5 Anonymous function conversions — там описано, как происходит преобразование от анонимной функции к делегатам, и откуда берутся типы параметров анонимной функции, если они не указаны явно.

An anonymous-method-expression or lambda-expression is classified as an anonymous function (§7.14). The expression does not have a type but can be implicitly converted to a compatible delegate type or expression tree type. Specifically, a delegate type D is compatible with an anonymous function F provided:
...
• If D has a void return type and the body of F is an expression, when each parameter of F is given the type of the corresponding parameter in D, the body of F is a valid expression (wrt §7) that would be permitted as a statement-expression (§8.6).


Как видишь, компилятору приходится заглядывать в тело анонимного метода, чтобы проверить, существует ли преобразование к делегату. Теперь, что происходит при обработке вызова метода Bar? Смотрим дальше:

7.5.5.1 Method invocations
• The set of candidate methods for the method invocation is constructed. For each method F associated with the method group M:
...
• F is applicable with respect to A (§7.4.3.1).

7.4.3.1 Applicable function member
A function member is said to be an applicable function member with respect to an argument list A when all of the following are true:
...
an implicit conversion (§6.1) exists from the argument to the type of the corresponding parameter


То есть, когда у тебя анонимный метод задан в качестве аргумента к перегруженному методу Bar, то компилятор перебирает методы и ищет те, к для которым существует неявное преобразование от анонимного метода к соответсвующему типу в сигнатуре. Поэтому производится несколько попыток задать типы (взятые из параметров делегата в сигнатуре) параметрам анонимного метода, и проверить, будет ли тело анонимного метода корректным. Те попытки, которые будут успешными, дадут кандидатов для overload resolution. В моем примере одна успешная попытка, там что overload resolution тривиален. Так что, тип есть откуда вывести.
Re[4]: [Этюд, C#] Overload resolution
От: nikov США http://www.linkedin.com/in/nikov
Дата: 05.02.09 11:43
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>В общем, очередное доказательство, того, что люди с большим энтузиазмом ищут оправдание ошибки нежели признают ее.


Только все происходило наоборот: я прочитал спецификацию, понял, что она приводит к таком интересному эффекту, а только потом написал код и убедился, что он работает, как предписано спецификацией. Никаких ошибок нет.
Re[6]: [Этюд, C#] Overload resolution
От: nikov США http://www.linkedin.com/in/nikov
Дата: 05.02.09 11:48
Оценка: -1
Здравствуйте, VladD2, Вы писали:

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


VD>Это шарпа то? Гы-гы. Ты видимо читал блоги каких-то других программистов. В Шарпе линейный вывод типов (только из инициализации).


Как бы не так. Вывод из инициализации — это только для переменных, объявленных через var. Для параметров в лямбдах тип может выводиться из анализа использования этих параметров (куда они передаются, какие методы вызываются, какие операторы применяются, к чему они кастятся и т.д.). В этом случае строится дерево всевозможных вариантов и производится перебор. Я достаточно хорошо разбираюсь в этом процессе, и много обсуждал его и с его разработчиками в Mirosoft, и с теми, кто его реализовывал для ReSharper в JetBrains.
Re[6]: [Этюд, C#] Overload resolution
От: VladD2 Российская Империя www.nemerle.org
Дата: 05.02.09 15:20
Оценка:
Здравствуйте, nikov, Вы писали:

N> скипнуто...


Я не знаю зачем ты мне все это излагал. Все это я и так знал.

N>То есть, когда у тебя анонимный метод задан в качестве аргумента к перегруженному методу Bar, то компилятор перебирает методы и ищет те, к для которым существует неявное преобразование от анонимного метода к соответсвующему типу в сигнатуре. Поэтому производится несколько попыток задать типы (взятые из параметров делегата в сигнатуре) параметрам анонимного метода, и проверить, будет ли тело анонимного метода корректным. Те попытки, которые будут успешными, дадут кандидатов для overload resolution. В моем примере одна успешная попытка, там что overload resolution тривиален. Так что, тип есть откуда вывести.


У тебя будет два подходящих кандидата. А поведение компилятора C# ошибка.
Повторение одних и тех же аргументов не делают эти аргументы правильнее.

Ты лучше объясни почему ты считаешь, что 7.5.4.1 должен накладывать какие-то ограничения на тип параметра?

Там же ясно сказано:

In a member access of the form E.I, if E is a single identifier, and if the meaning of E as a simple-name (§7.5.2) is a constant, field, property, local variable, or parameter with the same type as the meaning of E as a type-name (§3.8), then both possible meanings of E are permitted. The two possible meanings of E.I are never ambiguous, since I must necessarily be a member of the type E in both cases. In other words, the rule simply permits access to the static members and nested types of E where a compile-time error would otherwise have occurred. For example:


Так что же тебе дает основание пологать, что параметр T имеет тот же тип что и тип T?

ЗЫ

Ты по этому поводу с разработчиками компилятора говорил?
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[5]: [Этюд, C#] Overload resolution
От: VladD2 Российская Империя www.nemerle.org
Дата: 05.02.09 15:22
Оценка:
Здравствуйте, nikov, Вы писали:

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


VD>>В общем, очередное доказательство, того, что люди с большим энтузиазмом ищут оправдание ошибки нежели признают ее.


N>Только все происходило наоборот: я прочитал спецификацию, понял, что она приводит к таком интересному эффекту, а только потом написал код и убедился, что он работает, как предписано спецификацией. Никаких ошибок нет.


Я тебе уже 5 раз указал на ошибку в твоих рассуждениях. То что она совпала с работой компилятора может и случайность.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[7]: [Этюд, C#] Overload resolution
От: nikov США http://www.linkedin.com/in/nikov
Дата: 05.02.09 16:17
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Ты лучше объясни почему ты считаешь, что 7.5.4.1 должен накладывать какие-то ограничения на тип параметра?

Потому что из 7.5.4.1 следует, что T.Foo() — это некорректное выражение, если T имеет тип, отличный от T, и корректное, если Т имеет тип Т.

VD>Там же ясно сказано:

VD>

VD>In a member access of the form E.I, if E is a single identifier, and if the meaning of E as a simple-name (§7.5.2) is a constant, field, property, local variable, or parameter with the same type as the meaning of E as a type-name (§3.8), then both possible meanings of E are permitted. The two possible meanings of E.I are never ambiguous, since I must necessarily be a member of the type E in both cases. In other words, the rule simply permits access to the static members and nested types of E where a compile-time error would otherwise have occurred. For example:


VD>Так что же тебе дает основание пологать, что параметр T имеет тот же тип что и тип T?

1) Во время overload resolution, такое основание мне дает то, что в данный момент рассматривается overload с типом Action<T> в сигнатуре.
2) После overload resolution, такое основание мне дает то, что во время overload resolution был выбран overload с типом Action<T> в сигнатуре.

VD>ЗЫ

VD>Ты по этому поводу с разработчиками компилятора говорил?

Говорил.
Re[6]: [Этюд, C#] Overload resolution
От: nikov США http://www.linkedin.com/in/nikov
Дата: 05.02.09 16:20
Оценка: :))) :)
Здравствуйте, VladD2, Вы писали:

VD>Я тебе уже 5 раз указал на ошибку в твоих рассуждениях. То что она совпала с работой компилятора может и случайность.


Когда я говорю про overload resolution в C#, у меня не бывает ошибок
Я тебе уже несколько раз объяснил, почему то, что ты считаешь ошибкой — это не ошибка. Это просто неожиданный эффект.
Re: [Этюд, C#] Overload resolution
От: Andir Россия
Дата: 05.02.09 22:50
Оценка: +1
Здравствуйте, nikov, Вы писали:

N>Скомпилируется ли этот код?

[code skipped]

Итак, поведение компилятора выглядит обоснованным. Но всё равно остаётся какое-то неуловимое ощущение общей нелогичности такого поведения.

Архитектурное решение: Вывод типа лямбды на основе перебора возможных вариантов.
К чему это приводит: нарушение инвариантности имени переменной (от её имени зависит "правильность" программы), невозможности рефакторинга "Rename" в некоторых случаях.
Вопрос: А есть ли примеры подобного нарушения в языке C#, не относящиеся к выводу типа в лямбдах?

Дополнительно: Считаешь ли ты подобный выбор разработчиков обоснованным?

P.S. Логичным продолжением развития такого поведения является очевидно распространение подхода на содержимое методов. Думаю будет много интересных эффектов

С Уважением, Andir!
using( RSDN@Home 1.2.0 alpha 4 rev. 1135 ) { /* Работаем */ }
Re[5]: [Этюд, C#] Overload resolution
От: Undying Россия  
Дата: 06.02.09 06:53
Оценка:
Здравствуйте, nikov, Вы писали:

VD>>В общем, очередное доказательство, того, что люди с большим энтузиазмом ищут оправдание ошибки нежели признают ее.

N>Только все происходило наоборот: я прочитал спецификацию, понял, что она приводит к таком интересному эффекту, а только потом написал код и убедился, что он работает, как предписано спецификацией. Никаких ошибок нет.

Ошибочное поведение внесенное в документацию не перестает быть ошибочным.
Re[7]: [Этюд, C#] Overload resolution
От: VladD2 Российская Империя www.nemerle.org
Дата: 07.02.09 13:15
Оценка:
Здравствуйте, nikov, Вы писали:

N>Как бы не так. Вывод из инициализации — это только для переменных, объявленных через var. Для параметров в лямбдах тип может выводиться из анализа использования этих параметров (куда они передаются, какие методы вызываются, какие операторы применяются, к чему они кастятся и т.д.). В этом случае строится дерево всевозможных вариантов и производится перебор. Я достаточно хорошо разбираюсь в этом процессе, и много обсуждал его и с его разработчиками в Mirosoft, и с теми, кто его реализовывал для ReSharper в JetBrains.


Значит недостаточно. Для лямбд, в C#, тип параметров (если он не задан явно) всегда берется из места куда он подставляется. Это может быть параметр функции или переменная. Вывода типов из использования в C# нет. Так что тип возвращаемого значения можно вывести если известны все типы параметров лямбды. Но не зная хотя бы одного типа параметра компилятор будет бессилен.

Попробуй на досуге повторить на C# вот этот немерловый код:
    def f1 = d => d.AddDays(1D);
    WriteLine(f1(DateTime.Now));
    
    def f2 = d => d.AddDays(1D);
    _ = f2((d => d.AddDays(1D))(DateTime.Now));
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[7]: [Этюд, C#] Overload resolution
От: VladD2 Российская Империя www.nemerle.org
Дата: 07.02.09 13:20
Оценка:
Здравствуйте, Алексей., Вы писали:

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


VD>>Это шарпа то? Гы-гы. Ты видимо читал блоги каких-то других программистов. В Шарпе линейный вывод типов (только из инициализации).


А>Я так понимаю реализация 7.4.2.11 Inferred return type порождает комбинаторику аналогичную приведенную nikov'ым примером.


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

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

VD>>Вот при выводе типов из использования (как в Немерле) разрешение перегрузок и правда является NP-полной задачей. Но и там можно применять эвристики делающие процесс разрешения перегрузок приемлемо бысрым.


А>Это значит что найти пример который отправит в спячку Nemerle несколько сложнее.


Потенциально — конечно. Достаточно добиться перемножения в несколько миллионов вариантов. Но на прктике такого не встретишь. Приведенный же пример весьма реален.
Но мы то ведь о конкретном случае говорили?
Так что — это просто баг или не качественная реализация. Компилятор явно зацикливается. А с чего бы это может произойти?
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[7]: [Этюд, C#] Overload resolution
От: VladD2 Российская Империя www.nemerle.org
Дата: 07.02.09 13:25
Оценка:
Здравствуйте, nikov, Вы писали:

N>Когда я говорю про overload resolution в C#, у меня не бывает ошибок


Правильнее говорить — не бывало.

В антропологическом музее:
— А здесь был хрен!
— Не был, а бывал. Это женский скелет!


N>Я тебе уже несколько раз объяснил, почему то, что ты считаешь ошибкой — это не ошибка. Это просто неожиданный эффект.


Ну, я тебе все сказал. Можешь заблуждаться и дальше.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[6]: [Этюд, C#] Overload resolution
От: VladD2 Российская Империя www.nemerle.org
Дата: 07.02.09 13:27
Оценка:
Здравствуйте, Undying, Вы писали:

VD>>>В общем, очередное доказательство, того, что люди с большим энтузиазмом ищут оправдание ошибки нежели признают ее.

N>>Только все происходило наоборот: я прочитал спецификацию, понял, что она приводит к таком интересному эффекту, а только потом написал код и убедился, что он работает, как предписано спецификацией. Никаких ошибок нет.

U>Ошибочное поведение внесенное в документацию не перестает быть ошибочным.


А в документации как раз вроде как все ОК. Там же сказано, что все это относится для переменных тип и название которых совпадает с перекрытым типом.

В прочем, по сути я согласен.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[4]: [Этюд, C#] Overload resolution
От: VladD2 Российская Империя www.nemerle.org
Дата: 07.02.09 13:46
Оценка: 1 (1)
Здравствуйте, Алексей., Вы писали:

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


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

А>7.5.4.1 так прямо и говорит выражение simple-name (в данном случае T), которое по сути всегда является идентификатором, в определенном случае может интерпретироваться как имя типа.
А>

А>In a member access of the form E.I, if E is a single identifier, and if the meaning of E as a simple-name (§7.5.2) is a constant, field, property, local variable, or parameter with the same type as the meaning of E as a type-name (§3.8), then both possible meanings of E are permitted. The two possible meanings of E.I are never ambiguous, since I must necessarily be a member of the type E in both cases. In other words, the rule simply permits access to the static members and nested types of E where a compile-time error would otherwise have occurred.


Упорством с которым вы игнорируете выделенный текст меня поражает. Я все же услышу комментарий по поводу выделенного мной текста:

In a member access of the form E.I, if E is a single identifier, and if the meaning of E as a simple-name (§7.5.2) is a constant, field, property, local variable, or parameter with the same type as the meaning of E as a type-name (§3.8), then both possible meanings of E are permitted. The two possible meanings of E.I are never ambiguous, since I must necessarily be a member of the type E in both cases. In other words, the rule simply permits access to the static members and nested types of E where a compile-time error would otherwise have occurred.


А>Декларацию лямбды можно записать как: void (? T)


А>Сопоставляя параметры делегата и параметры лямбды получаем что параметр T лямбды имеет тип T.


С чего бы это?
Все что можно с уверенностью утверждать, что выполняются следующие правила:
1. Параметр Т совпадает по имени с типом Т.
2. Тип параметра T неизвестен.

По 7.5.4.1 должно выполняться еще одно правило. Тип переменной должен иметь то же тип, что и перекрываемый.

Вот простой пример демонстрирующий идею:
using System;

class Program
{
  public static Program Program;

  static void TestStatic() { }
  void Test() { }

  static void Main(string[] args)
  {
    Program.TestStatic();
    Program.Test();
  }
}

Этот код отличо компилируется и работает.
Если мы теперь добавим еще один класс содержащий такой же экзеплярный метод и заменим тип переменной на него:
using System;

class A
{
  public void Test() { }
}

class Program
{
  public static A Program;

  static void TestStatic() { }
  void Test() { }

  static void Main(string[] args)
  {
    Program.TestStatic();
    Program.Test();
  }
}

То вызов статического метода сразу станет не верным, так как не выполнится правило "переменная должна иметь тот же тип, что и перекрываемый".

Возвращаясь к исходному примеру, совершенно ясно, что тип переменной T вывести нельзя (так как он не задается ни явно, ни опосредовано). Стало быть правило 7.5.4.1 применять нельзя и даже вызов статического метода будет ошибкой. А уж типизация лямбды на этом основании просто нонсенс.

В общем, вы защищаете банальны баг подсистемы вывода типов шарпа.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[4]: [Этюд, C#] Overload resolution
От: nikov США http://www.linkedin.com/in/nikov
Дата: 08.02.09 12:48
Оценка:
Здравствуйте, koandrew, Вы писали:

K>P.S. Тем, кто осилил §7.4 (особенно §7.4.2) ИМХО надо памятник при жизни ставить — без ящика водки не разберёшься


Если там есть что-то непонятное, обращайся, постараюсь объяснить.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.