Nell'implementare l'interprete Scheme si era deciso
di costruire tutte le espressioni come ConsSexp e poi distinguere il tipo
di operatore in base alla testa della ConsSexp, testa che costituiva l'operatore
stesso. Nel caso Prolog si è pensato di seguire l'altra via, ovvero
quella di definire una sottoclasse di ConsSexp per ogni operatore. Si è
costruita quindi un'intera gerarchia sottostante la classe ConsSexp. Questa
gerarchia è sostanzialmente basata su quattro rami:
uno per i termini, con radice la classe TermSexp,
sottoclasse di ConsSexp,
uno per gli operatori prefissi, con radice la classe PrefixSexp,
sottoclasse di TermSexp;
uno per gli operatori infissi, con radice la classe InfixSexp,
sottoclasse di TermSexp;
uno per gli operatori postfissi, con radice la classe PostfixSexp,
sottoclasse di TermSexp.
Da quanto scritto risulta più corretto dire che
in effetti la radice della gerarchia dedicata al Prolog è la classe
TermSexp più che ConsSexp. Infatti in Prolog ogni "istruzione" è
un termine, tuttavia per facilitare la scrittura del codice si introduce
la possibilità di costruire tramite operatori i termini corrispondenti
ad operazioni predefinite. Per capirci basti osservare che l'or fra due
termini atomici a e b non è altro che il termine di funtore ";"
applicato a 2 argomenti di nome a e b, ovvero è il termine ;(a,b),
però per comodità è data la possibilità di
utilizzare la scrittura equivalente a;b, in cui il ; è usato
come operatore infisso. Lo stesso discorso vale per tutti gli operatori,
compreso il ":-" con cui si avvia la risoluzione Prolog o si definiscono
clausole. Nel nostro caso abbiamo tenuti distinti i termini corrispondenti
agli operatori dagli altri. Gli altri sono tutti quelli costruiti scrivendo
una stringa seguita da una parentesi aperta (senza separatori in mezzo),
dall'elenco degli argomenti separati con virgole e da una parentesi chiusa.
Fra questi ve ne sono alcuni predefiniti, come clause/2 (ovvero
clause
a due argomenti), functor/2, atom/1, e molti altri. Per ognuno
dei termini predefiniti è stata definita una classe dedicata, costruita
come sottoclasse di TermSexp. Ogni termine definito dall'utente nasce come
TermSexp.
La scelta di usare una gerarchia dettagliata (e
numerosa) è stata fatta appositamente per evidenziare le differenze
con il caso Scheme in cui la gerarchia era assolutamente "spartana" in
quanto limitata a quattro classi ed in cui si faceva uso esclusivo della
ConsSexp per tutte le espressioni non atomiche. L'utilizzo di una classe
per ogni tipo di operazione consente in fase di costruzione del Visitor
di non dover utilizzare degli if per stabilire il tipo di operazione. Infatti
ogni operazione corrisponde ad una classe e quindi all'invocazione del
metodo accept() viene automaticamente chiamato (tramite il meccanismo del
double dispatch) il metodo che si occupa di quell'operazione.
Inizialmente si era pensato di costruire un interprete
Prolog semplificato, in cui fossero disponibili pochi operatori (solo
quelli essenziali) e pochi predicati predefiniti. In questo modo si sarebbero
costruite poche classi e quindi l'utilizzo della gerarchia con radice TermSexp
sarebbe stato semplice. Tuttavia in corso d'opera si è deciso di
aggiungere via via nuove funzionalità finendo per inserire quasi
tutti gli operatori e quasi tutti i termini predefiniti, così da
ottenere un interprete Prolog quasi completo. La conseguenza è
stata quella di avere una gerarchia piuttosto numerosa che ha provocato
qualche scomodità nell'utilizzo del Visitor. La scomodità
si manifesta allorché si voglia aggiungere un'operatore, poiché
in quel caso oltre che creare la nuova classe si deve aggiungere la dichiarazione
del metodo visit() nel Visitor astratto e la definizione dello stesso
metodo in ogni Visitor concreto, mentre nel caso Scheme era sufficiente
aggiungere un semplice if nel metodo applyPrimitive().