Nie znam wszystkich języków programowania, ale jasne jest, że zwykle nie jest możliwe przeładowanie metody, biorąc pod uwagę jej typ zwracany (zakładając, że jej argumenty mają tę samą liczbę i ten sam typ).
Mam na myśli coś takiego:
int method1 (int num)
{
}
long method1 (int num)
{
}
To nie jest tak, że to duży problem z programowaniem, ale przy niektórych okazjach byłbym z tego zadowolony.
Oczywiście nie byłoby sposobu, aby te języki mogły to obsługiwać bez sposobu na odróżnienie wywoływanej metody, ale składnia tego może być tak prosta, jak coś takiego jak [int] method1 (num) lub [long] method1 (num) w ten sposób kompilator będzie wiedział, który będzie nazywany.
Nie wiem, jak działają kompilatory, ale nie wydaje się to takie trudne, więc zastanawiam się, dlaczego coś takiego zwykle nie jest wdrażane.
Jakie są powody, dla których coś takiego nie jest obsługiwane?
źródło
Foo
iBar
.Odpowiedzi:
Utrudnia to sprawdzanie typu.
Gdy zezwalasz na przeciążanie tylko na podstawie typów argumentów i zezwalasz tylko na dedukowanie typów zmiennych z ich inicjatorów, wszystkie informacje o typie płyną w jednym kierunku: w górę drzewa składniowego.
Jeśli zezwalasz na przesyłanie informacji o typie w obu kierunkach, na przykład poprzez dedukcję typu zmiennej na podstawie jej użycia, potrzebujesz solvera ograniczającego (takiego jak Algorytm W dla systemów typu Hindley – Milner), aby określić typ.
Tutaj musieliśmy pozostawić
x
typ zmiennej typu nierozwiązanego∃T
, a wszystko, co wiemy o tym, to to, że jest on analizowalny. Dopiero później, gdyx
zostanie użyty w konkretnym typie, mamy wystarczającą ilość informacji, aby rozwiązać ograniczenie i określić to∃T = int
, co propaguje informacje o typie w dół drzewa składni od wyrażenia wywołania dox
.Jeśli nie uda nam się określić typu
x
, albo ten kod również zostanie przeciążony (tak więc wywołujący określi typ), albo będziemy musieli zgłosić błąd dotyczący niejednoznaczności.Na tej podstawie projektant języka może stwierdzić:
Dodaje złożoności implementacji.
Powoduje spowolnienie sprawdzania typu - w przypadkach patologicznych wykładniczo.
Trudniej jest wytwarzać dobre komunikaty o błędach.
To zbyt różni się od status quo.
Nie mam ochoty go wdrażać.
źródło
Ponieważ to niejednoznaczne. Używając C # jako przykładu:
Jakiego przeciążenia powinniśmy użyć?
Ok, może to było trochę niesprawiedliwe. Kompilator nie może ustalić, jakiego typu użyć, jeśli nie powiemy tego w twoim hipotetycznym języku. Tak więc niejawne pisanie jest niemożliwe w twoim języku i są tam anonimowe metody i Linq ...
Co powiesz na ten? (Nieznacznie przedefiniowano podpisy, aby zilustrować ten punkt).
Czy powinniśmy użyć
int
przeciążenia, czyshort
przeciążenia? Po prostu nie wiemy, będziemy musieli to określić przy pomocy twojej[int] method1(num)
składni. Co jest trochę kłopotliwe, aby parsować i pisać szczerze.Chodzi o to, że jest zaskakująco podobna składnia do ogólnej metody w C #.
(C ++ i Java mają podobne funkcje).
Krótko mówiąc, projektanci języków postanowili rozwiązać problem w inny sposób, aby uprościć analizę i włączyć znacznie bardziej zaawansowane funkcje językowe.
Mówisz, że niewiele wiesz o kompilatorach. Gorąco polecam naukę gramatyki i parserów. Kiedy zrozumiesz, czym jest gramatyka bezkontekstowa, będziesz miał o wiele lepsze pojęcie o tym, dlaczego dwuznaczność jest złą rzeczą.
źródło
short
iint
.method
zwróciło skrót lub int, a typ zdefiniowano jako długi.long
/int
/short
dotyczy bardziej złożoności subtypingu i / lub niejawnych konwersji niż przeciążenia typu zwrotu. W końcu literały liczbowe są przeciążone na typie zwracanym w C ++, Java, C♯ i wielu innych, i to nie wydaje się stanowić problemu. Możesz po prostu stworzyć regułę: np. Wybrać najbardziej konkretny / ogólny typ.Wszystkie funkcje językowe zwiększają złożoność, więc muszą zapewniać wystarczającą korzyść, aby uzasadnić nieuniknione problemy, przypadki narożne i zamieszanie użytkownika, które powoduje każda funkcja. W przypadku większości języków ten po prostu nie zapewnia wystarczających korzyści, aby to uzasadnić.
W większości języków można oczekiwać, że wyrażenie
method1(2)
będzie miało określony typ i mniej lub bardziej przewidywalną wartość zwracaną. Ale jeśli zezwolisz na przeciążanie zwracanych wartości, oznacza to, że nie można powiedzieć, co to wyrażenie ogólnie oznacza, bez uwzględnienia otaczającego go kontekstu. Zastanów się, co się dzieje, gdy maszunsigned long long foo()
metodę, której implementacja kończy sięreturn method1(2)
? Czy powinno to wywołaćlong
przeciążenieint
powrotu lub przeciążenie powrotu, czy po prostu dać błąd kompilatora?Ponadto, jeśli musisz pomóc kompilatorowi, dodając adnotację typu zwracanego, nie tylko wymyślasz więcej składni (co podnosi wszystkie wyżej wymienione koszty związane z dopuszczeniem funkcji w ogóle), ale skutecznie robisz to samo, co tworzenie dwie metody o różnych nazwach w „normalnym” języku. Czy jest
[long] method1(2)
bardziej intuicyjny niżlong_method1(2)
?Z drugiej strony niektóre języki funkcjonalne, takie jak Haskell z bardzo silnymi statycznymi systemami typów, pozwalają na tego rodzaju zachowanie, ponieważ ich wnioskowanie o typach jest na tyle silne, że rzadko trzeba opisywać typ zwracany w tych językach. Ale jest to możliwe tylko dlatego, że języki te naprawdę egzekwują bezpieczeństwo tekstu, bardziej niż jakikolwiek tradycyjny język, a także wymagają, aby wszystkie funkcje były czyste i referencyjnie przejrzyste. To nie jest coś, co kiedykolwiek byłoby wykonalne w większości języków OOP.
źródło
To jest dostępny w Swift i działa dobrze tam. Oczywiście nie możesz mieć dwuznacznego typu po obu stronach, więc musisz wiedzieć po lewej.
Użyłem tego w prostym API do kodowania / dekodowania .
Oznacza to, że wywołania, w których znane są typy parametrów, takie jak
init
obiekt, działają bardzo prosto:Jeśli zwracasz szczególną uwagę, zauważysz
dechOpt
połączenie powyżej. Dowiedziałem się na własnej skórze, że przeciążenie tej samej nazwy funkcji, w której wyróżnik zwraca wartość opcjonalną, jest zbyt podatne na błędy, ponieważ kontekst wywołania może wprowadzić oczekiwanie, że jest ona opcjonalna.źródło
źródło
var1
i kontynuować wnioskowanie o typie, a gdy tylko inne wyrażenie określi typvar1
użycia do wyboru właściwe wdrożenie. W dolnej linii pokazanie przypadku, w którym wnioskowanie o typie nie jest trywialne, rzadko dowodzi, że punkt inny niż wnioskowanie o typie nie jest ogólnie trywialne.method1
należy zadeklarować, aby zwrócić określony typ.