Я не знаю всех языков программирования, но ясно, что обычно не поддерживается возможность перегрузки метода с учетом его возвращаемого типа (при условии, что его аргументы совпадают по числу и типу).
Я имею в виду что-то вроде этого:
int method1 (int num)
{
}
long method1 (int num)
{
}
Дело не в том, что это большая проблема для программирования, но в некоторых случаях я бы приветствовал это.
Очевидно, что у этих языков не было бы возможности поддерживать это без возможности различить, какой метод вызывается, но синтаксис для этого может быть таким простым, как что-то вроде [int] method1 (num) или [long] method1 (num) таким образом, компилятор будет знать, какой из них будет вызван.
Я не знаю, как работают компиляторы, но это не так сложно сделать, поэтому я удивляюсь, почему что-то подобное обычно не реализуется.
По каким причинам подобное не поддерживается?
источник
Foo
иBar
.Ответы:
Это усложняет проверку типов.
Когда вы разрешаете перегрузку только на основе типов аргументов и разрешаете только выводить типы переменных из их инициализаторов, вся информация о ваших типах передается в одном направлении: вверх по синтаксическому дереву.
Когда вы разрешаете перемещению информации о типе в обоих направлениях, например, выводу типа переменной из ее использования, вам необходим решатель ограничений (например, алгоритм W для систем типов Хиндли-Милнера), чтобы определить тип.
Здесь нам нужно было оставить тип
x
как неразрешенную переменную типа∃T
, где все, что мы знаем о ней, это то, что она разбирается. Только позже, когдаx
используется конкретный тип, у нас будет достаточно информации, чтобы решить ограничение и определить его∃T = int
, который распространяет информацию о типе вниз по синтаксическому дереву из выражения вызова вx
.Если мы не сможем определить тип
x
, либо этот код также будет перегружен (так что вызывающий будет определять тип), либо мы должны будем сообщить об ошибке о неоднозначности.Из этого дизайнер языка может сделать вывод:
Это добавляет сложности к реализации.
Это замедляет проверку типов - в патологических случаях, экспоненциально.
Сложнее создавать хорошие сообщения об ошибках.
Это слишком отличается от статус-кво.
Я не чувствую желания реализовать это.
источник
Потому что это неоднозначно. Используя C # в качестве примера:
Какую перегрузку мы должны использовать?
Хорошо, возможно, это было немного несправедливо. Компилятор не может понять, какой тип использовать, если мы не скажем это на вашем гипотетическом языке. Таким образом, неявная типизация невозможна на вашем языке, и там идут анонимные методы и Linq вместе с ним ...
Как насчет этого? (Немного переопределены подписи, чтобы проиллюстрировать суть.)
Должны ли мы использовать
int
перегрузку илиshort
перегрузку? Мы просто не знаем, нам придется указать это с вашим[int] method1(num)
синтаксисом. Что-то вроде боли разбирать и писать если честно.Дело в том, что он удивительно похож на синтаксис универсального метода в C #.
(C ++ и Java имеют схожие функции.)
Короче говоря, разработчики языка решили решить проблему по-другому, чтобы упростить синтаксический анализ и предоставить гораздо более мощные функции языка.
Вы говорите, что не много знаете о компиляторах. Я очень рекомендую изучить грамматику и парсеры. Как только вы поймете, что такое контекстно-свободная грамматика, у вас будет гораздо лучшее представление о том, почему неоднозначность - это плохо.
источник
short
иint
.method
возвращали short или int, а тип был определен как long.long
/int
/short
больше связана со сложностями подтипов и / или неявных преобразований, чем с перегрузкой возвращаемого типа. В конце концов, число литералов будут перегружены на их тип возвращаемого значения в C ++, Java, C♯, и многие другие, и что , кажется, не представляет проблемы. Вы можете просто составить правило: например, выбрать наиболее конкретный / общий тип.Все языковые функции усложняют задачу, поэтому они должны предоставить достаточно преимуществ, чтобы оправдать неизбежные ошибки, сложные случаи и путаницу среди пользователей, которую создает каждая функция. Для большинства языков этот просто не дает достаточных преимуществ, чтобы оправдать это.
В большинстве языков можно ожидать, что выражение
method1(2)
будет иметь определенный тип и более или менее предсказуемое возвращаемое значение. Но если вы разрешаете перегрузку возвращаемых значений, это означает, что невозможно сказать, что означает это выражение в целом, без учета контекста вокруг него. Подумайте, что происходит, когда у вас естьunsigned long long foo()
метод, реализация которого заканчиваетсяreturn method1(2)
? Должно ли это вызыватьlong
перегрузку -retning или перегрузкуint
-retning или просто вызывать ошибку компилятора?Кроме того, если вам нужно помочь компилятору, аннотируя тип возвращаемого значения, вы не только придумываете больше синтаксиса (что увеличивает все вышеупомянутые затраты на то, чтобы вообще позволить этой функции существовать), но вы фактически делаете то же самое, что и создание два метода с разными именами в «нормальном» языке. Есть
[long] method1(2)
более интуитивным , чемlong_method1(2)
?С другой стороны, некоторые функциональные языки, такие как Haskell с очень сильными статическими системами типов, допускают такого рода поведение, потому что их вывод типов достаточно мощный, так что вам редко нужно аннотировать возвращаемый тип в этих языках. Но это возможно только потому, что эти языки действительно обеспечивают безопасность типов, в большей степени, чем любой традиционный язык, и требуют, чтобы все функции были чистыми и ссылочно-прозрачными. Это не то, что когда-либо выполнимо на большинстве языков ООП.
источник
Он является доступен в Swift и прекрасно работает там. Очевидно, что вы не можете иметь неоднозначный тип с обеих сторон, поэтому он должен быть известен слева.
Я использовал это в простом API кодирования / декодирования .
Это означает, что вызовы, в которых известны типы параметров, такие как
init
объект, работают очень просто:Если вы обратите пристальное внимание, вы заметите
dechOpt
звонок выше. Я обнаружил сложный способ, что перегрузка того же имени функции, в котором дифференциатор возвращал необязательный параметр, была слишком подвержена ошибкам, так как вызывающий контекст мог ввести ожидание, что это было необязательным.источник
источник
var1
и перейти к выводу типа; как только какое-то другое выражение определяет типvar1
использования, который используется для выбора правильная реализация. Суть в том, что случай, когда вывод типа нетривиален, редко доказывает, что точка, отличная от вывода типа, обычно нетривиальна.method1
должен быть объявлен для возврата определенного типа.