Ответ 1
Общий принцип здесь заключается в том, что информация типа течет только "в одну сторону", изнутри наружу выражения. Пример, который вы даете, чрезвычайно прост. Предположим, мы хотели иметь поток информации о типе "в обоих направлениях" при выполнении вывода типа в методе R G<A, R>(A a)
и рассмотреть некоторые из сумасшедших сценариев, которые создают:
N(G(5))
Предположим, что существует десять различных перегрузок из N, каждый из которых имеет другой тип аргумента. Должны ли мы сделать десять разных выводов для R? Если бы мы это сделали, мы бы как-то выбрали "лучший"?
double x = b ? G(5) : 123;
Каким должен быть тип возврата G? Int, потому что другая половина условного выражения является int? Или двойной, потому что в конечном итоге эта вещь будет назначена на двойной? Теперь, возможно, вы начинаете видеть, как это происходит; если вы собираетесь сказать, что вы рассуждаете извне внутрь, насколько далеко вы идете? На этом пути может быть много шагов. Посмотрите, что произойдет, когда мы начнем их комбинировать:
N(b ? G(5) : 123)
Что нам делать? У нас есть десять перегрузок из N на выбор. Считаем ли мы, что R является int? Это может быть int или любой тип, который int неявно конвертируется в. Но из тех типов, какие из них неявно конвертируются в тип аргумента N? Разве мы пишем себе небольшую прологическую программу и просим движок пролога решить, какие все возможные типы возврата могут быть R, чтобы удовлетворить каждую из возможных перегрузок на N, а затем как-то выбрать лучший?
(Я не шучу, есть языки, которые, по сути, пишут небольшую прологическую программу, а затем используют логический движок, чтобы определить, что такое все. F # например, делает более сложный вывод типа, чем С# Система Haskell на самом деле является Turing Complete, вы можете кодировать произвольно сложные проблемы в системе типов и попросить компилятор их решить. Как мы увидим позже, то же самое верно и для разрешения перегрузки в С# - вы не можете кодировать проблему с остановкой в системе типа С#, как вы можете в Haskell, но вы можете кодировать проблемы NP-HARD в проблемы с разрешением перегрузки.)
Это все еще очень простое выражение. Предположим, что у вас что-то вроде
N(N(b ? G(5) * G("hello") : 123));
Теперь мы должны решить эту проблему несколько раз для G и, возможно, и для N, и мы должны решить их в совокупности. У нас есть пять проблем разрешения перегрузки для решения, и все они, если быть справедливыми, должны рассматривать как их аргументы, так и их тип контекста. Если для N существует десять возможностей, то для N (N (...)) существует потенциально сотни возможностей и тысяча для N (N (N (...))), и очень быстро вы должны решить проблему проблемы, которые легко имели миллиарды возможных комбинаций и сделали компилятор очень медленным.
Вот почему у нас есть правило, которое вводит информацию только в одном направлении. Он предотвращает такие виды проблем с курицей и яйцами, где вы пытаетесь определить внешний тип из внутреннего типа и определить внутренний тип из внешнего типа и вызвать комбинаторный взрыв возможностей.
Обратите внимание на то, что информация о типе передает оба пути для лямбда! Если вы скажете N(x=>x.Length)
, то, конечно же, рассмотрим все возможные перегрузки N, которые имеют функции или типы выражений в своих аргументах и опробовать все возможные типы для x. И, конечно, есть ситуации, в которых вы можете легко заставить компилятор опробовать миллиарды возможных комбинаций, чтобы найти уникальную комбинацию, которая работает. Правила вывода типа, которые позволяют сделать это для общих методов, чрезвычайно сложны и заставляют даже нервировать Джона Скита. Эта функция делает разрешение перегрузки NP-HARD.
Получение информации о типе в обоих направлениях для лямбда, чтобы общее разрешение перегрузки работало правильно и эффективно занимало меня около года. Это такая сложная функция, которую мы только хотели взять на себя, если бы мы совершенно положительно полюбили бы эту инвестицию. Выполнение работы LINQ стоило того. Но нет соответствующей функции, такой как LINQ, которая оправдывает огромные затраты на выполнение этой работы в целом.