Znane dynamicznie typy języków nigdy nie pozwalają programistom określać typów zmiennych, a przynajmniej mają bardzo ograniczone wsparcie dla tego.
Na przykład JavaScript nie zapewnia żadnego mechanizmu wymuszania typów zmiennych, gdy jest to wygodne. PHP pozwalają określić niektóre rodzaje argumentów metoda, ale nie ma sposobu, aby korzystać z rodzimych typów ( int
, string
etc.) dla argumentów i nie ma sposobu, aby wymusić typy na cokolwiek innego niż argumentów.
Jednocześnie wygodniej byłoby mieć możliwość określenia w niektórych przypadkach typu zmiennej w języku dynamicznie wpisywanym, zamiast ręcznego sprawdzania typu.
Dlaczego są takie ograniczenia? Czy to z przyczyn technicznych / wydajności (przypuszczam, że jest tak w przypadku JavaScript), czy tylko z powodów politycznych (co, jak sądzę, w przypadku PHP)? Czy dotyczy to innych dynamicznie pisanych języków, których nie znam?
Edycja: postępując zgodnie z odpowiedziami i komentarzami, oto przykład wyjaśnienia: powiedzmy, że mamy następującą metodę w zwykłym PHP:
public function CreateProduct($name, $description, $price, $quantity)
{
// Check the arguments.
if (!is_string($name)) throw new Exception('The name argument is expected to be a string.');
if (!is_string($description)) throw new Exception('The description argument is expected to be a string.');
if (!is_float($price) || is_double($price)) throw new Exception('The price argument is expected to be a float or a double.');
if (!is_int($quantity)) throw new Exception('The quantity argument is expected to be an integer.');
if (!$name) throw new Exception('The name argument cannot be an empty string.');
if ($price <= 0) throw new Exception('The price argument cannot be less or equal to zero.');
if ($price < 0) throw new Exception('The price argument cannot be less than zero.');
// We can finally begin to write the actual code.
// TODO: Implement the method here.
}
Przy pewnym wysiłku można to przepisać jako (patrz także Programowanie kontraktów w PHP ):
public function CreateProduct($name, $description, $price, $quantity)
{
Component::CheckArguments(__FILE__, __LINE__, array(
'name' => array('value' => $name, 'type' => VTYPE_STRING),
'description' => array('value' => $description, 'type' => VTYPE_STRING),
'price' => array('value' => $price, 'type' => VTYPE_FLOAT_OR_DOUBLE),
'quantity' => array('value' => $quantity, 'type' => VTYPE_INT)
));
if (!$name) throw new Exception('The name argument cannot be an empty string.');
if ($price <= 0) throw new Exception('The price argument cannot be less or equal to zero.');
if ($price < 0) throw new Exception('The price argument cannot be less than zero.');
// We can finally begin to write the actual code.
// TODO: Implement the method here.
}
Ale ta sama metoda zostałaby zapisana w następujący sposób, gdyby PHP opcjonalnie akceptował typy rodzime dla argumentów:
public function CreateProduct(string $name, string $description, double $price, int $quantity)
{
// Check the arguments.
if (!$name) throw new Exception('The name argument cannot be an empty string.');
if ($price <= 0) throw new Exception('The price argument cannot be less or equal to zero.');
if ($price < 0) throw new Exception('The price argument cannot be less than zero.');
// We can finally begin to write the actual code.
// TODO: Implement the method here.
}
Który z nich jest krótszy do napisania? Który z nich jest łatwiejszy do odczytania?
źródło
Odpowiedzi:
Istotą pisania statycznego jest zdolność do statycznego udowodnienia, że twój program jest poprawny pod względem typów (uwaga: nie do końca poprawna pod każdym względem). Jeśli masz system statyczny przez cały czas, możesz wykryć błędy typu przez większość czasu.
Jeśli masz tylko częściowe informacje o typie, możesz sprawdzić tylko małe fragmenty wykresu połączeń, na których informacje o typie są kompletne. Ale poświęciłeś czas i wysiłek na określenie informacji o typie niekompletnych części, które nie mogą pomóc, ale mogą dać fałszywe poczucie bezpieczeństwa.
Aby wyrazić informacje o typie, potrzebujesz części języka, która nie może być zbyt prosta. Wkrótce przekonasz się, że takie informacje
int
to za mało; będziesz potrzebować czegoś takiegoList<Pair<Int, String>>
, a następnie typów parametrycznych itp. Może to być dość mylące, nawet w dość prostym przypadku Javy.Następnie musisz obsługiwać te informacje podczas fazy tłumaczenia i fazy wykonania, ponieważ głupotą jest sprawdzanie tylko błędów statycznych; użytkownik spodziewa się, że ograniczenia typu zawsze będą obowiązywać, jeśli w ogóle zostaną określone. Dynamiczne języki nie są zbyt szybkie, a takie kontrole jeszcze bardziej spowolnią wydajność. Język statyczny może z trudem sprawdzać typy, ponieważ robi to tylko raz; dynamiczny język nie może.
Teraz wyobraź sobie, że dodajesz i utrzymujesz to wszystko, aby ludzie czasami opcjonalnie korzystali z tych funkcji, wykrywając tylko niewielką część błędów typu. Nie sądzę, żeby było warto.
Istotą dynamicznych języków jest posiadanie bardzo małego i bardzo plastycznego frameworka, w którym można łatwo robić rzeczy, które są o wiele bardziej zaangażowane, gdy wykonywane są w języku statycznym: różne formy łatania małp, które są używane do metaprogramowania, kpiny i testowanie, dynamiczne zastępowanie kodu itp. Smalltalk i Lisp, oba bardzo dynamiczne, doprowadziły go do takiej skrajności, że wysyłały obrazy środowiska zamiast budować ze źródła. Ale jeśli chcesz się upewnić, że poszczególne ścieżki danych są bezpieczne dla typu, dodaj asercje i napisz więcej testów jednostkowych.
źródło
W większości dynamicznych języków można przynajmniej dynamicznie przetestować typ obiektu lub wartości.
Istnieją również statyczne wnioskowania, kontrolery i / lub moduły egzekwujące dla niektórych dynamicznych języków: np
A Perl 6 będzie obsługiwał opcjonalny system pisania ze statycznym pisaniem.
Ale wydaje mi się, że sedno jest takie, że wiele osób używa dynamicznie języków, ponieważ są one dynamicznie pisane, a dla nich opcjonalne pisanie statyczne jest bardzo „huczące”. I wiele innych osób korzysta z nich, ponieważ są „łatwe w użyciu dla programistów”, głównie w wyniku dynamicznego pisania wybaczającego natury. Dla nich opcjonalne pisanie jest czymś, czego albo nie zrozumieją, albo nie będą mieli kłopotów z użyciem.
Jeśli jesteś cyniczny, możesz powiedzieć, że opcjonalne pisanie statyczne oferuje najgorsze z obu światów. Zealot typu statycznego nie zapobiega wszystkim awariom typu dynamicznego. Dla fanów typu dynamicznego nadal jest to prosta kurtka ... choć paski nie są mocno napięte.
źródło
JavaScript planował dołączyć opcjonalne pisanie statyczne i wydaje się, że wiele dojrzałych języków dynamicznych zmierza w tym kierunku -
Powodem jest to, że kiedy pierwszy raz kodujesz, chcesz być szybki i dynamicznie pisać. Gdy kod jest już solidny, działa i ma wiele zastosowań (r), chcesz zablokować projekt, aby zmniejszyć liczbę błędów. (jest to korzystne zarówno dla użytkowników, jak i programistów, ponieważ ci pierwsi otrzymają kontrolę błędów podczas swoich wywołań, a drudzy nie spowodują przypadkowego uszkodzenia.
Ma to dla mnie sens, ponieważ zwykle stwierdzam, że na początku projektu jest za dużo sprawdzania typu, za mało na koniec jego życia, bez względu na to, jakiego języka używam;).
źródło
Obiekty Pythona zrobić mieć typ.
Typ określa się podczas tworzenia obiektu.
W rzeczywistości ręczne sprawdzanie typu w Pythonie jest prawie zawsze stratą czasu i kodu.
Pisanie kodu sprawdzającego typ w Pythonie jest po prostu złą praktyką.
Jeśli jakiś złośliwy socjopata zastosuje niewłaściwy typ, zwykłe metody Pythona zgłoszą zwykły wyjątek, gdy typ nie będzie odpowiedni.
Nie piszesz kodu, twój program nadal zawiesza się z
TypeError
.Są bardzo rzadkie przypadki, w których należy określić typ w czasie wykonywania.
Ponieważ nie jest to „ograniczenie”, pytanie nie jest prawdziwym pytaniem.
źródło
Przez większość czasu nie musisz, a przynajmniej nie na poziomie szczegółowości, który sugerujesz. W PHP operatory, których używasz, doskonale wyjaśniają, czego oczekujesz od argumentów; jest to trochę niedopatrzenie projektowe, ale PHP wyrzuci twoje wartości, jeśli to w ogóle możliwe, nawet jeśli przekażesz tablicę do operacji, która oczekuje łańcucha, a ponieważ rzutowanie nie zawsze jest znaczące, czasami otrzymujesz dziwne wyniki ( i właśnie tam przydatne są kontrole typu ). Poza tym nie ma znaczenia, czy dodasz liczby całkowite
1
i5
lub łańcuchy"1"
i"5"
- sam fakt, że używasz+
operator sygnalizuje PHP, że chcesz traktować argumenty jak liczby, a PHP będzie posłuszny. Ciekawą sytuacją jest otrzymywanie wyników zapytań z MySQL: Wiele wartości liczbowych jest po prostu zwracanych jako ciągi, ale nie zauważysz, ponieważ PHP rzuca je za ciebie, ilekroć traktujesz je jako liczby.Python jest nieco bardziej rygorystyczny w swoich typach, ale w przeciwieństwie do PHP, Python od początku miał wyjątki i konsekwentnie go używa. Paradygmat „łatwiej prosić o wybaczenie niż pozwolenie” sugeruje po prostu wykonanie operacji bez sprawdzania typu i poleganie na wyjątku zgłaszanym, gdy typy nie mają sensu. Jedynym minusem tego, o czym myślę, jest to, że czasami zauważysz, że gdzieś typ nie pasuje do tego, czego się spodziewasz, ale znalezienie przyczyny może być żmudne.
I jest jeszcze jeden powód do rozważenia: języki dynamiczne nie mają etapu kompilacji. Nawet jeśli masz ograniczenia typu, mogą one uruchamiać tylko w czasie wykonywania, po prostu dlatego, że nie ma czasu kompilacji . Jeśli mimo to kontrole prowadzą do błędów w środowisku wykonawczym, znacznie łatwiej jest odpowiednio je modelować: jako jawne kontrole (takie jak
is_XXX()
w PHP lubtypeof
w javascript) lub poprzez zgłaszanie wyjątków (tak jak Python). Funkcjonalnie masz ten sam efekt (błąd jest sygnalizowany w czasie wykonywania, gdy sprawdzenie typu nie powiedzie się), ale lepiej integruje się z resztą semantyki języka. Po prostu nie ma sensu traktować błędów typu zasadniczo różniących się od innych błędów czasu wykonywania w języku dynamicznym.źródło
Haskell może Cię zainteresować - jego system typów odczytuje typy z kodu i możesz także określać typy.
źródło
Jak wspomniano w innych odpowiedziach, istnieją dwa podejścia do pisania przy wdrażaniu języka programowania.
Oba podejścia są prawidłowe, a ich zastosowanie zależy częściowo od względów technicznych, takich jak wydajność, a częściowo od przyczyn politycznych, takich jak docelowy rynek dla danego języka.
źródło
Po pierwsze, dynamiczne języki są tworzone głównie dla łatwości użytkowania. Jak już wspomniałeś, naprawdę dobrze jest automatycznie przeprowadzić konwersję typu i zapewnić nam mniejszy narzut. Ale jednocześnie brakuje mu problemów z wydajnością.
Możesz trzymać się dynamicznych języków, w przypadku, gdy nie martwisz się o wydajność. Powiedzmy na przykład, że JavaScript działa wolniej, gdy musi wykonać konwersję wielu typów w twoim programie, ale pomaga zmniejszyć liczbę wierszy w kodzie.
I należy wspomnieć, że istnieją inne dynamiczne języki, które pozwalają programiście określić typ. Na przykład Groovy jest jednym ze słynnych języków dynamicznych działających w JVM. I to było bardzo znane nawet w ostatnich dniach. Pamiętaj, że wydajność Groovy jest taka sama jak Java.
Mam nadzieję, że to ci pomoże.
źródło
Po prostu nie ma sensu tego robić.
Dlaczego?
Ponieważ system typów DTL jest dokładnie taki, że typów nie można określić w czasie kompilacji. Dlatego kompilator nie mógł nawet sprawdzić, czy określony typ ma sens.
źródło
Spójrz na Go, na powierzchni jest on statycznie wpisany, ale te typy mogą być interfejsami, które są zasadniczo dynamiczne.
źródło