Najpierw chcę powiedzieć, że Java jest jedynym językiem, jakiego kiedykolwiek używałem, więc proszę wybaczyć moją ignorancję na ten temat.
Dynamicznie pisane języki pozwalają wstawić dowolną wartość do dowolnej zmiennej. Na przykład możesz napisać następującą funkcję (psuedocode):
void makeItBark(dog){
dog.bark();
}
I możesz przekazać w nim dowolną wartość. Tak długo, jak wartość ma bark()
metodę, kod będzie działał. W przeciwnym razie generowany jest wyjątek czasu wykonywania lub coś podobnego. (Proszę mnie poprawić, jeśli się mylę).
Pozornie daje to elastyczność.
Jednak przeczytałem trochę na temat języków dynamicznych, a ludzie mówią, że projektując lub pisząc kod w języku dynamicznym, myślisz o typach i bierzesz je pod uwagę, tak samo jak w języku statycznym.
Na przykład, pisząc makeItBark()
funkcję, zamierzasz akceptować tylko „rzeczy, które mogą szczekać”, i nadal musisz upewnić się, że przekazujesz do niej tylko takie rzeczy. Jedyną różnicą jest to, że teraz kompilator nie powie ci, kiedy popełniłeś błąd.
Jasne, istnieje jedna zaleta tego podejścia, polegająca na tym, że w językach statycznych, aby osiągnąć „ta funkcja akceptuje wszystko, co może szczekać”, trzeba zaimplementować jawny Barker
interfejs. Mimo to wydaje się to niewielką zaletą.
Czy coś brakuje? Co właściwie zyskuję, używając dynamicznie pisanego języka?
źródło
makeItBark(collections.namedtuple("Dog", "bark")(lambda x: "woof woof"))
. Ten argument nie jest nawet klasą , to anonimowa krotka. Wpisywanie kaczki („jeśli kwaknie jak ...”) pozwala ci na tworzenie interfejsów ad hoc z zasadniczo zerowymi ograniczeniami i bez narzutu składniowego. Możesz to zrobić w języku takim jak Java, ale kończy się to nieporęcznymi refleksjami. Jeśli funkcja w Javie wymaga ArrayList i chcesz nadać jej inny typ kolekcji, jesteś SOL. W pythonie nawet to nie może się pojawić.bark()
metodę, przy czym kompilator narzeka, gdy przekażesz coś złego, ale bez konieczności faktycznego deklarowania interfejsu zawierającego bark ().getMember
funkcji niestandardowej ,makeItBark
wysadza się w powietrze, ponieważ wywołałeśdog.bark
zamiastdog.getMember("bark")
. To, co sprawia, że kod działa, polega na tym, że wszyscy domyślnie zgadzają się używać rodzimego typu obiektu Pythona.Just because I wrote makeItBark with my own types in mind doesn't mean you can't use yours, wheras in a static language it probably /does/ mean that.
Jak wskazano w mojej odpowiedzi, tak nie jest w ogóle . Tak jest w przypadku Java i C #, ale te języki mają sparaliżowane systemy typów i modułów, więc nie są reprezentatywne dla tego, co może zrobić pisanie statyczne. Potrafię napisać doskonale ogólnymakeItBark
w kilku statycznie typowanych językach, nawet niefunkcjonalnych, takich jak C ++ lub D.Odpowiedzi:
Języki o typie dynamicznym są jednoznaczne
Porównując systemy typów , dynamiczne pisanie nie ma przewagi. Pisanie dynamiczne to szczególny przypadek pisania statycznego - jest to język o typie statycznym, w którym każda zmienna ma ten sam typ. To samo można osiągnąć w Javie (bez zwięzłości), ustawiając każdą zmienną na typ
Object
i ustawiając wartości „obiektowe” na typMap<String, Object>
:Tak więc, nawet bez refleksji, możesz osiągnąć ten sam efekt w prawie każdym statycznie wpisanym języku, pomijając wygodę składniową. Nie dostajesz żadnej dodatkowej mocy ekspresji; przeciwnie, masz mniej siłę wyrazu, ponieważ w dynamicznie wpisywanych języka, jesteś zaprzeczyć zdolność do ograniczania pewnych typów zmiennych.
Robienie kaczki z kory statycznie pisanym językiem
Co więcej, dobry język o typie statycznym pozwoli ci pisać kod, który działa z każdym typem, który ma
bark
operację. W Haskell jest to klasa typu:Wyraża to ograniczenie, że aby dany typ
a
mógł zostać uznany za barkalny, musi istniećbark
funkcja, która przyjmuje wartość tego typu i nic nie zwraca.Następnie możesz napisać funkcje ogólne pod względem
Barkable
ograniczenia:Mówi to, że
makeItBark
będzie działać na każdy typ spełniającyBarkable
wymagania. Może się to wydawać podobne dointerface
języka Java lub C #, ale ma jedną wielką zaletę - typy nie muszą z góry określać klas typów, które spełniają. Mogę powiedzieć, że ten typDuck
jestBarkable
w dowolnym momencie, nawet jeśliDuck
nie napisałem tego typu strony trzeciej. W rzeczywistości nie ma znaczenia, że autorDuck
nie napisałbark
funkcji - mogę podać ją później, gdy powiem językowi, któryDuck
spełniaBarkable
:To mówi, że
Duck
s mogą szczekać, a ich funkcja szczekania jest realizowana przez uderzenie kaczki przed jej kwakaniem. Dzięki temu możemy wezwaćmakeItBark
kaczki.Standard ML
iOCaml
są jeszcze bardziej elastyczne, ponieważ możesz zaspokoić tę samą klasę typów na więcej niż jeden sposób. W tych językach mogę powiedzieć, że liczby całkowite można zamówić za pomocą konwencjonalnego porządku, a następnie odwrócić się i powiedzieć, że można je również uporządkować według podzielności (np.10 > 5
Ponieważ 10 jest podzielne przez 5). W Haskell możesz utworzyć instancję klasy tylko raz. (Dzięki temu Haskell automatycznie wie, że można zadzwonićbark
do kaczki; w SML lub OCaml musisz wyraźnie określić, którejbark
funkcji chcesz, ponieważ może być więcej niż jedna).Zwięzłość
Oczywiście istnieją różnice składniowe. Przedstawiony kod Pythona jest o wiele bardziej zwięzły niż napisany przeze mnie odpowiednik Java. W praktyce zwięzłość ta jest dużą częścią uroku dynamicznie pisanych języków. Jednak wnioskowanie o typach pozwala pisać kod, który jest równie zwięzły w językach o typie statycznym, dzięki czemu nie musisz jawnie pisać typów każdej zmiennej. Język o typie statycznym może również zapewniać natywną obsługę dynamicznego pisania, usuwając szczegółowość wszystkich operacji rzutowania i mapowania (np. C #
dynamic
).Prawidłowe, ale źle napisane programy
Aby być sprawiedliwym, wpisywanie statyczne niekoniecznie wyklucza niektóre programy, które są technicznie poprawne, nawet jeśli moduł sprawdzania typów nie może tego zweryfikować. Na przykład:
Większość języków o typie statycznym odrzuciłaby to
if
oświadczenie, nawet jeśli gałąź else nigdy się nie pojawi. W praktyce wydaje się, że nikt nie korzysta z tego typu kodu - coś zbyt sprytnego dla sprawdzania typów prawdopodobnie sprawi, że przyszli opiekunowie twojego kodu będą przeklinać ciebie i twoich najbliższych. W tym przypadku ktoś z powodzeniem przetłumaczył 4 projekty Pythona o otwartym kodzie źródłowym na Haskell, co oznacza, że nie robili niczego, czego nie byłby w stanie skompilować dobry język o typie statycznym. Co więcej, kompilator znalazł kilka błędów związanych z typem, których nie wykryły testy jednostkowe.Najsilniejszym argumentem, jaki widziałem dla dynamicznego pisania, są makra Lispa, ponieważ pozwalają one dowolnie rozszerzyć składnię języka. Jednak Typed Racket to statycznie wpisany dialekt Lisp, który ma makra, więc wydaje się, że pisanie statyczne i makra nie wykluczają się wzajemnie, choć być może trudniej jest je jednocześnie wdrożyć.
Jabłka i Pomarańcze
Na koniec nie zapominaj, że istnieją większe różnice w językach niż tylko ich system typów. Przed Javą 8 wykonywanie jakiegokolwiek programowania funkcjonalnego w Javie było praktycznie niemożliwe; prosta lambda wymagałaby 4 wierszy anonimowego kodu klasy. Java również nie obsługuje literałów kolekcji (np
[1, 2, 3]
.). Mogą również występować różnice w jakości i dostępności narzędzi (IDE, debuggery), bibliotek i wsparcia społeczności. Jeśli ktoś twierdzi, że jest bardziej produktywny w Pythonie lub Ruby niż Java, należy wziąć pod uwagę tę rozbieżność funkcji. Istnieje różnica między porównywaniem języków ze wszystkimi dołączonymi akumulatorami , rdzeniami językowymi i systemami typów .źródło
To trudna i dość subiektywna kwestia. (Twoje pytanie może zostać zamknięte jako oparte na opiniach, ale to nie znaczy, że jest to złe pytanie - wręcz przeciwnie, nawet myślenie o takich metajęzykowych pytaniach jest dobrym znakiem - po prostu nie pasuje do formatu pytań i odpowiedzi tego forum).
Oto mój pogląd na to: celem języków wysokiego poziomu jest ograniczenie tego, co programista może zrobić z komputerem. Jest to zaskakujące dla wielu osób, ponieważ uważają, że ich celem jest zapewnienie użytkownikom większej mocy i więcej . Ale ponieważ wszystko, co piszesz w Prologu, C ++ lub Listie, jest ostatecznie wykonywane jako kod maszynowy, w rzeczywistości nie jest możliwe, aby dać programiście więcej mocy, niż zapewnia już język asemblera.
Istotą języka wysokiego poziomu jest pomoc programiście w zrozumieniu kodu, który sami stworzyli, oraz zwiększenie wydajności w wykonywaniu tego samego. Nazwa podprogramu jest łatwiejsza do zapamiętania niż adres szesnastkowy. Automatyczny licznik argumentów jest łatwiejszy w użyciu niż sekwencja wywołań, tutaj musisz uzyskać liczbę argumentów dokładnie na własną rękę, bez pomocy. System typów idzie dalej i ogranicza rodzaj argumentów, które możesz podać w danym miejscu.
Tutaj różni się postrzeganie ludzi. Niektórzy ludzie (jestem wśród nich) uważają, że dopóki procedura sprawdzania hasła i tak będzie oczekiwać dokładnie dwóch argumentów i zawsze ciąg znaków poprzedzony identyfikatorem numerycznym, przydatne jest zadeklarowanie tego w kodzie i automatyczne przypomnienie, jeśli później zapomnisz przestrzegać tej zasady. Outsourcing takich drobnych księgowości do kompilatora pomaga uwolnić umysł od problemów na wyższym poziomie i pozwala lepiej zaprojektować i zaprojektować system. Dlatego systemy typów to wygrana netto: pozwalają komputerowi robić to, w czym jest dobry, a ludzie robią to, w czym są dobrzy.
Inni traktują to zupełnie inaczej. Nie lubią, gdy kompilator mówi im, co mają robić. Nie podoba im się dodatkowy wysiłek związany z podjęciem decyzji w sprawie deklaracji typu i jej wpisaniem. Preferują eksploracyjny styl programowania, w którym piszesz rzeczywisty kod biznesowy bez planu, który powiedziałby dokładnie, jakie typy i argumenty użyć gdzie. I jeśli chodzi o styl programowania, którego używają, może to być całkiem prawda.
Oczywiście, nadmiernie upraszczam tutaj. Sprawdzanie typu nie jest ściśle powiązane z wyraźnymi deklaracjami typu; istnieje również wnioskowanie typu. Programowanie za pomocą procedur, które faktycznie biorą argumenty różnych typów, pozwala na całkiem różne i bardzo potężne rzeczy, które w innym przypadku byłyby niemożliwe, po prostu wiele osób nie jest uważnych i konsekwentnych, aby z powodzeniem korzystać z takiej swobody.
W końcu fakt, że tak różne języki są bardzo popularne i nie wykazują oznak wymarcia, pokazuje, że ludzie zajmują się programowaniem bardzo inaczej. Myślę, że funkcje języka programowania w dużej mierze dotyczą czynników ludzkich - co lepiej wspiera ludzki proces decyzyjny - i dopóki ludzie pracują bardzo inaczej, rynek zapewni jednocześnie bardzo różne rozwiązania.
źródło
Kod napisany przy użyciu języków dynamicznych nie jest sprzężony z systemem typu statycznego. Dlatego ten brak sprzężenia jest zaletą w porównaniu ze słabymi / nieodpowiednimi układami typu statycznego (chociaż może to być mycie lub wada w porównaniu z doskonałym układem typu statycznego).
Ponadto w przypadku języka dynamicznego system typu statycznego nie musi być projektowany, wdrażany, testowany i konserwowany. Może to uprościć implementację w porównaniu do języka ze statycznym systemem typów.
źródło