‟overcq”

Programowanie

Od serca krytyka współczesnego popularnego programowania

Przeglądam czasem strony ‘www’ w poszukiwaniu wyższej jakości artykułów związanych z informatyką, które rozwijałyby zagadnienia na szerokim poziomie w ujęciu danego tematu. I czasami znajduję je. W dzisiejszych czasach zdaje się wszyscy, którzy są w samych centrach rozwoju technologii, a przynajmniej mają siłę przebicia na rynku informatycznym i promocji wyników swojej pracy — angażują się w programowaniu głównie w języki obiektowe takie jak C+, Java, czy też powstały ‟.Net”. W takich dobrze napisanych artykułach na temat jakiegoś zagadnienia związanego z językiem obiektowym używane są masy przeróżnych pojęć wprowadzonych do informatyki jako angielskie słowa opisujące przez porówanie do tego co się widzi w rzeczywistości — to co ma miejsce w budowie danego programu lub składni języka programowania. Można oczywiście zacząć zapamiętywać, jak to sobie inni (‚wyższe sfery’ >:-| ) nazywają poszczególne operacje w programowaniu i następnie stać się wielkim ‚guru’ programowania obiektowego, operować słownictwem używanym w samym centrum rozwoju technologii informatycznych. Jak dla mnie to byłoby niepotrzebne obciążanie pamięci całym workiem nazw dla rzeczy, które mają swoje prawdziwe nazwy, które można stosować z zaznaczeniem punktu drobnej modyfikacji, którą się wprowadza. Ale nie. Teraz obowiązuje posiadanie marki i każda technologia podstawowa ma inną nazwę w zależności od tego, kto ją promuje we własnym zakresie. Kiedyś algorytmy — jak wszelkie osiągnięcia naukowe — otrzymywały nazwy od ich twórców co najwyżej, dzisiaj te same algorytmy są nazywane całkiem dowolnie w procesie, który polega na rozgałęzianiu pojedynczych technologii i odcięciu mostów prowadzących do równoległej gałęzi, chociaż ta równoległa realizuje to samo z drobnymi różnicami.

Wróciwszy do nazw czynności, które wykonuje obiektowy program lub są realizowane przez kompilator albo interpretator, napiszę co zauważam od razu, gdy padają wysoce skomplikowane opisy języka obiektowego. Głównym problemem języków obiektowych jest sposób realizacji przez nie obiektowości w postaci wiernego odwzorowywania w pamięci komputera opisów obiektów, które wymyślił programista, w postaci skompilowanej, czyli jedyne co zachodzi na drodze ‛źródło programu–postać wykonywalna’ to zamiana tekstowej postaci obiektów w postać zrozumiałą dla maszyny bądź w postaci języka danego procesora (np. C++), bądź w postaci języka wyimaginowanego procesora (np. Java). Obiekty opisane w źródle programu przez programistę mają tylko jedną zaletę z punktu widzenia programowania: mogą być tworzone i usuwane z pamięci na żądanie programisty umieszczone wcześniej w programie. Poza podaną możliwością nic wielkiego nie różni języki funkcyjne od obecnych języków obiektowych i tym samym jak dla mnie to języki obiektowe są zabawką współczesnych informatyków, która operuje tylko na innych pojęciach niż języki funkcyjne, a nie realizuje nic nowego. Tym samym uważam, że tylko sposób myślenia różni programistę tworzącego w języku obiektowym od tworzącego w języku funkcyjnym, ponieważ efekty działania programu są identyczne, a jedynie realizacja wyglądu danych programu w pamięci jest różna. Wymienione pojęcia związane z językami obiektowymi są bardzo proste do zrozumienia pod warunkiem, że widzi się oczami wyobraźni jak program w rzeczywistości jest przetwarzany w wyniku kompilacji. Wtedy po prostu widać, że obiektowość to jest taka fajna zabawka po to, by był cel utworzyć niezbędnego menedżera pamięci. Nie o to chodzi, że chcę skrytykować używanie języków obiektowych. Chcę natomiast sprowadzić na ziemię wszystkich, którzy wynoszą na piedestały języki obiektowe jako kolejny krok w rozwoju informatyki. Jedynym krokiem, jaki został poczyniony w wyniku powstania języków obiektowych, jest zgromadzenie ogromnych bibliotek algorytmów możliwych do użycia w takich językach. Natomiast sama funkcjonalność pomiędzy innego rodzaju językami nie uległa zmianie — być może na korzyść języków funkcyjnych, jeśli bierze się pod uwagę rozmiar wynikowego kodu wykonywalnego.

Dlaczego widzę taki wielki problem w związku z używaniem współczesnych języków obiektowych? Odtworzę tutaj jedną z moich zasad programowania w ujęciu tego zagadnienia. Mam na myśli to, że obecne języki obiektowe operują pojęciami abstrakcyjnymi na poziomie podstawowych struktur języka. Są oczywiście w danym języku obiekty nazwane specjalnie jako obiekty abstrakcyjne, ale to nie jest tego typu abstrakcja, całkiem inny sens. Te abstrakcyjne obiekty nie są wcale abstrakcyjne w zgodzie z definicją abstrakcji, którą umieściłem na mojej stronie ‘www’. Można by je nazwać jedynie obiektami prototypowymi lub obiektami nie zaimplementowanymi. Abstrakcja polega tutaj na tym, że podstawowe struktury języka są traktowane w taki sposób, jakby nie było żadnych elementów podstawowych, które wiążą fizyczną maszynę wykonującą polecenia z elementami programu, których używa programista. Mogę to tylko nazwać jednym określeniem: kompilacja dzisiejszych języków obiektowych nie zachodzi do końca, bo chyba jeszcze nikt nie dorósł do zrobienia takiego kompilatora, który zamiast ładować bezmyślnie i zwalniać obiekty z pamięci potraktuje obiekty z sensem założonym przez programistę.

Wynikiem kompilacji programu napisanego w języku obiektowym jest taki dziwny twór, który posiada wewnątrz instrukcje alokowania pamięci w zależności od jego potrzeb, czego dokonuje w miejscach starych jak same wielozadaniowe systemy operacyjne, czyli na stosie i stercie. Tym samym system operacyjny zatracił zdolność przewidzenia wymagań programu przed jego uruchomieniem i program zawsze musi mieć możliwość zrobienia rzeczy, których żaden program nie powinien mieć prawa robić — myślę tutaj o bezsensownym zajęciu zasobów systemu operacyjnego i zatrzymaniu go w ten sposób. Widać tutaj wyraźnie, że sposób rozwiązania obiektowości, jaki teraz istnieje, czyli odwzorowywanie wierne obiektów wymyślonych przez programistę w pamięci komputera — jest cofnięciem się na drodze współpracy programu z systemem operacyjnym. Zostają — mówiąc w najprostszych słowach — zatracone przed uruchomieniem programu granice między danymi a kodem wykonywalnym, ale te granice podczas wykonywania są tak samo ważne jak ważne były w programach nieobiektowych. To tak jakby uruchamiać coś, czego działania nie jest się w stanie przewidzieć, ale można ustawić strażnika, który zareaguje, gdy coś się stanie nagle — i tylko w tym wypadku, jeśli powoli to nie. Taki sposób kompilacji programów z języków obiektowych tworzy potrzebę istnienia narzędzi badających wykorzystanie pamięci przez program, których używa producent programu w czasie jego tworzenia. Mówię o narzędziach rozszerzających już sam ‘profiler’ kodu o badanie zachowania programu — nieprzewidywalnego w obszarze rozmiaru jednocześnie zaalokowanej pamięci.

Kolejne błędne rozwiązanie, które jest zastosowane w implementacji obiektowości, to alokowanie pamięci na stosie, który został wymyślony na dane lokalne, którymi są adresy powrotu z funkcji i zmienne tymczasowe. Taka organizacja umożliwiała łatwe profilowanie programu w celu ustalenia jego wymagań w czasie pracy i wprowadzenie modyfikacji optymalizujących jego działanie. Dla programów powstałych w wyniku kompilacji z języka obiektowego nie powstało nic specjalnego co miałoby przechowywać obiekty, ale mimo to w wyniku kompilacji te obiekty nie są tak naprawdę eliminowane do postaci zgodnej z architekturą komputera, ale są jedynie zamieniane na postać binarną (z tekstowej) i eliminowane są powtórzenia ewentualne; powstające instrukcje na niższym poziomie (czyli instrukcje procesora) są jeszcze szlifowane jak to miało miejsce w językach funkcyjnych.

Mógłbym tu wymieniać wiele negatywnych cech języków obiektowych — począwszy od zamieniania programisty w bajkopisarza bez talentu, który bez znajomości sprzętu ma pisać optymalne programy, w czym ci lepsi programiści ratują się wstawianiem do programu pisanego w języku obiektowym dodatkowych instrukcji, które korzystają ze sprzętu bezpośrednio i przyspieszają działanie programu z powodu użycia instrukcji, o których kompilator nie ma pojęcia — ale zamiast tego podam tylko moją ocenę. Uważam, że:

I rozmowa o kroku naprzód z powodu wymyślenia języków obiektowych to w rzeczywistości rozmowa o tym, że istnieją szeroko stosowane algorytmy kopiowania fragmentów programu do pamięci w sposób nieoptymalny z punktu widzenia danego celu programu. (Ale kogo to obchodzi? <:-( ).

Sęk w tym, że abstrakcja była i pozostała domeną człowieka, a języki obiektowe tworzą abstrakcję ustaloną już w swoich podstawach. Myślenie abstrakcyjne jest narzędziem człowieka (obecnie), które prowadzi do rozwiązania w określonych wypadkach. To nie jest natomiast środowisko pracy konkretnego programu, które by dawało coś ponad to, że na poziomie wykonywania programu wszystkie abstrakcje muszą być eliminowane i zastępowane fizyczną reprezentacją, ale już bez udziału człowieka, a sprzęt może to wykonać tylko bezmyślnie. To jest jakaś droga, by operować w programowaniu abstrakcjami na poziomie języka, a nie tylko w momencie projektowania, ale te abstrakcje muszą być rozumiane przez maszynę, bo inaczej (jak się to dzieje teraz) są one gubione i program powstaje gorszy niż wtedy, gdy programista utworzy z danego zestawu abstrakcji pojedyncze wyobrażenie realizowanego zadania.

Dużo tutaj napisałem i mam chociaż niewielką nadzieję, że czytający będzie świadomy na co się decyduje, gdy wybiera programowanie w obecnie istniejących obiektowych językach programowania, które są jak bańki mydlane na wietrze: każdy obraz może się w nich odbić, nie mają związku z rzeczywistością, ale każdy lubi dmuchać coraz to nowe, gdy się bawi.