Jakiś czas temu, przy okazji opisywania podstaw Rails, starałem się wyjaśnić czym są klasy i obiekty. Jednak takie mieszanie poziomów ogólności w jednym wpisie może sprawiać, że całość wyda się mało przystępna dla początkujących i nudna dla obeznanych z tematem. Napiszę więc krótko o programowaniu obiektowym dla wszystkich tych, którzy znają już jakieś inne imperatywne, ale nieobiektowe języki programowania, a chcą dowiedzieć się, czym są obiekty i klasy.

Clojure to funkcyjny język programowania ogólnego przeznaczenia bazujący na modelu Lisp–1. Jego wzorcowa implementacja działa pod kontrolą maszyny wirtualnej Javy, ale istnieją też wydania pracujące w innych środowiskach, na przykład popularny ClojureScript zaimplementowany w JavaScripcie. Clojure jest Lispem, który powstał z myślą o przetwarzaniu współbieżnym i korzystaniu z ekosystemu Javy.

Typy danych pozwalają klasyfikować wartości pod względem różnych cech i wykształcać relacje między tak powstałymi klasami. Programiście pomaga to generalizować lub uszczegóławiać operacje przeprowadzane na danych, a mechanizmom języka zarządzać pamięcią i wykrywać niektóre rodzaje błędów. W Clojure mamy do czynienia z kilkoma powiązanymi ze sobą systemami typów, które możemy rozszerzać, a wykorzystując ich polimorficzne mechanizmy jesteśmy w stanie abstrahować zarządzanie danymi i budować ujednolicone interfejsy wymiany informacji.

Wykonywanie współbieżne pozwala spożytkować moc obliczeniową więcej niż jednego procesora lub rdzenia, pomaga też w lepszym zarządzaniu czasem obliczeniowym, gdy niektóre operacje muszą oczekiwać na obsługę komunikacji międzyprocesowej lub podsystemu wejścia/wyjścia. Clojure proponuje przejrzystą i wygodną implementację wykonywania współbieżnego, wykorzystującą typy referencyjne i programową pamięć transakcyjną.

Agent to typ referencyjny podobny do Atomu. Dzięki niemu można wyrażać częste, niekoordynowane zmiany stanów współdzielonych tożsamości w asynchroniczny sposób. Wykorzystywane są na przykład do obsługi niezależnych zdarzeń czy jednokierunkowej komunikacji między komponentami realizującymi dostęp do jednego zasobu (równoległe pobieranie danych z sieci, zapis do jednego pliku przez wiele wątków itp.).

Ref to typ referencyjny, dzięki któremu można wyrażać częste i jednoczesne zmiany stanów wielu współdzielonych tożsamości w synchroniczny sposób. Wykorzystywany jest tam, gdzie kilka wartości wskazywanych przez referencje zależy od siebie i modyfikacja ich wszystkich powinna być atomowa (np. przekazywanie środków między rachunkami bankowymi). Do obsługi Refów intensywnie wykorzystywana jest programowa pamięć transakcyjna (STM).

Korzystanie z dodatkowych wątków i sterowanie wykonywaniem bieżącego pozwalają precyzyjnie zarządzać współbieżnym realizowaniem zadań. W Clojure możemy w tym celu użyć dodatkowych typów referencyjnych: Future, Promise i Delay. Istnieją również odpowiednie klasy Javy realizujące podobne cele, a nawet typ Volatile, który pozwala tworzyć szybkie choć niebezpieczne odpowiedniki zmiennych.

Makra to jeden z mechanizmów metaprogramowania – zbioru technik umożliwiających odczytywanie, tworzenie i modyfikowanie programów przez inne programy lub przez nie same. Są one jedną z charakterystycznych cech dialektów języka Lisp, pozwalając na przekształcanie kodu źródłowego programu zanim dojdzie do jego ewaluacji. Dzięki makrom jesteśmy budować tzw. języki dziedzinowe, dostosowane do wyrażania specyficznych rozwiązań problemów w zwięzły i przejrzysty sposób.

Polimorfizm to zbiór mechanizmów, dzięki którym te same konstrukcje języków programowania mogą być używane w odniesieniu do danych różnych rodzajów. Dzięki niemu możemy przetwarzać informacje z użyciem wyabstrahowanych, generycznych operacji, zyskując na czasie i czytelności kodu. Polimorfizm w Clojure to przede wszystkim obsługa wieloargumentowości, multimetod, protokołów i rekordów, a także korzystanie z interfejsów Javy.