dynamicznie typowane - czy ma jakieś plusy?

dynamicznie typowane - czy ma jakieś plusy?
superdurszlak
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Kraków
  • Postów: 2018
1
Aleksander32 napisał(a):

Co do tej redukcji ilości kodu jaki piszemy w dynamicznych językach to się trochę nie zgodzę. Tzn teoretycznie jak nie mamy inferencji typów to tak, trzeba pisac te długie nazwy, ale w praktyce mamy ctrl + spacje w IDE, więc jak ktos nie kodzi w notatniku to ten argument jest trochę inwalidą ;) Ogólnie cieszy mnie to że inferencja typów stała się popularna, więc argument o boilerplate staje się inwalidą.

Na inferencji typów świat się nie kończy ;)

Argument z IDE jest trochę inwalidą. Jeśli język jest upierdliwy bez IDE to znaczy, że jest upierdliwy, a IDE jedynie ratuje sytuację. Jasne, że w 2021 roku nikt raczej nie programuje w notatniku jeśli nie musi, ale zauważ - nie zawsze tak było:

  • nie zawsze istniały IDE z takimi możliwościami, jak współczesne
  • nie zawsze istniały IDE w ogóle
  • nawet, gdy istniały, niekoniecznie musiały wspierać dany język lub daną wersję lub standard
  • nawet, gdy wspierały akurat dany język, mogły nie być tak łatwo dostępne jak obecnie (przez koszt licencji, brak internetu, brak wiedzy o ich istnieniu)

Można tak wymieniać. Teoretycznie w 2021 roku to zupełnie nieistotne, ale z drugiej strony - jeśli każde prace i każdy skrypcik, który potrzebujesz wyrzeźbić na kolanie zaczynał od zainstalowania IDE / pluginów do vima (bo akurat masz "czystą" VMkę z terminalem lub dostałeś dostępy na serwer na którym nie ma nic) skonfigurowania projektu w IDE... byłoby to słabe. W takim scenariuszu taki Bash, Python czy Perl będzie miał sporą przewagę nad wielkim rzeźbieniem i żonglowaniem typami, bo kątrol spacja nie pomoże :P

S9
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Warszawa
  • Postów: 1092
1

@superdurszlak:

Na inferencji typów świat się nie kończy ;)

?

Teoretycznie w 2021 roku to zupełnie nieistotne, ale z drugiej strony - jeśli każde prace i każdy skrypcik, który potrzebujesz wyrzeźbić na kolanie zaczynał od zainstalowania IDE / pluginów do vima (bo akurat masz "czystą" VMkę z terminalem lub dostałeś dostępy na serwer na którym nie ma nic) skonfigurowania projektu w IDE... byłoby to słabe.

no tak, dynamiczne typowanie jest jeszcze OK to skryptów, ale w aplikacji bankowej jednak wolę silne typowanie i o to mi chodziło.

Jasne, że w 2021 roku nikt raczej nie programuje w notatniku jeśli nie musi, ale zauważ - nie zawsze tak było:

Ale ja nie twierdzę że zawsze tak było ;)

hauleth
  • Rejestracja: dni
  • Ostatnio: dni
0

Są miejsca, gdzie statyczne typowanie słabo się sprawdza. Dokładniej nie specjalnie widzę, jak można zapisać Erlangowe receive w języku statycznie typowanym bez uciekania się do haków w postaci Any (lub innego ekwiwalentu). Oczywiście, można w języku przyjąć, że "to nie powinno mieć miejsca", ale wtedy dalej potrzebujesz uściślić co ma się stać, jak jednak do czegoś takiego dojdzie, a tutaj już nie ma dobrego wyboru.

WeiXiao
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 5251
1

no dobre, a jeżeli mamy tak ogromną przewagę wad nad zaletami, to skąd wynika to, że kiedyś tworzono i pewnie nadal się tworzy języki dynamicznie typowane?

czy takie języki łatwiej zaimplementować? zapytałem o to kiedyś ludziuf którzy afaik mają trochę sensownego expa w tworzeniu m.in frontendów kompilatorów i stwierdzili że nie jest ani prościej, ani trudniej - po prostu inaczej, więc zatem o co chodzi?

S9
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Warszawa
  • Postów: 1092
1

no dobre, a jeżeli mamy tak ogromną przewagę wad nad zaletami, to skąd wynika to, że kiedyś tworzono i pewnie nadal się tworzy języki dynamicznie typowane?

No dlatego że różni ludzie mają różne podejście, dla niektórych Python jest czytelniejszy, dla innych może być to Java ;)

WeiXiao
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 5251
1

@Aleksander32:

no ale nie musisz mieć składni jak w javce int xd = 5 aby mieć silne typy, do pewnego stopnia mógłbyś to wyciągać tak, jak aktualnie robi to twój var

więc czy tak bardzo chodzi o składnie?

hauleth
  • Rejestracja: dni
  • Ostatnio: dni
3

@Spearhead: Rusta można zapisać ładniej

Kopiuj
use serde_json::Value;
use anyhow;

fn main() -> anyhow::Result<()> {
    let data = r#"{"a":{"b":{"c":[2,5,3,1,4]}}}"#;
    let json: Value = serde_json::from_str(data)?;
    let mut vec: Vec<_> = json["a"]["b"]["c"]
        .as_array()
        .into_iter()
        .flat_map(IntoIterator::into_iter)
        .flat_map(Value::as_i64)
        .collect();
    vec.sort();
    println!("{:?}", vec);
    
    Ok(())
}

Playground

Mamy i względnie ładną obsługę błędów jak i stosunkowo przejrzysty kod, który w 100% przechodzi typowanie.

hauleth
  • Rejestracja: dni
  • Ostatnio: dni
4

@WeiXiao:

skąd wynika to, że kiedyś tworzono i pewnie nadal się tworzy języki dynamicznie typowane?

Niektóre koncepcje łatwiej jest przedstawić bez typów niż z nimi. Przykładowo Erlangowe:

Kopiuj
Pid ! Data. % Wyślij wiadomość zawierającą `Data' do procesu z danym `Pid'em

Jest praktycznie nie do zaimplementowania w językach ze statycznym typowaniem (bez haków w postaci Any) jeśli chcemy zachować wszystkie cechy Erlanga (tzn., że Pid może być na osobnej maszynie względem wysyłającego).

Innym przykładem mogą być makra w Lisp-like. Wszystkie języki silnie typowane muszą "obchodzić" swoje ograniczenia w tej kwestii w mniej lub bardziej skomplikowany sposób i żaden z nich nie ma elegancji Lispowych makr.

Ostatnią rzeczą jest to, że w wielu przypadkach kod z dynamicznym typowaniem można pisać szybciej niż ze statycznym typowaniem, więc potrafi być to zaletą jak chcemy szybko dostarczyć produkt na rynek.


W skrócie:

  • Niektóre idee są trudniejsze do opisania typami (Erlang, Prolog czy Smalltalk są tego dobrymi przykładami)
  • Języki nastawione na "wygodę" dewelopera lub skryptowe (AWK, bash, Ruby, Python)
S9
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Warszawa
  • Postów: 1092
1

Ostatnią rzeczą jest to, że w wielu przypadkach kod z dynamicznym typowaniem można pisać szybciej niż ze statycznym typowaniem, więc potrafi być to zaletą jak chcemy szybko dostarczyć produkt na rynek.

A później wywali się w runtime zamiast podczas kompilacji, rzeczywiście spora oszczędność czasu ;] Oszczędnością czasu jest var/val itp konstukcje do inferencji typów. No i jak pisanie kodu większogo niż jakiś scrypt może być szybsze z dynamicznym typowaniem jak nie mamy takich podpowiedzi ze strony kompilatora i IDE (które może zgłupieć po prostu)?

superdurszlak
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Kraków
  • Postów: 2018
0

@Aleksander32: nie upraszczałbym tego aż tak, że języki są tworzone bo komuś bardziej podoba się taka lub śmaka składnia ;)

Raczej - przyjazna składnia (jak na swoje czasy) ułatwia popularyzację języka, ale w zasadzie jest tak naprawdę owocem mody, przekonań na temat tego co jest pożądane a co nie, ograniczeń kompilatora / interpretera, planowanego zastosowania. Przypuszczam, że Perl jest write-only nie przez widzimisię Larry'ego Wall'a tylko przez pragmatyzm - miał robić konkretną robotę, a nie służyć tworzeniu poważnego enterprajsowego oprogramowania rozwijanego latami przez setki programistów z gigantycznym codebase. I tak - przypuszczam - było z większością języków, które powstały w jakimś celu, a nie jako C++++ do szuflady z pozamienianymi słowami kluczowymi :P

hauleth
  • Rejestracja: dni
  • Ostatnio: dni
0
Aleksander32 napisał(a):

A później wywali się w runtime zamiast podczas kompilacji, rzeczywiście spora oszczędność czasu ;]

Zależy od tego jak te błędy są obsługiwane. Erlang ma systemy działające na poziomie stabilności dziewięciu dziewiątek, więc jak widać można to zrobić lepiej niż w językach ze statycznym typowaniem ;)

Oszczędnością czasu jest var/val itp konstukcje do inferencji typów.

Ma to swoje ograniczenia, jak np. Iterator::collect z Rusta wyżej, gdzie czasem musisz niezłą ekwilibrystykę odstawić by ta inferencja zadziałała.

No i jak pisanie kodu większogo niż jakiś scrypt może być szybsze z dynamicznym typowaniem jak nie mamy takich podpowiedzi ze strony kompilatora i IDE (które może zgłupieć po prostu)?

Zależy od języka. W Erlangu/Elixirze w większości to nie jest problem, bo masz funkcje przypisane do modułów, więc podpowiadanie w większości rzeczy można zrobić w "oczywisty" sposób. Dodatkowo jak masz krótkie moduły, to nie jest to specjalny problem by trzymać wszystko niejako "w pamięci".

Wibowit
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: XML Hills
2
hauleth napisał(a):

@WeiXiao:

skąd wynika to, że kiedyś tworzono i pewnie nadal się tworzy języki dynamicznie typowane?

Niektóre koncepcje łatwiej jest przedstawić bez typów niż z nimi. Przykładowo Erlangowe:

Kopiuj
Pid ! Data. % Wyślij wiadomość zawierającą `Data' do procesu z danym `Pid'em

Jest praktycznie nie do zaimplementowania w językach ze statycznym typowaniem (bez haków w postaci Any) jeśli chcemy zachować wszystkie cechy Erlanga (tzn., że Pid może być na osobnej maszynie względem wysyłającego).

Jest Akka Typed. Pewne rzeczy zmieniono względem Akki Classic (tej z nieotypowanym receive), ale dalej wygląda to jak stara Akka: https://doc.akka.io/docs/akka/current/typed/from-classic.html Jedną z podstawowych różnic jest brak metody sender w aktorze, a więc żeby odesłać wiadomość to w przychodzącej wiadomości musi być (otypowana) referencja do aktora, któremu odesłać wiadomość.

hauleth
  • Rejestracja: dni
  • Ostatnio: dni
0
Wibowit napisał(a):

Jest Akka Typed. Pewne rzeczy zmieniono względem Akki Classic (tej z nieotypowanym receive), ale dalej wygląda to jak stara Akka: https://doc.akka.io/docs/akka/current/typed/from-classic.html

A jak to działa, jeśli masz klaster? Bo wtedy wiadomość wysyłasz niejako "w próżnię" licząc na to, że po drugiej stronie będzie ktoś, kto potrafi daną wiadomość obsłużyć. Z języków na BEAM jest jeszcze Gleam, który ma silne typowanie, ale dalej w niektórych miejscach musi być "ucieczka".

Jedną z podstawowych różnic jest brak metody sender w aktorze, a więc żeby odesłać wiadomość to w przychodzącej wiadomości musi być (otypowana) referencja do aktora, któremu odesłać wiadomość.

W Erlangu nigdy sender nie był domyślnie częścią wiadomości, jak chcesz otrzymać odpowiedź, to musisz powiedzieć komu ją wysłać, np. Pid ! {self(), Data}.

No i zostaje ostatni aspekt - hot code reload. W Erlangu (i reszcie języków BEAM) można podmienić moduły w czasie działania programu bez potrzeby ubijania procesów. Dla implementacji serwerów w OTP jest wtedy odpalana magiczna funkcja code_change/3, która pozwala na zaktualizowanie stanu danego procesu do formatu wymaganego przez nową implementację.

W skrócie sprowadza się to do tego, że jeśli wprowadzimy pewne ograniczenia do konceptu, to można dodać statyczne typowanie, ale implementacja w 100% tego co oferuje Erlang ze statycznym typowaniem prędzej czy później napotkamy problemy. Oczywiście są type checkery dla Erlanga (Dialyzer jest nawet częścią OTP), ale one mają swoje ograniczenia (i obecna implementacja jest strasznie powolna).

Wibowit
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: XML Hills
2
hauleth napisał(a):

A jak to działa, jeśli masz klaster? Bo wtedy wiadomość wysyłasz niejako "w próżnię" licząc na to, że po drugiej stronie będzie ktoś, kto potrafi daną wiadomość obsłużyć.

Jest biblioteka akka-cluster-typed. Nie widziałem w dokumentacji żadnych ostrzeżeń o tym, by otypowany klaster nie działał, więc zakładam, że działa. Otypowana referencja ma takie same gwarancje co nieotypowana, z tą różnicą, że przy otypowanej masz pewność, że wysyłasz wiadomość, którą docelowy aktor jest w stanie rozpoznać. Działa to mniej więcej tak:

  • ActorX obsługuje wiadomości MsgX i podtypy
  • ActorY obsługuje wiadomości MsgY i podtypy
  • ActorX ma skądś referencję do ActorY, a więc ma TypedActorRef[MsgY], bo te referencje są otypowane typem akceptowanej wiadomości, a nie samego aktora
  • ActorX wysyła do ActorY wiadomośc MsgY1(data: SomeData, sender: TypedActorRef[MsgX]), gdzie do sender wstawia referencję do samego siebie
  • ActorY otrzymuje tą wiadomość i dzięki referencji z pola sender: TypedActorRef[MsgX] może odesłać do ActorX wiadomość np. MsgX1(data: SomeOtherData)

Jedną z podstawowych różnic jest brak metody sender w aktorze, a więc żeby odesłać wiadomość to w przychodzącej wiadomości musi być (otypowana) referencja do aktora, któremu odesłać wiadomość.

W Erlangu nigdy sender nie był domyślnie częścią wiadomości, jak chcesz otrzymać odpowiedź, to musisz powiedzieć komu ją wysłać, np. Pid ! {self(), Data}.

W Akce Classic sender był zawsze dostępny. Prawdopodobnie chęć zachowania tego typu funkcjonalności spowodowała wydłużenie wprowadzenia otypowanych aktorów. Kombinowano z różnymi podejściami, włącznie z czymś a'la RMI. Na szczęście zmieniono podejście i to ostateczne nawet mi się podoba.

No i zostaje ostatni aspekt - hot code reload. W Erlangu (i reszcie języków BEAM) można podmienić moduły w czasie działania programu bez potrzeby ubijania procesów. Dla implementacji serwerów w OTP jest wtedy odpalana magiczna funkcja code_change/3, która pozwala na zaktualizowanie stanu danego procesu do formatu wymaganego przez nową implementację.

W Akce jest pewnego rodzaju hot code reload (być może nie do końca spełnia definicję). Działa na zasadzie najpierw stawiania nowych wersji węzłów w klastrze (np. dodanie nowego węzła w klastrze OrdersService z kodem projektu OrdersService v25), a potem usuwania starych wersji (czyli tutaj usuwania węzłów w klastrze OrdersService z kodem projektu OrdersService v24). W ten sposób można płynnie przejść z jednej wersji mikroserwisu na kolejną, bez zatrzymywania go. Nie znam szczegółów, bo nigdy akkowego klastra nie stawiałem.

elwis
  • Rejestracja: dni
  • Ostatnio: dni
0

Myślę, że teoretycznie i abstrakcyjnie rzecz ujmując, dynamiczne typowanie nie jest lepsze od statycznego. Jednak w prawdziwym świecie nie ma języków idealnych, a rodzaj typowania nie jest jedyną cechą. Tak się ciekawie składa, że pomimo upodobania dla statycznego typowania, moje ulubione języki (Common Lisp, Perl) są dynamicznie typowane. Pewnie dlatego, że zwykle statyczne typowanie idzie w parze z jakąś niedogodnością. Pewnie wraz z rozwojem języków i teorii typów będzie się to niwelować. Wystarczy poczytać o Idrisie, żeby zobaczyć ile jest potencjału w statycznym typowaniu. Natomiast, do pewnych zadań (np. napisanie prostego skryptu czy hooka w dla gita) pewnie zawsze języki skryptowe będą wygrywać, przekonuję się o tym ilekroć piszę coś w tym okropnym bashu. :D

Escanor16
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 367
3

dynamiczne typowanie? Nie, dziękuje, wolę mieć kontrole nad swoimi typami

PK
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 245
0

Poniżej zostawiam ocene tego do jakiej postaci zbiega kod, gdy pisze się go statycznie typownym językiem. Taka moja amatorska próba oceny kiedy jedno typowanie jest lepsze / gorsze względem drugiego. Myśli próbowałem przełożyć w miarę prosto, tam gdzie nie byłem pewen dodałem przykłady.

W projekcie na początku doszukujemy się regularności w danych i oznaczamy je w kodzie jako typy.

W przypadku gdy dane są mniej regularne, zróżnicowane dochodzi do wyodrębnienia typu bazowego i typów pochodnych. Ekstra punkty zdobywamy jeśli typy są podzielone na kategorie, wówczas możemy napisać funkcje, które mogą tą logikę zastosować do zbliżonych typów (np. obsłużyć klasy ArrayList/LinkedList jako List lub też Collection).

Gdy pracujemy nad projektem, wybrane typy reprezentują modele, i w tutaj w tym podejściu główny koszt za ich wyznaczenie stanowi klepanie osobno pod każdy z nich obsługi różnych działań (takich jak zapis, odczyt, parsowanie, wyświetlenie i praktycznie wszystkie co obejmuje interakcje ze światem).

Przykladowo gdy mamy parsowanie to spotykamy klasę parser (funkcjonująca jako template method) a po niej dziedziczą wybrane parsery modeli. Każdy parser implementuje szczegóły związane z parsowaniem modelu o jakim mówi.

Myślę, że jeśli modele trzeba rozpatrywać osobno to taki podział sprzyja przede wszystkim zróżnicowanym implementacjom, takim które cięzej uogólnić.

Natomiast gdyby implementacje pod każdy typ były do siebie bardziej podobne to myślę, że wtedy ich ilość analogicznego kodu pociąga za sobą coraz bardziej rozdęty kod (bo mamy wiele parserów, które robią to samo). Oczywiście zbliżone operacje pewnie można rozwiązać przy użyciu adnotacji i refleksji (o ile taka opcja jest możliwa), ale oceniam to za trudniejsze podejście i myślę, wtedy ludzie bardziej szukają gotowych bibliotek lub wracają i rozpoczynają klepanie wszystkich parserów.

Stąd dochodzę do wniosku, że w przypadku języka ze statycznym typowaniem to podejście dobrze wpasowuje w sytuacje gdy mamy jednocześnie: regularne dane i bardzo zróżnicowane implementacje operacji względem modeli jakie występują w projekcie.

Chciałbym zapytać czy Wy też podobnie tak to widzicie?

LukeJL
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 8495
0

dynamicznie typowane - czy ma jakieś plusy?

Ma plusy - że nie ma takich problemów jak ten w Rust, że aż trzeba wprowadzać cały feature do języka po to, żeby użytkownik mógł sobie utworzyć i działać na tablicy o dowolnej liczbie elementów, bo w pewnych przypadkach system typów to utrudniał (For a long time, even the standard library methods for arrays were limited to arrays of length at most 32 due to this problem. :D
https://blog.rust-lang.org/2021/02/26/const-generics-mvp-beta.html

Więc w Rust musieli wprowadzić const generics z tego powodu: https://blog.rust-lang.org/2021/03/25/Rust-1.51.0.html#const-generics-mvp

Ogólnie więc nie jest tak, że statyczne typowanie to bajka, a dynamiczne typowanie to samo zło.

Ja to widzę tak: dynamiczne typowanie jest nowocześniejsze, łatwiej w nim pisać, ale jednocześnie komputer woli statyczne programowanie, bo łatwiej to kompilatorom optymalizować i analizować. I statyczne typowanie jest bardziej przewidywalne.

Dlatego stoimy przed wyborem - wybrać statyczne typowanie, gdzie trzeba będzie się trochę nagimnastykować, ale będziemy mieć pewne benefity, czy może wybrać dynamiczne typowanie, w którym będzie się wygodniej pisać, ale będziemy odczuwać też pewne bolączki.

Jednak kto wie, czy za 10-20 lat dynamiczne typowanie nie wróci do łask, jak powstaną lepsze narzędzia do obsługi takich języków.

Szekel
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 253
0

Ja od siebie dam tylko jeden plus.

Czasami skraca logikę i ją efektywnie upraszcza.

Weźmy taki przykład. Zmienna jest ok jeżeli jest klasą a jeżeli jest stringiem to oznacza błąd i od razu komunikat błędu (typowy rule pattern dla walidacji formatek).

W standardowych językach musisz zrobić klasę która ma przynajmniej dwa pola (obiekt i string z błędem).

Jest to nie tylko nadmiarowy kod a jednocześnie mniej wydajny (nie w każdym języku zapewne) bo musisz zarezerwować pamięć na dwa pola zamiast jednego.

Ale to jedyny use case dla jakiego imo warto.

Wibowit
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: XML Hills

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.