This commit is contained in:
Tibo De Peuter 2025-05-12 15:30:50 +02:00
parent a5110f5a9d
commit 5726f33d34
2 changed files with 106 additions and 34 deletions

View file

@ -33,21 +33,14 @@
% KERN: Programmeertaal, libraries, etc.
Ghent Prolog werd geschreven in Kotlin en maakt gebruik van zowel object-gerichte als functionele concepten.
Er werden twee bibliotheken gebruikt:
\begin{itemize}
\item \href{https://github.com/h0tk3y/better-parse}{\texttt{better-parse}}, voor het parsen van een gegeven grammatica,
\item \href{https://github.com/xenomachina/kotlin-argparser}{\texttt{kotlin-argparser}}, voor het gebruik van command-line argumenten.
\end{itemize}
Er werden twee bibliotheken gebruikt:
\href{https://github.com/h0tk3y/better-parse}{\texttt{better-parse}}, voor het parsen van een gegeven grammatica, en
\href{https://github.com/xenomachina/kotlin-argparser}{\texttt{kotlin-argparser}}, voor het gebruik van command-line argumenten.
% KERN: Hoe ziet Ghent Prolog er abstract uit?
De interpreter bestaat uit een aantal fases, die zo onafhankelijk mogelijk zijn om de implementatie modulair en uitbreidbaar te maken.
\begin{enumerate}
\item Lexing and parsing
\item Preprocessor
\item Programma en read-eval-print loop (REPL)
\end{enumerate}
De verschillende fases van een interpreter (lexing, parsing, evaluatie, etc.) zijn in Ghent Prolog zo goed mogelijk gescheiden.
Op die manier blijft de implementatie modulair en uitbreidbaar.
\section{Lexing, parsing en preprocessing}\label{sec:lexing-parsing-preprocessing}
@ -79,15 +72,15 @@
typealias Answers = Sequence<Answer>
\end{minted}
Een lege \texttt{Sequence} betekent dat er geen oplossingen zijn gevonden.
Een \texttt{Result} met een \texttt{Substitutions} representeert een oplossing met de resulterende substituties (die leeg kan zijn).
Een \texttt{Result} met een \texttt{Failure} betekent een fout of bijzondere logica die afgehandeld moet worden.
Een lege \mintinline{kotlin}{Sequence} betekent dat er geen oplossingen zijn gevonden.
Een \mintinline{kotlin}{Result.success} met \mintinline{kotlin}{Substitutions} representeert een oplossing met de resulterende substituties (die leeg kan zijn).
Een \texttt{Result.failure} betekent een fout of bijzondere logica die afgehandeld moet worden.
Er werden twee interfaces gedefinieerd:
\begin{minted}{kotlin}
interface Resolvent {
fun resolvent(goal: Goal, substitutions: Substitutions): Answers
fun solve(goal: Goal, substitutions: Substitutions): Answers
}
interface Satisfiable {
@ -95,17 +88,21 @@
}
\end{minted}
Klassen die \texttt{Resolvent} implementeren kunnen een goal oplossen, bijvoorbeeld een database of een regel (\textit{rule}).
Klassen die \texttt{Satisfiable} implementeren kunnen worden geëvalueerd, bijvoorbeeld operatoren.
Klassen die \mintinline{kotlin}{Resolvent} implementeren kunnen een \textit{goal} oplossen, bijvoorbeeld een database of een regel (\textit{rule}).
Klassen die \mintinline{kotlin}{Satisfiable} implementeren kunnen worden geëvalueerd, bijvoorbeeld operatoren.
De verschillende Prolog termen werden voorgesteld als klassen op basis van
\href{https://www.swi-prolog.org/pldoc/man?section=glossary}{SWI-Prolog's Glossary of Terms},
met bijkomende inspiratie uit~\cite{deransart-1996}.
\subsection{Evaluatiestrategie}\label{subsec:evaluatiestrategie}
% KERN: Evaluatiestrategie
Ghent Prolog maakt gebruik van een depth-first zoekstrategie.
Op het moment dat de databank een goal gevraagd wordt (\textit{query}), geeft die de goal beurtelings door aan de overeenkomende clauses, aan de hand van \texttt{solve}.
Op het moment dat de databank een goal gevraagd wordt (\textit{query}), geeft die de goal beurtelings door aan de overeenkomende clauses, aan de hand van \mintinline{kotlin}{solve}.
Eerst wordt de goal met de head van de clause geünificeerd, wat nieuwe substituties kan introduceren.
Daarna wordt de body van de clause geëvalueerd (\texttt{body.solve}), die zo de nieuwe goal wordt.
Deze procedure wordt herhaald totdat de goal \texttt{true} of \texttt{false} is.
Daarna wordt de body van de clause geëvalueerd (\mintinline{kotlin}{Body.satisfy}), die zo de nieuwe goal wordt.
Deze procedure wordt herhaald totdat de goal \mintinline{prolog}{true} of \mintinline{prolog}{false} is.
Vervolgens zal het programma backtracken naar de functie die de laatste stap uitvoerde, en de pas geïntroduceerde substituties omhoog doorgeven.
Daar kan dan logica uitgevoerd worden, of het resultaat verder doorgegeven worden aan de volgende stap.
@ -114,26 +111,74 @@
% Overzicht van geïmplementeerde predicaten in appendix.
Operator-specifieke logica bevindt zich in de klassen van de operatoren zelf.
\subsection{Unificatie}\label{subsec:unificatie}
Zoals beschreven in Artificial Intelligence a Modern Approach. Algoritme is geïnspireerd door
Het unificatie-algoritme is gebaseerd op Robinsons Unificatie-algoritme, zoals beschreven door~\cite{boizumault-1993}, met inspiratie van~\cite{russell2016}.
Merk op dat er gebruik gemaakt wordt van de \textit{occurs check}.
\subsection{Cut}\label{subsec:cut}
De cut operator geeft altijd een \mintinline{kotlin}{Result.failure(AppliedCut)} terug wanneer \mintinline{kotlin}{satisfy} wordt opgeroepen.
Deze uitzondering moet dan afgehandeld worden door de aanroepende functie.
\subsection{Meta abstracties}\label{subsec:meta-abstractions}
\section{Resultaat}\label{sec:resultaat}
% Code wordt lelijk en onoverzichtelijk. Door de geneste datatypes veel boilerplate.
De implementatie van Ghent Prolog ondersteunt de gevraagde functionaliteit, waaronder database operaties, meta abstracties.
De architecturele verschillen met SWI-Prolog zijn echter groot, wat zowel positieve als negatieve gevolgen heeft.
Omdat er veel overerving is, waarbij toch steeds methoden moeten worden overschreven, is er vaak boilerplate code nodig.
Waarschijnlijk is een visitor-patroon beter geschikt.
\subsection{Voordelen}\label{subsec:voordelen}
\subsection{Afwijkingen van SWI-Prolog}\label{subsec:afwijkingen}
De architectuur van Ghent Prolog is modulair en uitbreidbaar.
Eens de evaluatiestrategie en datastructuren zijn gedefinieerd, is het eenvoudig om extra ingebouwde operatoren toe te voegen.
Een volledig overzicht van de geïmplementeerde predicaten kan teruggevonden worden in sectie~\ref{sec:predicaten}.
\subsection{Uitdagingen}\label{subsec:uitdagingen}
% KERN: Nesting probleem
\textbf{Code wordt snel onoverzichtelijk} door het gebruik van \texttt{Sequence} en \texttt{Result}.
Belangrijke logica zit genest in onduidelijke boilerplate.
Volgende code komt bijvoorbeeld in de meeste termen voor, weliswaar in verschillende vorm:
\begin{minted}{kotlin}
/* Function entry logic */
unifyLazy(a, b, subs).forEach { firstResult ->
firstResult.map { firstSubs ->
/* First stage logic, e.g. preparing the body etc. */
c.satsify(firstSubs).forEach { secondResult ->
secondResult.fold(
onSuccess = { secondSubs ->
/* End result logic */
yield(Result.success(firstSubs + secondSubs))
}
onFailure = { failure ->
when (failure) {
is AppliedCut -> /* Cut logic */
is AppliedShift -> /* Shift logic */
} } ) } } }
\end{minted}
% KERN: Overerving zorgt voor boilerplate
\textbf{De implementatie bevat boilerplate code} door het gebruik van overerving en interfaces in de klassenrepresentatie van termen.
Deze methode wordt gebruikt om de onderlinge relaties tussen de verschillende termen te beschrijven.
Hoewel nuttig voor de representatie van de AST en generieke functies, moeten de meeste klassen de \texttt{satsify} en \texttt{solve} methoden overschrijven.
Dit zorgt voor boilerplate code en verpreid de logica over verschillende klassen.
Daarnaast kan het leiden tot verlies van type-informatie wanneer generieke functies en type-specifieke functies worden gemengd.
\subsection{Functionele afwijkingen van SWI-Prolog}\label{subsec:afwijkingen}
% TODO Maak appendix?
\begin{itemize}
\item Door het gebruik van de \textit{occurs check} is het niet mogelijk om een oplossing te vinden voor recursieve unificatie.
Voor \mintinline{prolog}{?- X = f(X).} vindt SWI-Prolog de oplossing \mintinline{prolog}{X = f(f(X))}, maar Ghent Prolog geeft \texttt{false} terug.
\item ISO Prolog en SWI-Prolog maken gebruik van SLD-resolutie
% TODO Bron
\end{itemize}
% Occurs check
% SLD Resolutie
@ -145,7 +190,11 @@
\section{Toekomstig werk}\label{sec:toekomstig-werk}
% Stack gebruiken
\begin{itemize}
\item Het gebruik van een stack voor choicepoints en andere informatie om het gebruik van exceptions weg te werken en eenvoudiger choicepoints te kunnen manipuleren.
Zo kan bijvoorbeeld de cut operator transparanter \textit{committen} tot de huidige oplossing door in één stap de juiste choicepoints te verwijderen, zonder dat elke operator een \texttt{AppliedCut} moet afhandelen.
\item Het gebruik van een visitor-patroon in plaats van overerving, om boilerplate te verminderen, zoals aangegeven in sectie~\ref{subsec:uitdagingen}.
\end{itemize}
\section{Conclusie}\label{sec:conclusie}
@ -184,7 +233,8 @@
\section{Uitvoeren en testen}\label{sec:uitvoeren-en-testen}
Om Ghent Prolog op een Windows, Linux of MacOS uit te voeren is het voldoende om Java te installeren en Ghent Prolog op te roepen met `./src/gpl`. De nodige stappen, waaronder het bouwen van een JAR, worden dan automatisch uitgevoerd.
Om Ghent Prolog op een Windows, Linux of MacOS uit te voeren is het voldoende om Java te installeren en Ghent Prolog op te roepen met `./src/gpl`.
De nodige stappen, waaronder het bouwen van een JAR, worden dan automatisch uitgevoerd.
De ingediende JAR kan ook handmatig opgeroepen worden met \texttt{java -jar ./build/gpl.jar}.
@ -205,6 +255,10 @@
\section{Overzicht van geïmplementeerde predicaten}\label{sec:predicaten}
Deze sectie geeft een overzicht van de geïmplementeerde predicaten in Ghent Prolog, gesorteerd volgens de categorieën die in
\href{https://www.swi-prolog.org/pldoc/man?section=builtin}{de SWI-Prolog documentatie}
gebruikt worden.
\begin{multicols}{2}
\begin{itemize}[label={}]
\item \textbf{Analysing and Constructing Terms}
@ -326,8 +380,8 @@
\section{Dankwoord}\label{sec:dankwoord}
Bedankt, LogicalCaptain
% TODO Link naar account
om steeds nuttige informatie en voorbeelden te geven bij SWI-Prolog documentatie die iets minder duidelijk is. Uw nota's waren verhelderend en zorgden voor een beter begrip van en voor de nuances van SWI-Prolog.
Bedankt, \href{https://www.swi-prolog.org/user/view_profile?user=c86f61d8-c201-11e6-84af-00163e986a2a}{LogicalCaptain}
om steeds nuttige informatie en voorbeelden te geven bij SWI-Prolog documentatie die iets minder duidelijk is.
Uw nota's waren verhelderend en zorgden voor een beter begrip van en voor de nuances van SWI-Prolog.
\end{document}