Opracowuję statycznie i silnie typowany, skompilowany język i ponownie zastanawiam się nad tym, czy włączyć funkcję przeciążania funkcji jako funkcję języka. Uświadomiłem sobie, że jestem trochę stronniczy, pochodząc głównie z C[++|#]
tła.
Jakie są najbardziej przekonujące argumenty za i przeciw uwzględnieniu przeciążenia funkcji w języku?
EDYCJA: Czy nie ma nikogo, kto ma przeciwne zdanie?
Bertrand Meyer (twórca Eiffla w 1985/1986) wywołuje metodę przeciążającą to: (źródło)
mechanizm próżności, który nie wnosi nic do semantycznej potęgi języka OO, ale utrudnia czytelność i komplikuje zadanie każdego
Są to teraz pewne ogólne uogólnienia, ale jest on mądrym facetem, więc myślę, że można bezpiecznie powiedzieć, że mógłby je w razie potrzeby poprzeć. W rzeczywistości prawie przekonał Brada Abramsa (jednego z programistów CLSv1) do przekonania, że .NET nie powinien obsługiwać przeciążania metod. (źródło) To kilka potężnych rzeczy. Czy ktoś może rzucić nieco światła na jego myśli i czy jego punkt widzenia jest nadal uzasadniony 25 lat później?
źródło
Polecam przynajmniej mieć świadomość klas typów w Haskell. Klasy typów zostały stworzone, aby być zdyscyplinowanym podejściem do przeciążania operatorów, ale znalazły inne zastosowania i do pewnego stopnia sprawiły, że Haskell jest tym, czym jest.
Na przykład, oto przykład przeciążenia ad-hoc (niezupełnie prawidłowy Haskell):
A oto ten sam przykład przeciążenia klasami typów:
Minusem jest to, że trzeba wymyślić z funky nazw dla wszystkich typeclasses (jak w Haskell, masz dość abstrakcyjny
Monad
,Functor
,Applicative
jak również prostsze i bardziej rozpoznawalneEq
,Num
iOrd
).Zaletą jest to, że gdy znasz już klasę, wiesz, jak używać dowolnego typu w tej klasie. Ponadto łatwo jest chronić funkcje przed typami, które nie implementują potrzebnych klas, jak w:
Edycja: Jeśli w Haskell chcesz
==
operatora, który akceptuje dwa różne typy, możesz użyć klasy typu wieloparametrowego:Oczywiście jest to prawdopodobnie zły pomysł, ponieważ pozwala wyraźnie porównywać jabłka i pomarańcze. Możesz jednak rozważyć to
+
, ponieważ dodawanieWord8
doInt
naprawdę jest rozsądną rzeczą do zrobienia w niektórych kontekstach.źródło
(==) :: Int -> Float -> Bool
gdziekolwiek? (niezależnie od tego, czy jest to dobry pomysł, oczywiście)class Eq a ...
byłoby przetłumaczone na pseudo-C-rodzinęinterface Eq<A> {bool operator==(A x, A y);}
i zamiast używać kodu szablonowego do porównywania dowolnych obiektów, używasz tego „interfejsu”. Czy to prawda?==
być w innej przestrzeni nazw, ale nie pozwala na przesłonięcie. Pamiętaj, żePrelude
domyślnie dostępna jest pojedyncza przestrzeń nazw ( ), ale możesz zapobiec jej ładowaniu, używając rozszerzeń lub importując ją jawnie (import Prelude ()
nic nie importujePrelude
iimport qualified Prelude as P
nie wstawia symboli do bieżącej przestrzeni nazw).Zezwól na przeciążenie funkcji, nie możesz wykonać następujących czynności z opcjonalnymi parametrami (lub jeśli możesz, nie ładnie).
trywialny przykład zakłada brak
base.ToString()
metodyźródło
Zawsze wolałem parametry domyślne od przeciążenia funkcji. Przeciążone funkcje zazwyczaj wywołują „domyślną” wersję z domyślnymi parametrami. Po co pisać
Kiedy mogłem zrobić:
To powiedziawszy, zdaję sobie sprawę, że czasami przeciążone funkcje robią różne rzeczy, zamiast wywoływać inny wariant z domyślnymi parametrami ... ale w takim przypadku nie jest to zły pomysł (w rzeczywistości prawdopodobnie dobry pomysł), aby po prostu dać mu inna nazwa.
(Także argumenty słów kluczowych w stylu Pythona działają naprawdę ładnie z parametrami domyślnymi.)
źródło
Array Slice(int start, int length) {...}
z przeciążeniemArray Slice(int start) {return this.Slice(start, this.Count - start);}
? Tego nie można zakodować przy użyciu parametrów domyślnych. Czy uważasz, że należy im nadać inne nazwy? Jeśli tak, to jak byś je nazwał?indexOf(char ch)
+indexOf(Date dt)
na liście. Lubię też wartości domyślne, ale nie można ich zamieniać z typowaniem statycznym.Właśnie opisałeś Javę. Lub C #.
Dlaczego wymyślasz koło?
Upewnij się, że typ zwrotu jest częścią podpisu metody iprzeciążasz zawartość twojego serca, to naprawdę czyści kod, kiedy nie musisz mówić.źródło
EnumX.Flag1 | Flag2 | Flag3
. Jednak nie będę tego wdrażał. Gdybym to zrobił i typ zwrotu nie był używany, szukałbym typu zwrotuvoid
.Grrr .. niewystarczający przywilej, aby komentować jeszcze ..
@Mason Wheeler: Pamiętaj więc o Adzie, która przeciąża typ zwrotu. Również mój język Felix robi to w niektórych kontekstach, szczególnie gdy funkcja zwraca inną funkcję i pojawia się wywołanie takie jak:
typ b może być użyty do rozwiązania w przypadku przeciążenia. Również C ++ przeciąża typ zwracany w niektórych okolicznościach:
W rzeczywistości istnieją algorytmy przeciążania typu zwracanego za pomocą wnioskowania typu. W rzeczywistości nie jest tak trudno zrobić z maszyną, problem polega na tym, że ludzie mają trudności. (Myślę, że kontur jest podany w Dragon Book, algorytm nazywa się algorytmem saw-saw, jeśli dobrze pamiętam)
źródło
Przypadek użycia przeciwko implementacji przeciążenia funkcji: 25 podobnie nazwanych metod, które robią te same rzeczy, ale z zupełnie innymi zestawami argumentów w szerokiej gamie wzorców.
Przypadek użycia przeciwko niezaimplementowaniu przeładowania funkcji: 5 podobnie nazwanych metod z bardzo podobnymi zestawami typów w dokładnie tym samym wzorcu.
Na koniec dnia nie mogę się doczekać przeczytania dokumentacji dotyczącej interfejsu API opracowanego w obu przypadkach.
Ale w jednym przypadku chodzi o to, co użytkownicy mogą zrobić. W innym przypadku użytkownicy muszą to zrobić z powodu ograniczeń językowych. IMO lepiej jest przynajmniej pozwolić, aby autorzy programów byli wystarczająco inteligentni, aby rozsądnie przeciążać bez powodowania dwuznaczności. Kiedy uderzysz ich w dłonie i zabierzesz opcję, w zasadzie będziesz gwarantować dwuznaczność. Bardziej ufam użytkownikowi, że zrobi dobrą rzecz, niż zakładam, że zawsze zrobi coś złego. Z mojego doświadczenia wynika, że protekcjonizm prowadzi do jeszcze gorszych zachowań społeczności językowej.
źródło
Zdecydowałem się zapewnić zwykłe przeciążanie i klasy typów w moim języku Felix.
Uważam (otwarte) przeładowanie za niezbędne, szczególnie w języku, który jako wiele typów liczbowych (Felix ma wszystkie typy liczbowe C). Jednak w przeciwieństwie do C ++, który nadużywa przeciążania poprzez uzależnianie szablonów od niego, polimorfizm Felixa jest parametryczny: potrzebujesz przeciążenia szablonów w C ++, ponieważ szablony w C ++ są źle zaprojektowane.
Klasy typów są również dostępne w Felix. Dla tych, którzy znają C ++, ale nie grokują Haskella, zignoruj tych, którzy opisują to jako przeciążenie. Nie przypomina to przeciążania, jest raczej specjalizacją od szablonów: deklarujesz szablon, którego nie implementujesz, a następnie zapewniasz implementacje dla konkretnych przypadków, gdy są potrzebne. Wpisywanie jest parametrycznie polimorficzne, implementacja odbywa się przez tworzenie instancji ad hoc, ale nie ma być nieograniczona: musi implementować zamierzoną semantykę.
W Haskell (i C ++) nie można podać semantyki. W C ++ idea „Koncepcji” jest z grubsza próbą przybliżenia semantyki. W Felixie możesz przybliżać intencję za pomocą aksjomatów, redukcji, lematów i twierdzeń.
Główny i jedyny zaletą (otwartego) przeciążenia w dobrze funkcjonującym języku, takim jak Felix, jest to, że ułatwia zapamiętanie nazw funkcji bibliotecznych, zarówno dla twórcy programu, jak i dla recenzenta kodu.
Podstawową wadą przeciążenia jest złożony algorytm wymagany do jego wdrożenia. Nie jest też zbyt dobrze oparty na wnioskowaniu typu: chociaż oba nie są całkowicie wykluczające, algorytm do wykonania obu jest wystarczająco złożony, programista prawdopodobnie nie byłby w stanie przewidzieć wyników.
W C ++ jest to również problem, ponieważ ma on niepoprawny algorytm dopasowywania i obsługuje również automatyczne konwersje typów: w Felixie „naprawiłem” ten problem, wymagając dokładnego dopasowania i braku automatycznych konwersji typów.
Myślę, że masz wybór: przeciążenie lub wnioskowanie typu. Wnioskowanie jest urocze, ale bardzo trudne do wdrożenia w sposób, który prawidłowo diagnozuje konflikty. Na przykład Ocaml mówi ci, gdzie wykrywa konflikt, ale nie, skąd wywnioskował oczekiwany typ.
Przeładowanie nie jest dużo lepsze, nawet jeśli masz kompilator jakości, który próbuje powiedzieć ci wszystkich kandydatów, może być trudno odczytać, czy kandydaci są polimorficzni, a nawet gorzej, jeśli jest to hackery szablonów C ++.
źródło
Wszystko sprowadza się do kontekstu, ale myślę, że przeciążenie sprawia, że klasa jest znacznie bardziej przydatna, gdy używam jednej napisanej przez kogoś innego. Często kończy się to mniejszą redundancją.
źródło
Jeśli chcesz mieć użytkowników zaznajomionych z językami rodziny C. Tak, powinieneś, ponieważ użytkownicy będą się tego spodziewać.
źródło