Общий принцип здесь заключается в том, что информация о типах передается только «в одну сторону»: от внутри до вне выражения.Пример, который вы приводите, предельно прост.Предположим, что мы хотим иметь поток информации о типе «в обе стороны» при выполнении вывода типа для метода 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 #,делает намного более сложный вывод типов, чем C #. Система типов Haskell на самом деле Turing Complete: вы можете кодировать произвольно сложные проблемы в системе типов и просить компилятор их решать. Как мы увидим позже, то же самое верно и для разрешения перегрузки.в C # - вы не можете кодировать проблему остановки в системе типов C #, как в 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, которая оправдывает огромные затраты на выполнение этой работы в целом.