[Miesiąc Programowania:] Najpopularniejszy język programowania jest nadal słabo rozumiany. Wszystko przez walkę klas

Dodane:

Katarzyna Mazur Katarzyna Mazur

Udostępnij:

W dzisiejszych czasach liczy się każdy rodzaj przewagi na rynku, zwłaszcza w przypadku startupów, których produktem jest nowoczesne oprogramowanie i dla których czas dotarcia na rynek jest fundamentalnym czynnikiem decydującym o sukcesie lub porażce.

Zawód programisty od kilku lat owiewają liczne legendy, mity i plotki. Wśród nich są tematy zarobków, zakresu zadań, używanych technologii, czasu potrzebnego na przygotowanie do zawodu czy sposobów nauki programowania.

Postanowiliśmy odczarować tę profesję i wspólnie z serwisem Antyweb zorganizowaliśmy akcję „Miesiąc Programowania”. Przez cały miesiąc będziemy publikować wysokiej jakości materiały skierowane do programistów oraz wszystkich osób, które chciałyby rozpocząć swoją przygodę z programowaniem.

Tu znajdziesz je wszystkie.

fot. unsplash.com

Rozległa znajomość zaawansowanych zagadnień dotyczących składni i paradygmatów wspieranych przez różne języki programowania ma niezwykłe znaczenie w procesie rekrutacji programistów do najbardziej zaawansowanych zespołów. Takich, których poziom wiedzy i doświadczenia niezbędny jest wszystkim nowoczesnym startupom do odniesienia sukcesu. Na tak wysokim poziomie nie oczekujemy od kandydatów, że potrafią zaimplementować sortowanie bąbelkowe — to uważamy za pewnik — lecz tego, by potrafili wyjaśnić szczegółowo elementy danego języka, takie jak model obiektowy czy system typów, nie tylko rozumiejąc jak działa lecz — co ważniejsze — dlaczego twórcy danego języka zaimplementowali go tak a nie inaczej oraz jakie to niesie za sobą konsekwencje. Tylko w taki sposób możemy mieć pewność, że dobieramy najlepszych ludzi z branży, gotowych sprostać najwyższym oczekiwaniom klientów.

W tym artykule postaram się nakreślić pewne podstawowe pojęcia dotyczące programowania obiektowego, będącego jednym z wielu istotnych elementów, na które należy zwracać szczególną uwagę przy organizacji zaawansowanych zespołów i przy wyborze podwykonawców. Nie będę zagłębiał się w szczegóły techniczne wymagające doświadczenia w programowaniu, opowiem więcej o historii poszczególnych koncepcji niż o samym kodzie. Po przeczytaniu tego artykułu każdy powinien wiedzieć o co pytać programistę, by sprawdzić jego znajomość wiedzy o fundamentach programowania obiektowego i nawet jeśli niekoniecznie będzie w stanie zrozumieć wszystkie szczegóły jego odpowiedzi, to przynajmniej będzie w stanie ocenić to czy ów programista wie o czym mówi.

Wielu ludzi mówiąc dziś o programowaniu obiektowym myśli o językach takich jak Java, C# czy C++ lub innych, których obiektowość wywodzi się bezpośrednio z języka Simula, jednak nie jest to jedyny dziś używany model programowania obiektowego mimo, iż za taki często uchodzi, co nieustannie prowadzi do niezliczonych nieporozumień. Budując zespoły programistów w inFullMobile staramy się dobierać ludzi, którzy dobrze rozumieją technologie, w których pracują i jakkolwiek to samo w sobie nie jest z pewnością niczym niespotykanym, to jednak wiele firm skupia się wyłącznie na składni używanych języków podczas gdy my staramy się pamiętać również o semantyce i wspieranych paradygmatach, między innymi dotyczących programowania obiektowego, o którym dziś będzie mowa.

Początki programowania obiektowego

Pierwszym językiem programowania uważanym dziś za obiektowy jest Simula 67 opracowana w połowie lat sześćdziesiątych przez Ole-Johana Dahla i Kristena Nygaarda w Norweskim Ośrodku Obliczeniowym w Oslo, powstała poprzez dodanie między innymi nieznanych dotąd klas i obiektów do języka ALGOL 60. Za wprowadzenie tych i innych pojęć leżących dziś u podstaw programowania obiektowego Dahl i Nygaard otrzymali prestiżową Nagrodę Turinga od ACM w roku 2001 a rok później Medal Johna von Neumanna od IEEE i niewątpliwie powstanie języka Simula 67 stanowi przełom w historii informatyki, jednak wbrew powszechnie obowiązującej opinii nie był to bynajmniej koniec rozwoju obiektowego paradygmatu.

Kolejnym istotnym punktem zwrotnym było mianowicie powstanie języka Smalltalk-80 opracowanego przez Alana Kaya w latach siedemdziesiątych w słynnym Palo Alto Research Center, ośrodku badawczym należącym do firmy Xerox, znanym lepiej jako Xerox PARC. Od okienkowych systemów GUI, procesorów tekstu typu WYSIWYG, po sieć Ethernet i drukarki laserowe — znaczna część technologii, których dziś używamy, powstała w latach siedemdziesiątych i osiemdziesiątych w PARC, dlatego też na wszystko wymyślone tam zwłaszcza w tych latach należy zwrócić szczególną uwagę, w tym również na nowe podejście do programowania obiektowego wypracowane przez Alana Kaya w wyniku prac nad Smalltalkiem.

C++ kontra Objective-C, Self i Smalltalk

Pomimo to Bjarne Stroustrup, odrzuciwszy część wniosków Alana Kaya czym zapoczątkował konflikt trwający do dziś, stworzył na początku lat osiemdziesiątych język C++ poprzez dodanie obiektowych elementów Simuli do języka C, co dało początek całej grupie języków takich jak Java, C# czy F#. Ale mniej więcej w tym samym czasie co C++ powstał również inny język oparty na C, w którym Tom Love and Brad Cox wprowadzili elementy obiektowe pochodzące wprost z języka Smalltalk. W ciągu kolejnych lat C++ zyskało większą popularność niż Objective-C, które to stało się językiem używanym do dziś głównie do programowania w środowiskach stworzonych przez firmę Apple.

Co prawda Alan Kay zawsze wyrażał pewną pretensję do Steve’a Jobsa, że wykorzystał on w Apple wszystkie uzupełniające się wzajemnie technologie opracowane wspólnie jako pewna całość w Xerox PARC — od systemu GUI opartego na oknach i ikonach po system obiektów z języka Smalltalk — poza jednak samym językiem Smalltalk, co według Kaya było dużym błędem, jednak nawet jeśli składnia Smalltalka pozostaje dziś w dużej mierze nieznana, to smalltalkowy system obiektów jest dziś dość popularny dzięki językowi Objective-C, choć już stopniowo traci na popularności za sprawą języka Swift, który obiekty traktuje bardziej podobnie do języków takich jak Java i C# a zatem wycofuje się poniekąd ze smalltalkowych korzeni, wracając z powrotem do Simuli.

Te dwa główne nurty oparte na wpływach Simuli i Smalltalka są dziś najczęściej dość dobrze rozumiane przez programistów. Jeśli nawet ktoś nie słyszał o Simuli to z pewnością zna C++, Javę lub C# i jego obiektowe intuicje są bardzo zgodne z modelem użytym w Simuli. Wielu członków naszego zespołu zna również Objective-C ze względu na doświadczenie w tworzeniu aplikacji mobilnych na system iOS a zatem również i model obiektów używany w Smalltalku jest dość dobrze znany, nawet dla tych, którzy na co dzień nie używają Smalltalka jako takiego.

Te dwa modele nie wyczerpują jednak złożoności obiektowych paradygmatów używanych dziś powszechnie. W Xerox PARC w roku 1986 powstał również inny język, dziś znacznie mniej znany niż Smalltalk. David Ungar i Randall Smith opracowali język Self oparty w dużej mierze na Smalltalku jednak wprowadzający zupełnie nową koncepcję prototypów zastępujących używane wcześniej klasy, zmieniając całkowicie system dziedziczenia obiektów. Różnica między dziedziczeniem opartym na klasach a dziedziczeniem opartym na prototypach ma znacznie dalej idące konsekwencje niż różnica między modelem Simuli i Smalltalka, z których oba używały klas. W języku Self nie ma klas, jednak jest to jak najbardziej obiektowy język, co wielu programistom może się wydawać dziś dziwne, ze względu na liczebną przewagę języków z systemem obiektów wywodzącym się z Simuli i Smalltalka. Należy tu podkreślić i za chwilę wyjaśni się dlaczego to tak istotne, że prototypy powstały jako udoskonalenie klas.

Java czy JavaScript, oto jest pytanie

W roku 1995 powstał pewien język, który niemal każdy wówczas zbagatelizował i wielu ludzi bagatelizuje do dziś. Język ten opracował Brendan Eich w firmie Netscape w ciągu zaledwie 10 dni, co jest dość niespotykanym osiągnięciem. Pozostałe opisywane tu języki powstawały zwykle latami stąd informacja, że powstały np. w połowie lat siedemdziesiątych — w przypadku tego języka możemy być znacznie bardziej precyzyjni, powstał on mianowicie w maju 1995 roku, pierwotnie nosząc nazwę Mocha, następnie LiveScript, po to by ostatecznie zmienić nazwę na JavaScript.

Ostateczna nazwa była wynikiem ówczesnej współpracy Netscape Communications z Sun Microsystems, który w tym czasie promował nowopowstały język Java i użycie nazwy JavaScript było uzasadnione względami marketingowymi, jednak wprowadziło niezwykle wiele nieporozumień. Dodatkowo, ze względu na to, iż słowo Java stanowi znak towarowy — kiedyś należący do Sun Microsystems a dziś do Oracle — Microsoft w pierwszych wersjach przeglądarki Internet Explorer wprowadzając język JavaScript użył nazwy JScript, by uniknąć stosowania zastrzeżonego znaku towarowego, a później w procesie standaryzacji przez Europejskie Stowarzyszenie Producentów Komputerów — European Computer Manufacturers Association, w skrócie ECMA, dziś używające nazwy Ecma International — język JavaScript opisano używając nazwy ECMAScript, również w obawie o używanie zastrzeżonych znaków towarowych w procesie standaryzacji.

Mocha, LiveScript, JavaScript, JScript, ECMAScript, JS, ES – ten sam język znany pod tak wieloma nazwami i kilkoma różnymi skrótami wystarczająco komplikuje sytuację, lecz na domiar złego najbardziej powszechnie przyjęła się ta nazwa, która jest najbardziej z nich wszystkich myląca — JavaScript.

Pierwszym z nieporozumień wynikającym z pierwszego członu nazwy JavaScript było powszechne założenie, że JavaScript ma cokolwiek wspólnego z Javą a w istocie poza zbliżoną składnią opartą na C i C++ oba te języki nie miały ze sobą prawie nic wspólnego, a już z pewnością nie powstawały jako dopełniające się narzędzia. Co więcej, Brendan Eich pierwotnie zamierzał użyć składni opartej na s-expressions znanych z języków Lisp i Scheme a dopiero później Netscape zdecydował by użyć łatwiejszej według nich dla początkujących programistów składni zbliżonej do C i C++, podobnie jak w Javie. Gdyby nie to, JavaScript w ogóle nie przypominał by Javy.

„Uboższa” wersja Javy

Drugim nieporozumieniem była powszechna opinia, że drugi człon nazwy JavaScript oznacza, iż jest to prostsza i uboższa wersja języka Java, przeznaczona do mniej poważnych zastosowań — do pisania prostych skryptów a nie prawdziwych programów. Stanowiło to błędny lecz powszechny wniosek, który nie pojawiłby się gdyby pozostawiono nazwę Mocha lub LiveScript.

W istocie język JavaScript, nawet w jego pierwotnej wersji, był pod wieloma względami znacznie bardziej zaawansowany niż Java. Do dziś, ponad dwadzieścia lat później, najnowsze wersje Javy nie dorównują nawet najstarszym wersjom JavaScriptu pod względem wsparcia programowania funkcyjnego. Dzisiejsze tzw. “lambdy” w Javie wprowadzone w roku 2014 nie stanowią prawdziwych domknięć leksykalnych w rozumieniu rachunku lambda, w przeciwieństwie do zwyczajnych funkcji w JavaScripcie dostępnych już w 1995 roku.

Próbując zrozumieć język JavaScript wielu ludzi zaczyna od zdefiniowania podobieństw i różnic do Javy lecz zamiast tego powinniśmy porównywać JavaScript do języków, do których JavaScript jest podobny nie tylko z nazwy. Zawsze uważałem JavaScript za połączenie składni C z semantyką języka Scheme, modelem zdarzeniowym z HyperCard oraz modelem obiektowym z języka Self — przy czym ten ostatni punkt jest chyba najsłabiej rozumiany praktycznie od początku istnienia języka JavaScript i nie widzę by przez ostatnie dwadzieścia kilka lat ten stan rzeczy uległ znaczącej poprawie.

Powszechna opinia, że JavaScript to uproszczona wersja Javy, oraz stosunkowo mała znajomość języka Self i modelu obiektowego opartego na prototypach, a w szczególności faktu, iż prototypy stanowią nowocześniejszy model w stosunku do klas, wszystko to razem złożyło się na to, że od początku powstania JavaScriptu nieustannie zarzuca mu się brak klas, co zdaniem wielu programistów nie znających modelu prototypów sprawia rzekomo, iż JavaScript nie jest językiem w pełni obiektowym, co oczywiście nie jest prawdą. Od początku domagano się od JavaScriptu by był bardziej jak Java, w szczególności domagano się wprowadzenia klas — nazywałem to zawsze walką klas (z prototypami) choć najczęściej owa walka prowadzona była przez ludzi nie znających wcale swego wroga, czyli prototypów.

W końcu pod wpływem nieustannych nacisków środowiska informatycznego, w wersji języka ECMAScript 2015, znanej bardziej jako ES6, czyli wersji zdefiniowanej w szóstym wydaniu standardu ECMA-262, wprowadzono słowo kluczowe “class” tak bardzo oczekiwane przez programistów próbujących upodobnić JavaScript do Javy.

Pierwotnie gdy dowiedziałem się o planach wprowadzenia klas do JavaScriptu byłem temu mocno przeciwny ponieważ zakładałem, że będzie to stanowić odejście od prototypów. Gdy dowiedziałem się jednak, że słowo kluczowe “class” w istocie nie wprowadza klas a jedynie stanowi dodatkową składnię służącą do prostszego definiowania prototypów, uznałem, że prowadzić to będzie do niezwykłych nieporozumień — i miałem zresztą rację. Nie przewidziałem jednak tego, że zadowoli to niemal wszystkich, którzy wcześniej domagali się wprowadzenia klas do języka pomimo to, iż ich postulat nie został nawet w najmniejszym stopniu spełniony, z czego najwyraźniej nie zdawali sobie sprawy.

Sytuacja istnienia w JavaScripcie słowa kluczowego “class” definiującego prototypy a nie klasy przypomina istnienie w języku SQL słowa kluczowego “table” definiującego relacje a nie tabele w relacyjnych bazach danych, z czego również mało kto zdaje sobie sprawę i o czym również staram się pisać od niemal dwudziestu lat bez większego skutku, jednak w przypadku JavaScriptu problem ten jest znacznie poważniejszy ponieważ używa go o wiele więcej ludzi niż języka SQL i ponadto języka JavaScript w przeciwieństwie do SQL nie da się w wielu zastosowaniach zastąpić niczym innym bez ponoszenia znacznych kosztów wynikających na przykład z konieczności transpilacji.

Dziś język JavaScript nadal nie ma klas, nigdy ich nie miał i podejrzewam, że nigdy ich mieć nie będzie, nie licząc najwyżej pewnych metod optymalizacji stosowanych wewnętrznie przez kompilatory JIT, jednak pomimo to większość ludzi nierozumiejących różnicy między różnymi modelami dziedziczenia w językach obiektowych niestety tego nie rozumie i usiłuje używać obiektów jakby klasy istniały — i trudno zresztą ich za to winić przy celowo, jak sądzę, mylącej składni.

Z jednej strony cieszy mnie fakt, iż ludzie myśląc, że mamy dziś w JavaScripcie klasy, przestali się domagać ich wprowadzenia ponieważ zniknęło dzięki temu zagrożenie cofnięcia języka do starszego modelu obiektów. Z drugiej jednak strony pracując na co dzień z zespołem programistów niezwykle ważne jest, by każdy rozumiał różnicę między klasami i prototypami a obecny stan rzeczy wprowadza wiele zamieszania. Jest to niestety wciąż temat słabo rozumiany i dlatego tak ważne jest, by zwracać na niego szczególną uwagę w procesach rekrutacyjnych, by brak zrozumienia zagadnień tak fundamentalnych wychwycić na możliwie najwcześniejszym etapie.  

Najpopularniejszy język programowania na świecie

JavaScript jest dziś najpopularniejszym językiem programowania na świecie. Dzięki projektom takim jak Node.js możliwe jest pisanie w JavaScripcie backendowego kodu działającego na serwerach a nie tylko w przeglądarkach. Najnowocześniejsze technologie Serverless Computing oraz FaaS — takie jak AWS Lambda, Google Cloud Functions, Cloud Functions for Firebase czy Azure Functions — wszystkie bez wyjątku zaczęły od wsparcia dla języka JavaScript i dopiero później wprowadziły kolejne języki do swojego repertuaru. Bazy danych takie jak MongoDB i CouchDB używają wewnętrznie języka JavaScript do rozszerzeń i zapytań, nawet jeśli same napisane są w innych językach. Format JSON wywodzący się z języka JavaScript (JSON oznacza JavaScript Object Notation) niemal do końca wyparł już XML w niektórych zastosowaniach takich jak REST, GraphQL i innych rodzajach API, a obsługiwany jest już we wszystkich używanych powszechnie językach programowania. Wprowadzone zostały takie pojęcia jak izomorficzny JavaScript i uniwersalny JavaScript by określić systemy używające tego języka na wielu różnych poziomach swojego stosu technologicznego.

JavaScript to zatem jeden z najważniejszych języków programowania i z każdym dniem zyskuje na znaczeniu, jednak do dziś jest pod wieloma względami wciąż słabo rozumiany. Jednym z jego najgorzej rozumianych elementów są omawiane wyżej prototypy.

Wydawałoby się, że koncepcja ta po ponad trzydziestu latach od powstania języka Self powinna być już dziś powszechnie znana przez wszystkich programistów jednak niestety tak nie jest. Mam nadzieję, że ten artykuł rzucił nieco światła na to zagadnienie i przyczyni się do poprawy tej sytuacji.

Walka klas (z prototypami) wciąż trwa choć jej forma ulega ciągłym zmianom. Jednak, aby tę walkę zrozumieć lub choćby dostrzec jej istnienie, a już w szczególności by wybrać stronę w tej walce, należy poznać jej istotę, czyli różnice między klasami i prototypami, do czego wszystkich bardzo zachęcam.

 —

Rafał Pocztarski

Node.js Developer w inFullMobile, członek Komitetu Certyfikacyjnego w Node.js Foundation i jedyny w Polsce laureat Złotego Medalu Node.js na Stack Overflow. Od dziecka interesuje się programowaniem i zgłębianiem zasad działania otaczającego go świata, czym chętnie dzieli się z każdym, kto nieopatrznie wykaże choćby cień zainteresowania.