Jakiś czas temu pisałem o historii wyjątków, która swój początek miała w pierwszej połowie lat 60. Dziś znów powrócimy do tej samej dekady i porozmawiamy sobie o języku, który wprowadził do programowania paradygmat obiektowy. Języku, w którym po raz pierwszy pojawiły się dobrze nam dziś znane klasy i obiekty, a także dziedziczenie i metody wirtualne.
Spis treści
Język do symulacji
Co jest tą prawdziwie rewolucyjną technologią? Jest nią Simula 67, znana też po prostu jako Simula. Zanim jednak przejdziemy do szczegółów, warto dowiedzieć się co nieco o jej poprzedniczce, czyli języku nazywanym obecnie Simula I. Było to dzieło dwóch norweskich informatyków – Kristena Nygaarda oraz Ole-Johan Dahl, którzy obrali sobie za cel stworzenie języka dedykowanego do pisania symulacji komputerowych. Stąd też wywodzi się jego nazwa, która bywa rozwijana jako Simulation language. Simula I została zaprezentowana w roku 1962 i od strony technicznej była jedynie rozszerzeniem ALGOLA, tyle że ukierunkowanym na symulacje. Zyskiwała jednak popularność i zaledwie cztery lata później Nygaard i Dahl rozpoczęli prace nad kolejnym językiem. Tym razem miał być to język ogólnego przeznaczenia – z tego powodu niektórzy rozwijają jego skrót jako Simple universal language [1].
Mimo ambicji, aby być językiem do różnych zastosowań, Simula 67 nie wyrzekła się swych korzeni i wciąż posiadała wbudowane pewne mechanizmy ułatwiające tworzenie symulacji, m.in. dość rozbudowaną klasę Simulation (jeśli jesteście ciekawi symulacyjnych aspektów Simuli, to polecam artykuł na ten temat dostępny na Two-Bit History [2]). Właśnie – klasę. Koncepcja ta nie została wprawdzie wymyślona przez autorów Simuli (zainspirowali się oni między innymi publikacją Tony’ego Hoare’a opublikowaną w biuletynie Algola [3][4]), jednak to oni jako pierwsi wcielili ją w życie, czyniąc ją elementem stworzonego przez siebie języka.
Początki obiektowości
Czym zatem są w Simuli klasy i jak się je definiuje? Spójrzmy na poniższy przykład:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
Begin Class Logger; Virtual: Procedure printError is procedure printError(message); Text message;; Begin End; Logger Class BasicLogger; Begin Procedure printError(message); Text message; Begin Outtext("[ERR] "); Outtext(message); Outimage; End; Outtext("--Creating BasicLogger--"); Outimage End; Logger Class VerboseLogger(module); Text module; Begin Procedure printError(message); Text message; Begin Outtext("Error occurred in module "); Outtext(module); Outtext(": "); Outtext(message); Outimage; End; Outtext("--Creating VerboseLogger--"); Outimage; End; Ref(Logger) myLogger; myLogger :- New BasicLogger; myLogger.printError("Invalid argument"); myLogger :- New VerboseLogger("MainModule"); myLogger.printError("Wrong credentials"); End |
Widzimy tutaj zdefiniowane trzy klasy: bazową klasę Logger
oraz dwie dziedziczące po nim klasy: BasicLogger
i VerboseLogger
. Posiadają one metodę wirtualną printError
, przyjmującą jeden argument typu Text
. Dodatkowo, klasa VerboseLogger
posiada parametr module
, który przekazujemy do niej przy tworzeniu jej obiektu. Obie klasy posiadają natomiast fragmenty kodu (linie 17-18 oraz 33-34) wykonywane automatycznie podczas tworzenia ich instancji, a zatem coś, co można porównać do konstruktora.
Po definicjach klas następuje fragment w kodu, w którym następuje ich użycie. Tworzony jest obiekt myLogger
, będący referencją typu Logger
. Dwukrotnie wywoływana jest na nim metoda printError
i, tak jak można się tego spodziewać, za każdym razem wyświetla ona żądaną wiadomość w inny sposób. Innymi słowy – typowy przykład polimorfizmu, wcale niemniej czytelny niż w dowolnym współczesnym wysokopoziomowym języku programowania.
Jeśli chcecie samodzielnie przetestować powyższy kod, można uczynić to przy użyciu kompilatora cim, instalując go lokalnie w swoim systemie, bądź przy pomocy któregoś z kompilatorów dostępnych onlnie, np. Tutorialspoint lub TIO. Radzę tylko nie przejmować się sporą ilością wyrzucanych przez te narzędzia ostrzeżeń – zazwyczaj nie wynikają one z błędów w kodzie zapisanym w Simuli, ale z implementacji kompilatora cim, który najpierw przekształca kod do kodu w języku C, a dopiero później kompiluje, używając gcc.
Dziedzictwo Simuli
Nie da się ukryć, że Simula jest dziś językiem wyłącznie historycznym, technologią, w której nie produkuje się już oprogramowania. Ale jest też faktem, że wywarła ona ogromny wpływ na wiele innych języków programowania i na sposób, w jaki programujemy współcześnie. Przykłady? Proszę bardzo:
- Bjarne Stroustrup, twórca języka C++, miał okazję używać Simuli – właśnie to doświadczenie zainspirowało go do stworzenia „C z klasami” [5].
- Również James Gosling, autor Javy, przyznaje, że Simula wywarła na niego ogromny wpływ [6].
- Smalltalk, pierwszy w pełni obiektowy język programowania, wiele swoich elementów zaczerpnął właśnie z Simuli [7].
W obliczu powyższych faktów można pokusić się, aby zadać pytanie – dlaczego Simula nie przetrwała próby czasu i nie stała się popularnym językiem? Wśród możliwych powodów takiego stanu rzeczy J.Sklenar podaje m.in. brak nowoczesnego IDE, zbyt duże skomplikowanie samego języka, małą liczbę publikacji na jego temat, brak wsparcia dla GUI oraz nieistnienie interfejsów [1].
Źródła
- Introduction to OOP in Simula, J. Sklenar, 1997.
- OOP Before OOP with Simula, Sinclair Target, 2019.
- The Roots of Object-Oriented Programming: Simula 67, Ole-Johan Dahl, 2002.
- Record Handling, C.A.R. Hoare, ALGOL Bulletin no. 21, 1965.
- History of C++.
- Before C, What Did You Use?
- Dr. Alan Kay on the Meaning of “Object-Oriented Programming”.
- Concepts and terminology in the Simula Programming Language, Stein Krogdahl, 2010.
- Kompilator Simula 67 – cim.
- The Simula Programming Laguage, Ted Felix.
- OO History: Simula and Smalltalk.
- The birth of Simula, Strein Krogdahl, 2003.
- RosettaCode: przykładowe kody źródłowe w Simuli.