Modularyzacja to coś oczywistego z perspektywy współczesnych języków programowania. Wiadomo przecież, że wrzucanie całego kodu to jednego pliku to coś, co sprawdza się jedynie na etapie programistycznej piaskownicy lub naprawdę niewielkich skrypcików.
Dla wielu osób zaskoczeniem może być więc fakt, że nie wszystkie implementacje Prologa w ogóle wspierają modularyzację. Wynika to z prostego powodu – Prolog jest już dość leciwym językiem (za rok będziemy obchodzić jego 50. urodziny) i powstawał w czasach, kiedy moduły wcale nie były taką oczywistością jak dziś.
Wiele implementacji Prologa przystosowało się do współczesnych standardów, dzięki czemu bez problemu możemy dziś używać modułów. Należy jednak pamiętać o tym, że nie jest to kwestia unormowana na poziomie języka, więc różne implementacje dostarczają odmiennych narzędzi do modularyzacji.
Dziś przyjrzymy się temu zagadnieniu na przykładzie trzech popularnych implementacji.
Spis treści
GNU Prolog
Jest to niestety sztandarowy przykład Prologa pozbawionego jakiegokolwiek sensownego wsparcia dla modularyzacji. Modułów w żaden sposób nie definiujemy, ponieważ po prostu ich nie ma w tej implementacji. [1]
Kod dobrze jest oczywiście podzielić na mniejsze pliki, ale jedyne co oferuje GNU Prolog to dyrektywa include/1
. Jej sposób działania jest dość prymitywny – po prostu wkleja ona całą treść załączanego pliku w miejsce użycia dyrektywy.
Nie istnieje więc żadna kontrola nad tym jakie predykaty eksportujemy bądź importujemy, nie istnieje obsługa cyklicznych importów – jednym słowem: słabo!
SWI Prolog
- Deklaracja modułu następuje przez użycie dyrektywy
module/2
na samym początku pliku. Nazwa modułu zwyczajowo jest identyczna jak nazwa pliku. Drugi argument to lista zdefiniowanych w module predykatów, które zostaną wyeksportowane.
1:- module(nazwa_modulu, [ pierwszy_predykat/2, drugi_predykat/1 ] ). - Importowanie modułów odbywa się przy pomocy dyrektyw
consult/1
lubuse_module/1
albouse_module/2
. Z uwagi m.in. na obsługę cyklicznych importów, używanieconsult/1
nie jest zalecane. [3] - Użycie predykatu
use_module(nazwa_modulu).
zaimportuje wszystkie predykaty, które zostały wyeksportowane z modułu. - Możemy zaimportować jedynie wybrane predykaty spośród wyeksportowanych, np.:
1:- use_module(nazwa_modulu, [ drugi_predykat/1 ]). - Eksportowanie predykatów nie jest odpowiednikiem (obecnego w innych językach programowania) oznaczania ich jako
public
. To jedynie niejako rekomendacja tego, jakie predykaty powinny z danego modułu importować inne pliki/moduły. - Nawet nie wyeksportowane predykaty można „na siłę” zaimportować, przy pomocy predykatu
import/1
. - Moduły mogą reeksportować zaimportowane predykaty. Albo przez dodanie ich do listy eksportowanych modułów w
module/2
, albo dzięki użyciu predykatyreexport/1
/reexport/2
, który łączy te dwie operacje – najpierw importuje podane predykaty, a następnie eksportuje je dalej. - Nie można importować predykatów o identycznych nazwach. Można za to importować predykaty, nadając im przy tym dowolne nazwy:
123:- use_module(my_module, [ predykat/2,inny_predykat/2 as foo]). - SWI Prolog pozwala nawet na dynamiczne tworzenie modułów i „wimportowywanie” do nich predykatów.
- Predykaty zdefiniowane w plikach bez modułów oraz tworzone w REPL-u należą do specjalnego modułu
User
.
SICStus Prolog
- Deklaracja modułu odbywa się bardzo podobnie jak w SWI Prologu – przy pomocy predykatu
module/2
- Importowanie modułów jest realizowane z użyciem predykatu
load_files/(1,2)
- Predykat
load_files/3
jako ostatni argument przyjmuje listę opcji, pozwalających dokonać chociażby importu warunkowego, np. tylko wtedy, kiedy moduł nie został jeszcze zaimportowany, albo został zmodyfikowany od czasu ostatniego importu:
1:- load_files(my_module, [if(changed)]). - W przypadku kolizji nazw podczas importowania modułów w trybie interaktywnym, nie otrzymujemy jedynie wiadomości z ostrzeżeniem, ale możemy wybrać sposób rozwiązania problemu. [2]
- Niezależnie od importowania, dowolnych predykatów zdefiniowanych w innych modułach można używać, posługując się prefiksami. Jest to jednak funkcjonalność przewidziana przede wszystkim do debugowania, a nie do użycia w środowisku produkcyjnym.
1my_module:my_predicate(arg).
Linki
- GNU Prolog – dokumentacja.
- SWI Prolog: Compilation of mutually dependent code.
- SICStus Prolog: Name Clashes.
- SICStus Prolog: The Module System.
- Using SWI-Prolog’s modules, M. Richter.
- SWI Prolog: Modules.