Sync
This commit is contained in:
parent
a5110f5a9d
commit
5726f33d34
2 changed files with 106 additions and 34 deletions
|
@ -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}
|
||||
|
|
Reference in a new issue