Temat: Po co to całe typowanie?
Ciekawy temat. Ja może nieco porzucę dyskusję n/t do czego nadaje się PHP a do czego Java - bo jest trochę bezcelowa i wrócę do początkowego pytania.
Generalnie teoria typów jest zagadnieniem z jednej strony bardzo skomplikowanym z drugiej nie tak znowu bardzo ścisłym. Pytanie, które początkowo zadał Konrad brzmiało 'Po co to (silne, w domyśle) typowanie' i po co określanie typów przy zmiennej. To są jednak dwa oddzielne zagadnienia.
W uproszczeniu systemy typów (oraz języki programowania) możemy podzielić na te ze słabym typowaniem oraz z silnym typowaniem. Te ostatnie dodatkowo dzielą się na dynamicznie typowane oraz statycznie typowane. Silne typowanie to zapewnienie tego, że dana posiada określony typ, który nie może być zmieniony w trakcie jej cyklu życia. I nic więcej. W jaki sposób się to dzieje to już cecha języka.
W części języków stosuje się mechanizm statycznego definiowania typów (np. Java, C#, ADA, C/C++, Haskell, OCaml, F#, Scala, Pascal etc.). W uproszczeniu typ jest weryfikowany na etapie kompilacji lub analizy składniowej. Jedne z tych języków mają te mechanizmy słabsze i z pewnymi wyjątkami (np. C/C++) a inne mocniejsze np. Haskell czy OCaml.
W części języków typ jest weryfikowany dynamicznie (w trakcie wykonywania programu). Np. w przypadku wszelkich dialektów Lispa, Smalltalk, Ruby, Python, JavaScript i dziesiątki innych języków etc. Może się wydawać, że te języki są bardzo podobne do języków ze słabym typowaniem (np. zwykle nie potrzeba w nich deklarować typu danych). Niemniej tak nie jest - dane *nie mogą zmieniać* typu w trakcie wykonania programu (zmienna liczbowa pozostaje zmienną liczbową). Aczkolwiek typ może być zwykle "rozszerzany" w trakcie działania programu.
Ciekawe jest to, że silne i statyczne typowanie *wcale* nie musi się wiązać z annotacją typów (określaniem typów i 'szumem w kodzie'). Wiele z nich implementuje bardzo zaawansowane mechanizmy 'type inference' (
http://en.wikipedia.org/wiki/Type_inference). Warto spojrzeć na Haskella czy OCamla (czy jego nowsze dialekty takie jak F#), czy Scalę. Zwykle kod w Haskellu wygląda mniej więcej tak:
sum a b = a + b
Jak widać wcale nie stosujemy tutaj określania typów. Co nie zmienia faktu, że nie możemy zrobić czegoś takiego.
sum "2" 3
Podobnie sprawa ma się np. w Scali:
val xs = List(1,2,3,4,5)
ale już nie możemy zrobić xs - 8
Powyższe przykłady dają pojęcie na temat zalet takiego podejścia.
Mocne typowanie:
* daje pewność, że operacje (np. funkcje) mogą być aplikowane jedynie dla właściwych operandów (parametrów). Pewność ta jest na etapie kompilacji lub analizy składniowej (statyczna weryfikacja typów) lub w runtime (dynamiczna).
* pozwala uniknąć całej klasy potencjalnych błędów składniowych już na etapie kompilacji (statycznie typowanie)
* pozwala na stosowanie wielu narzędzi statycznej analizy kodu (np. FindBugs, PMD, CheckStyle etc.). Oczywiście jest wiele narzędzi tego typu dla języków dynamicznych - jednak są one o niebo mniej wyrafinowane (np. CodeNarc)
* pozwala na budowanie bardzo zaawansowanych narzędzi refaktoryzacji (znowu są takie narzędzia również dla języków dynamicznych, ale ponownie *nieporównywalnie* mniej niezawodne i funkcjonalne.
Mają one również, jak zauważyłeś, kilka wad. Przede wszystkim zwykle (choć nie zawsze) kod jest mniej zwięzły. Czasami również po prostu zalety silnego typowania nie są tak ważne: choćby w przypadku rozwiązań ad-hoc czy prototypownia. Stąd zwykle języki skryptowe (Perl, PHP, bash) są słabo typowane.
Nie ma jednego rozwiązania "dobrego dla wszystkich". Zawsze należy dobrać narzędzie do problemu a nie odwrotnie.
Artur Karazniewicz edytował(a) ten post dnia 04.12.10 o godzinie 08:22