Dynamiczny język programowania

Ten artykuł dotyczy klasy języków programowania. Metoda na zmniejszanie złożoności obliczeniowej algorytmów o podobnej nazwie opisana jest w artykule Programowanie dynamiczne.

Dynamiczny język programowania jest terminem powszechnie używanym w informatyce oznaczającym klasę języków programowania wysokiego poziomu, które podczas działania programu wykonują wiele operacji przeprowadzanych w innych językach na etapie kompilacji. Do tych działań zalicza się na przykład rozszerzanie programu przez dodawanie nowego kodu, przez rozszerzanie obiektów i definicji, albo przez zmianę typów danych – wszystko podczas działania programu. Zachowania takie można emulować w niemal wszystkich językach programowania o wystarczającej złożoności, jednak języki dynamiczne mają wbudowane konstrukcje umożliwiające ich bezpośrednie wykorzystanie.

Języki dynamiczne i dynamiczne typowanie nie są tożsamymi pojęciami, a dynamiczny język programowania nie musi zawsze posiadać mechanizmu dynamicznej zmiany typów, chociaż w praktyce wiele z takich języków obsługuje tę właściwość.

Ograniczenia i wieloznaczności definicji

Definicja języka dynamicznego jest wieloznaczna, ponieważ próbuje się w niej dokonać rozróżnienia na kod i dane, podobnie jak na kompilację i uruchamianie, chociaż pojęcia te nie mają uniwersalnego znaczenia. Maszyny wirtualne, just-in-time compilation i zdolność wielu języków programowania do bezpośrednich modyfikacji kodu maszynowego w niektórych systemach, czynią wspomniane rozróżnienia bardzo abstrakcyjnymi. Na ogólnym poziomie, założenie, że język jest dynamiczny, jest naprawdę założeniem odnośnie do łatwego korzystania z dynamicznych mechanizmów, bardziej niż precyzyjnym określeniem możliwości danego języka.

Implementacje

Istnieje kilka mechanizmów wynikających z koncepcji programowania dynamicznego. Żaden z nich nie może być podstawą do sklasyfikowania języka jako dynamicznego, chociaż większość można znaleźć wśród tych języków.

Eval

Konstrukcja eval została wprowadzona w języku programowania Lisp i oznaczała ewaluację wyrażenia, czyli proces wykonywania instrukcji umieszczonych w strukturze danych zwanej tam wyrażeniem symbolicznym. Nowe znaczenie tego terminu odnosi się do mechanizmu lub procesu polegającego na wykonywaniu dowolnych instrukcji umieszczonych w łańcuchach tekstowych lub innych danych niebędących kodem maszynowym, do których program ma dostęp. Ewaluacja nowego kodu programu jest częstym zjawiskiem w wielu nowych językach programowania, które w przeciwieństwie do Lispa, nie wymagają od programisty rozróżniania procesu wczytywania łańcucha znaków i przekształcania go do postaci zinternalizowanej w celu wykonania na nim dalszych działań. Języki te to głównie języki interpretowane, gdzie z ewaluacją mamy w gruncie rzeczy do czynienia podczas każdorazowego wczytywania kodu w celu uruchomienia.

Funkcje wyższego rzędu

Erik Meijer i Peter Drayton zwracają uwagę, że każdy język programowania, w którym możliwe jest ładowanie kodu wykonywalnego podczas pracy programu jest w pewnym sensie zdolny do wykonywania ewaluacji, nawet jeśli kod ten jest w postaci dynamicznie ładowanej biblioteki współdzielonej lub maszynowej formy zrozumiałej przez procesor. Sugerują oni, że tak naprawdę dopiero obecność funkcji wyższego rzędu jest wyznacznikiem pozwalającym nazwać język dynamicznym, a niektóre z języków używają konstrukcji eval tylko jako ubogiej namiastki funkcji wyższego rzędu[1]

Zmiana obiektów podczas pracy

W języku dynamicznym typy danych lub system obiektów mogą być modyfikowane podczas działania programu. Może to oznaczać tworzenie nowych typów i obiektów o właściwościach wynikających z wyników umieszczonych w programie wyrażeń, albo spowodowanych użyciem domieszek istniejących obiektów i typów. Zmiana obiektów ma również miejsce podczas procesu dziedziczenia lub drzewiastego odwzorowywania typów, czyli zmiany zachowania zdefiniowanych już typów danych (ze szczególnym uwzględnieniem wywoływanych metod).

Programowanie funkcyjne

Koncepcje znane z programowania funkcyjnego zaimplementowano w wielu dynamicznych językach programowania. One również pochodzą z języka Lisp.

Domknięcia

Jednym z najpowszechniej używanych konstrukcji programowania funkcyjnego występujących w dynamicznych językach są domknięcia. Umożliwiają one tworzenie nowych instancji funkcji, które jednak nie tracą dostępu do danych kontekstu, w którym zostały utworzone. Prostym przykładem może być stworzenie funkcji służącej do wyszukiwania słowa w tekście:

function nowy_skaner (słowo)
  temp_function = function (wejście)
    szukaj_słowa (wejście, słowo)
  end function
  return temp_function
end function

Zauważ, że wewnętrzna funkcja jest anonimowa (nie ma nazwy), a miejscem jej przechowywania jest zmienna temp_function. Za każdym razem, gdy wywoływana jest funkcja nowy_skaner(), zwracana przez nią będzie nowa funkcja, która pamięta wartość przekazanego jej podczas definiowania argumentu słowo.

Domknięcia[2] są jednym z głównych narzędzi programowania funkcyjnego, a wiele języków obsługuje mechanizmy tego programowania przynajmniej na tym poziomie.

Kontynuacje

Kolejną cechą niektórych języków dynamicznych jest mechanizm kontynuacji. Konstrukcje kontynuacyjne pozwalają odzwierciedlić stan wykonywania się programu, który może być potem ponownie wywołany. Na przykład parser może zwracać wynik pośredni i kontynuację, która po wywołaniu sprawi, że przetwarzanie danych wejściowych będzie dalej prowadzone. Kontynuacje wpływają na to, jak traktowane są zasięgi zmiennych, funkcji i metod, ze szczególnym naciskiem na tzw. domknięcia. Ich użycie wymaga czujności programisty i ostrożności podczas implementowania przez twórców języka. Z tego powodu wiele języków dynamicznych nie obsługuje kontynuacji.

Mechanizm refleksji

Refleksja jest cechą wielu dynamicznych języków i zazwyczaj wiąże się z ogólnym procesem analizy typów i metadanych lub polimorfizmem danych. Może ona również oznaczać pełną ewaluację i modyfikację kodu programu reprezentowanego w strukturach danych, tak jak to ma miejsce w wyrażeniach symbolicznych języka Lisp.

Makra

Pewna ograniczona liczba dynamicznych języków programowania udostępnia mechanizmy, w których ewaluacja i introspekcja kodu łączą się w konstrukcję zwaną makrem. Większość programistów zna ten termin i używało makrodefinicji na przykład w języku C czy C++, gdzie pozwalają one na dokonywanie prostych podstawień łańcuchów stanowiących kod programu. W językach dynamicznych makra umożliwiają dostęp do wewnętrznych struktur kompilatora i pełny dostęp do interpretera, maszyny wirtualnej lub środowiska uruchomieniowego. W ten sposób programista jest w stanie wpływać na procesy związane z optymalizacją kodu i modyfikować składnię i gramatykę używanego języka.

Języki

Asembler, język C, C++, wczesne wydania Javy, a także FORTRAN nie są językami dynamicznymi.

Przypisy

  1. Meijer, Erik i Peter Drayton: Static Typing Where Possible, Dynamic Typing When Needed: The End of the Cold War Between Programming Languages. Microsoft Corporation, 2005.
  2. Obejrzyj przykład użycia na str. 330 książki Larry'ego Walla pt. Programming Perl ISBN 0-596-00027-8

Linki zewnętrzne

  • Dynamic Languages—ready for the next challenges, by design, David Ascher, PhD; ActiveState, July 27, 2004.