<!DOCTYPE article PUBLIC "-//NLM//DTD JATS (Z39.96) Journal Archiving and Interchange DTD v1.0 20120330//EN" "JATS-archivearticle1.dtd">
<article xmlns:xlink="http://www.w3.org/1999/xlink">
  <front>
    <journal-meta />
    <article-meta>
      <title-group>
        <article-title>2P-Kt: logic programming with objects &amp; functions in Kotlin</article-title>
      </title-group>
      <contrib-group>
        <contrib contrib-type="author">
          <string-name>Giovanni Ciatto</string-name>
          <xref ref-type="aff" rid="aff1">1</xref>
        </contrib>
        <contrib contrib-type="author">
          <string-name>Roberta Calegari</string-name>
          <xref ref-type="aff" rid="aff0">0</xref>
        </contrib>
        <contrib contrib-type="author">
          <string-name>Enrico Siboni</string-name>
          <xref ref-type="aff" rid="aff2">2</xref>
        </contrib>
        <contrib contrib-type="author">
          <string-name>Enrico Denti</string-name>
          <xref ref-type="aff" rid="aff1">1</xref>
        </contrib>
        <contrib contrib-type="author">
          <string-name>Andrea Omicini</string-name>
          <xref ref-type="aff" rid="aff1">1</xref>
        </contrib>
        <aff id="aff0">
          <label>0</label>
          <institution>Alma Mater Research Institute for Human-Centered Artificial Intelligence, Alma Mater Studiorum-Università di Bologna</institution>
          ,
          <country country="IT">Italy</country>
        </aff>
        <aff id="aff1">
          <label>1</label>
          <institution>Dipartimento di Informatica - Scienza e Ingegneria (DISI), Alma Mater Studiorum-Università di Bologna</institution>
          ,
          <country country="IT">Italy</country>
        </aff>
        <aff id="aff2">
          <label>2</label>
          <institution>University of Applied Sciences and Arts of Western Switzerland (HES-SO)</institution>
          ,
          <addr-line>Sierre</addr-line>
          ,
          <country country="CH">Switzerland</country>
        </aff>
      </contrib-group>
      <fpage>219</fpage>
      <lpage>236</lpage>
      <abstract>
        <p>Mainstream programming languages nowadays tends to be more and more multi-paradigm ones, by integrating diverse programming paradigms-e.g., object-oriented programming (OOP) and functional programming (FP). Logic-programming (LP) is a successful paradigm that has contributed to many relevant results in the areas of symbolic AI and multi-agent systems, among the others. Whereas Prolog, the most successful LP language, is typically integrated with mainstream languages via foreign language interfaces, in this paper we propose an alternative approach based on the notion of domain-specific language (DSL), which makes LP available to OOP programmers straightforwardly within their OO language of choice. In particular, we present a Kotlin DSL for Prolog, showing how the Kotlin multiparadigm (OOP+FP) language can be enriched with LP in a straightforward and efective way. Since it is based on the interoperable 2P-Kt project, our technique also enables the creation of similar DSL on top of other high-level languages such as Scala or JavaScript-thus paving the way towards a more general adoption of LP in general-purpose programming environments.</p>
      </abstract>
      <kwd-group>
        <kwd>eol&gt;logic programming</kwd>
        <kwd>object-oriented programming</kwd>
        <kwd>multi-paradigm languages</kwd>
        <kwd>domain-specific languages</kwd>
        <kwd>Kotlin</kwd>
      </kwd-group>
    </article-meta>
  </front>
  <body>
    <sec id="sec-1">
      <title>1. Introduction</title>
      <p>
        Logic Programming (LP) [
        <xref ref-type="bibr" rid="ref1 ref2">1, 2</xref>
        ] is a programming paradigm based on formal logic, inspired to
the idea of declaratively specifying a program semantics via logic formulae, so that automatic
reasoners can then prove such formulae by leveraging on diferent control strategies. In the
years, LP has contributed to the development of a number of diverse research fields laying
under the umbrella of symbolic AI—such as automatic theorem proving, multi-agent systems,
model checking, research optimisation, natural language processing, etc.
      </p>
      <p>
        Nowadays, LP is one of the major programming paradigms available for software development,
along with the imperative, functional, and object-oriented ones. In particular, LP is today one
of the best-suited choices for tackling problems involving knowledge representation, logic
inference, automated reasoning, search in a discrete space, or meta-programming [
        <xref ref-type="bibr" rid="ref3">3</xref>
        ]. Moreover,
today LP supports the core of AI components in pervasive and distributed systems, providing
intelligence where and when it is needed [
        <xref ref-type="bibr" rid="ref4">4</xref>
        ].
      </p>
      <p>This is why the integration of LP within the main programming languages is nowadays more
interesting than ever. However, to make it actually work, integration should be designed – from
a linguistic standpoint – so as to reduce both the development time and the learning curve of
developers, as well as the psychological and cultural resistances against the adoption of new
paradigms—thus, basically, moving LP up to a manageable level by the OOP developer.</p>
      <p>Most mainstream programming languages – such as Java, Kotlin, Scala, Python, JavaScript,
C# – have recognised the added value of the integration of diverse programming paradigms
under a unique syntax, a coherent API, and a rich standard library. In fact, they all already
support both the object-oriented (OOP) and functional (FP) programming paradigms. We believe
that the same languages would largely benefit from extending their support to LP languages as
well, making them usable in the OOP context in the same way as FP features already are.</p>
      <p>
        Interoperability among Prolog [
        <xref ref-type="bibr" rid="ref5">5</xref>
        ] (as the first and most prominent LP language) with other
languages from diferent paradigms is not new: the Prolog community has actually been studying
this theme for years [
        <xref ref-type="bibr" rid="ref6">6</xref>
        ], as shown by the many forms of integration historically present in most
Prolog systems, providing either (i) logic-to-OOP interoperability, making object orientation
available within Prolog scripts, or (ii) OOP-to-logic interoperability, making logic programming
exploitable within object-oriented code—or both, as in the case of tuProlog Java Library [
        <xref ref-type="bibr" rid="ref7">7</xref>
        ]. It
is worth noting here that existing integrations make quite strong assumptions. For instance,
item (i) assumes the main application is written in Prolog and the interoperation is given via
a suitable interface allowing the injection of (small) parts written in other languages, while
item (ii) implicitly assumes LP and Prolog to be somehow harmonised with the language(s)
hosting them, at the paradigm, syntactical, and technological levels. Both these assumptions
can actually create a barrier against an efective adoption of LP in today applications despite its
huge potential.
      </p>
      <p>Therefore, the approach adopted in this work is to devise instead a form of LP-FP-OOP blended
integration, such that the key features of LP paradigm can be exposed and made available to
mainstream language developers in a way that is the most natural in that context. The key
requirement, therefore, is the “making LP easy to adopt for OOP programmers”, in order to
break down the learning curve for non-experts, and pursuit the typical developers’ mindset.
Going beyond the mere interoperability intended as simple technical habilitation, our approach
is meant to embrace the perspective of the OOP developer aiming at exploiting the potential of
LP.</p>
      <p>To this end, we show how designing Prolog as a domain-specific language (DSL) for an OOP
language such as Kotlin can make LP close enough to the OOP developers’ mindset to overcome
most cultural barriers, while at the same mimicking the Prolog syntax and semantics close
enough to make its use straightforward for LP developers.</p>
      <p>Generally speaking, the integration of Prolog with other paradigms aims at bringing the
efectiveness and declarativeness of LP into general-purpose OOP framework. In particular,
OOP designers and programmers can be expected to benefit from LP features for (i) data-driven
or data-intensive computations, such as in the case of complex-event-processing frameworks;
(ii) operation research or symbolic AI algorithms, or more generally other algorithms involving a
search space to be explored; (iii) multi-agent systems, and the many areas in that field leveraging
on LP, such as knowledge representation, argumentation, normative systems, etc.</p>
      <p>
        The proposed solution is based on the 2P-Kt technology [
        <xref ref-type="bibr" rid="ref8">8</xref>
        ], a re-engineering of the tuProlog
project [
        <xref ref-type="bibr" rid="ref10 ref9">9, 10</xref>
        ] as a Kotlin multi-platform library supporting the JVM, JS, Android, and Native
platforms. The case of a Kotlin-based Prolog DSL is presented and discussed.
      </p>
      <p>Accordingly, the paper is organised as follows. Section 2 briefly introduces LP, providing
details about tuProlog, 2P-Kt, and Kotlin as well. It also summarises the current state of
the integration between LP and mainstream programming languages. Section 3 discusses
the rationale, design, and architecture of our Prolog-Kotlin DSL along with some examples.
Section 4, briefly illustrates a case study where our DSL is used in combination with functional
programming to solve a simple AI task in an elegant way. Finally, in section 5, concludes the
paper by providing some insights about the possible future research directions stemming from
our work.</p>
    </sec>
    <sec id="sec-2">
      <title>2. State of the Art</title>
      <sec id="sec-2-1">
        <title>2.1. Logic programming</title>
        <p>
          Logic programming (LP) is a programming paradigm based on computational logic [
          <xref ref-type="bibr" rid="ref11 ref12">11, 12</xref>
          ]. In
LP languages, a program is typically a set of logical relations, that is, a set of sentences in logical
form, expressing facts and rules about some domain. In Prolog [
          <xref ref-type="bibr" rid="ref5">5</xref>
          ] , in particular, both facts and
rules are written in the form of clauses, i.e.  :- 1, ..., , which are read declaratively as
logical implications ( is true if all  are true). While both  and  are atomic formulae, 
is called the head of the rule and (1, ..., ) is called the body. Facts are rules without body,
and they are written in the simplified form: .
        </p>
        <p>An atomic formula is an expression in the form  (1, ..., ) where  is a predicate having
 arguments,  ≥ 0, and 1, ...,  are terms. Terms are the most general sort of data structure
used in Prolog. A term is either a constant (either a number or an atom), a variable, or a (possibly
recursive) function of terms. Variables are named placeholders which may occur within clauses
to reference (yet) unknown objects. When reading clauses declaratively, variables are assumed
to be universally quantified if occurring into a rule head, existentially quantified if in the body.</p>
        <p>
          A logic program is executed by an inference engine that answers a query by trying to prove
it is inferable from the available facts and rules. In particular, the Prolog interpreter exploits a
deduction method based on the SLD resolution principle introduced in [
          <xref ref-type="bibr" rid="ref2">2</xref>
          ]. The SLD principle,
in turn, heavily leverages on the unification algorithm [
          <xref ref-type="bibr" rid="ref13">13</xref>
          ] for constructing a most general
unifier (MGU) for any two suitable terms. Provided that such a MGU exists, the subsequent
application of the resulting substitution to the terms, renders them syntactically equal. These
two mechanisms – resolution and unification – constitute the basis of any Prolog solver. In
particular, SLDNF is an extension of SLD resolution for dealing with negation as failure [
          <xref ref-type="bibr" rid="ref14">14</xref>
          ]. In
SLDNF, goal clauses can contain negation as failure literals—i.e. the form ().
        </p>
        <p>terms
clauses &amp; theories</p>
        <p>Theory
assertA(Clause)
assertZ(Clause)
retract(Clause)
retractAl (Clause)
get(Clause): Sequence&lt;Clause&gt;
1
*</p>
        <p>Clause
head: Struct?
body: Term
Directive</p>
        <p>Fact</p>
        <p>Rule
resolution</p>
        <p>Term
apply(Substitution): Term
freshCopy(): Term</p>
        <p>Solution
query: Struct</p>
        <p>Solver
solve(Struct,Long): Sequence&lt;Solution&gt;
Integer
value: Int</p>
        <p>Real
value: Double</p>
        <p>Truth
isTrue: Boolean
Constant
value: Any
Numeric</p>
        <p>Var
name: String</p>
        <p>Struct
faurngcst:oAr:rrSatyri&lt;nTgerm&gt;</p>
        <p>Atom
value: String</p>
        <p>List
EmptyList
substitutioYne:sUnifier No
substitutions</p>
        <p>Substitution
get(Var): Term
contains(Var): Boolean</p>
        <p>Cons
head: Term
tail: Term</p>
        <p>Unifier</p>
        <p>Fail</p>
        <p>Halt
exception: PrologError</p>
        <p>MutableSolver
loadStaticKb(Theory)
loadDynamicKb(Theory)
unification</p>
        <p>Unificator
mgu(Term, Term): Substitution
unify(Term, Term): Term?
match(Term, Term): Boolean</p>
      </sec>
      <sec id="sec-2-2">
        <title>2.2. tuProlog and 2P-Kt</title>
        <p>
          tuProlog [
          <xref ref-type="bibr" rid="ref10">10</xref>
          ] (2P for short) is a logic programming framework supporting multi-paradigm
programming via a clean, seamless, and bidirectional integration between the logic and
objectoriented paradigms. 2P-Kt is a Kotlin project for LP, representing a complete rewriting of
the tuProlog project, whose ultimate purpose is to provide an open LP ecosystem supporting
multiple platforms and programming paradigms.
        </p>
        <p>More importantly, 2P-Kt includes a rich API providing many logic-programming facilities to
developers. For instance, it enables the exploitation of clauses and terms to represent symbolic
knowledge via FOL in Kotlin. Clauses and terms can be manipulated via logic unification,
possibly producing substitutions to be applied to other clauses and terms. Logic theories can
be created as indexed and ordered sequences of clauses, enabling the exploitation of classical
assert or retract operations – as well as bare clause retrieval via pattern-matching – in an
eficient way. Finally, the full potential of Prolog SLDNF resolution strategy can be exploited, if
needed, via lightweight solvers which could be instantiated and queried on the fly, by lazily
consuming their solutions.</p>
        <p>Accordingly, the 2P-Kt API – sketched in fig. 1 – exposes a Kotlin type for each relevant
LP concept: (i) logic Terms (as well as for each particular sort of term, e.g. Variables, or
Structures, etc.), (ii) logic Substitutions and Unification, (iii) Horn Clauses (there
including Rules, Facts, and Directives), (iv) knowledge bases and logic theories (e.g. the
Theory type), and (v) automatic reasoning via Prolog’s SLDNF resolution strategy, as provided
by the Solver interface.</p>
      </sec>
      <sec id="sec-2-3">
        <title>2.3. Prolog integration</title>
        <p>In order to integrate Prolog with high-level languages, many Prolog implementations expose a
foreign language interface (FLI, table 1). In most cases, the target language is Java, and the FLI is
bi-directional—meaning that it supports both “calling Prolog from the target language” and vice
versa. In a few particular cases, however, the JavaScript and C# languages are also supported.
As we are mostly interested in discussing how and to what extent Prolog exploitation is possible
within the target languages, in the reminder of this section we only focus on those FLI allowing
to “call Prolog from the target language”.</p>
        <p>Each FLI mentioned in table 1 is characterised by a number of features that heavily impact
the way the users of the target languages may actually exploit Prolog. These are, for instance,
the nature of the FLI – which is tightly related to the target platform of the underlying Prolog
implementation – and its reference programming paradigm.</p>
        <p>By “nature” of the FLI, we mean the technological mechanism exploited by a particular Prolog
implementation to interact with the target language. There are some cases – like tuProlog and
 Prolog – where this aspect is trivial, because the target language is also the implementation
language—so Prolog is considered there as just another library for the target language. More
commonly, however, Prolog is implemented in C, and the Java Native Interface (JNI) is exploited
to make it callable from Java. This is for instance the case of SWI- and ECLiPSe-Prolog. While
this solution is very eficient, it hinders the portability of Prolog into particular contexts such
as Android, and its exploitation within mobile applications.</p>
        <p>
          Another viable solution is to leverage on the TCP/IP protocol stack. This is for instance the
case of SICStus- and Ciao-Prolog. The general idea behind this approach is that the Prolog
implementation acts as a remote server ofering logic-programming services to the target
language via TCP/IP, provided that a client library exists on the Java side making the exploitation
of TCP/IP transparent to the users. While this solution is more portable – as virtually any sort of
device supports TCP/IP –, it raises eficiency issues because of the overhead due to
network/interprocess communications. A more general solution of that sort is instead ofered by the LPaaS
architecture [
          <xref ref-type="bibr" rid="ref22">22</xref>
          ], where the fundamental idea is to have many Prolog engines distributed over
the network, accessed as logic-based web services. By the way, the implementation of LPaaS1 is
based on tuProlog.
        </p>
        <p>By “reference programming paradigm” of the FLI we mean the specific programming style
proposed by a Prolog implementation to the target language users through its API. Some FLI are
conceived to be used in a strictly imperative way: this is e.g. the case of BProlog or Ciao! Prolog,
where a Java API is available for writing Prolog queries and issue them towards the underlying
Prolog system in an imperative way. Other Prolog implementations, such as SICStus- and
SWI-Prolog, ofer a more object-oriented API which let developer not only represent queries but
also terms, and solutions (there including variable bindings) which can be consumed through
ordinary OOP mechanisms such as iterators.</p>
        <p>In most cases, however, the code to be produced in the target language is far less concise and
compact than pure Prolog – unless strings and parsing are extensively adopted –, especially
when the size of the Prolog code to be represented grows. So, for instance, the simple Prolog
query ?- parent(adam, X) (aimed at computing who Adam’s son is) in Java through
SWIProlog’s JPL interface would be written as:
Query query = new Query("parent", new Term[] { new Atom("adam"), new
˓→ Variable("X") } );
Map&lt;String,Term&gt; solution = query.oneSolution();
System.out.println("The child of Adam is " + solution.get("X"));
While this a totally efective solution on the technical level, we argue that a more convenient
integration among LP and the other paradigms is possible. In particular, in this paper we
show how 2P-Kt allows for a finer integration at the paradigm level through domain-specific
languages.</p>
      </sec>
      <sec id="sec-2-4">
        <title>2.4. Kotlin Domain-Specific Languages (DSL)</title>
        <p>
          Domain-specific languages (DSL) are a common way to tackle recurrent problems in a general
way via software engineering. When a software artefact (e.g. library, architecture, system) is
available to tackle with some sort of problems, designers may provide a DSL for that artefact to
ease its usage for practitioners. There, the exploitation of a DSL hides the complexity of the
library behind the scenes, while supporting the usage of the artefact for non-expert users too.
This is, for instance, the approach of the Gradle build system2—which is nowadays one of the
most successful build automation systems for the JVM, Android, and C/C++ platforms. It is
also the approach followed by the JADE agent programming framework, which is nowadays
usable through the Jadescript DSL [
          <xref ref-type="bibr" rid="ref23">23</xref>
          ].
        </p>
        <p>Building a DSL usually requires the definition of a concrete syntax, the design and
implementation of a compiler or code generator, and the creation of an ecosystem of tools—e.g., syntax
highlighters, syntax checkers, debuggers. In particular, compilation or code generation are
fundamental as they are what makes a DSL machine-interpretable and -executable.</p>
        <p>Some high-level languages, however, such as Kotlin, Groovy, or Scala, enable a diferent
approach. They come with a flexible syntax which natively supports the definition of new DSL
with no addition to the hosting language required. For instance, Gradle consists of a JVM library
of methods for building, testing, and deploying sources codes, plus a Kotlin- or Groovy-based
DSL allowing developers to customise their particular workflows.</p>
        <p>The advantages of this approach are manifold. First, no compiler or code generator has to be
built, as the DSL is already part of the hosting language and it is therefore machine-interpretable
and -executable by construction. Second, the DSL automatically inherits all the constructs,
API, and libraries of the hosting language—there including conditional or iterative
constructs, string manipulation API, etc., which are common and useful for many DSL, regardless of
their particular domain. Third, the ecosystem of tools supporting the hosting language – e.g.
compilers, debuggers, formatters, code analysers, IDE, etc. – can be reused for the DSL as well,
easing its adoption and making it more valuable.</p>
        <p>When Kotlin is the hosting language of choice, DSL leverage on a small set of features making
the Kotlin syntax very flexible, described below:
operator overloading3 — allowing ordinary arithmetic, comparison, access, and function
invocation operators to change their ordinary meaning on a per-type basis;
block-like lambda expressions4 — including a number of syntactic sugar options such as
(i) the possibility to omit formal parameters in case of a single-argument lambda
expression, and (ii) the possibility to omit the round parentheses in case of a function invocation
having a lambda expression as a last argument;
function types/literals with receiver4 — allowing functions and methods to accept lambda
expressions within which the this variable references a diferent object than the outer
scope;
extension methods5 — allowing pre-existing types to be extended with new instance methods
whose visibility is scope-sensible.</p>
        <p>Of course, Kotlin-based DSL automatically inherit the full gamma of facilities exposed by
the Kotlin language and standard library—there including support for imperative, and
objectoriented programming, as well as a rich API supporting functional programming through
most common high-order operations. Furthermore, if properly engineered, these DSL may be
executed on all the platforms supported by Kotlin—which commonly include, at least, the JVM,
JavaScript, and Android. This is for instance the case of our DSL proposed in section 3.</p>
        <p>
          While this paper focuses on Kotlin-based DSL, similar results could be achieved in other
languages by means of equivalent mechanisms. For instance, in Scala, extension methods have
to be emulated via implicit classes, and DSL, in particular, can be built via the “Pimp My Library”
pattern [
          <xref ref-type="bibr" rid="ref24">24</xref>
          ].
        </p>
      </sec>
    </sec>
    <sec id="sec-3">
      <title>3. A domain-specific language for LP</title>
      <sec id="sec-3-1">
        <title>3.1. Design Rationale</title>
        <p>Regardless of the technological choices, the design of our DSL leverages on a small set of
principles (Pi) briefly discussed below. In fact, our aim is to ( P1) provide a DSL that is a strict
extension of its hosting language, meaning that no feature of the latter language is prevented
by the usage of our DSL. Dually, we require (P2) our DSL to be fully interoperable and finely
integrated with the hosting language, meaning that all the features of the latter language can be
3https://kotlinlang.org/docs/reference/operator-overloading.html
4https://kotlinlang.org/docs/reference/lambdas.html
5https://kotlinlang.org/docs/reference/extensions.html
exploited from within our DSL. However, we also require (P3) the DSL to be well encapsulated
and clearly identifiable within the hosting language, in order to prevent unintended usage of
the DSL itself. Finally, we require (P4) our DSL to be as close as possible to Prolog, both at the
syntactic and semantic level, to ease its exploitation for logic programmers.</p>
        <p>In order to accomplish to the aforementioned principles, we choose the Kotlin language as the
technological reference for prototyping our proposal. This is because it (i) comes with a flexible
syntax supporting the definition of DSL, (ii) supports a considerable number of platforms (JVM,
JavaScript, Android, Native) and therefore enables a wide exploitation of LP – for instance, on
smart devices –, (iii) includes a number of libraries supporting LP-based applications, such as
2P-Kt.</p>
      </sec>
      <sec id="sec-3-2">
        <title>3.2. The Kotlin DSL for Prolog</title>
        <p>Before dwelling into the details of our proposal, we provide an overview of what a Kotlin DSL
for Prolog has to ofer.</p>
        <p>Let us consider the following Prolog theory describing a portion of Abraham’s family tree:
ancestor(X, Y) :- parent(X, Y).
ancestor(X, Y) :- parent(X, Z), ancestor(Z, Y).
parent(abraham, isaac).
parent(isaac, jacob).
parent(jacob, joseph).</p>
        <p>It enables a number of queries, e.g. ancestor(abraham, X), which can be read as “Does
there exist some X which is a descendant of Abraham?”. According to the Prolog semantics, this
query may have a number of solutions, enumerating all the possible descendants of Abraham
that can be deduced from the above theory—i.e., Isaac, Jacob, and Joseph.</p>
        <p>The same result may be attained through the following Kotlin program, which leverages on
our DSL for Prolog:
prolog {
staticKb(
rule {</p>
        <p>"ancestor"("X", "Y") `if` "parent"("X", "Y")
)
},
rule {
"ancestor"("X", "Y") `if` ("parent"("X", "Z") and
˓→ "ancestor"("Z", "Y"))
},
fact { "parent"("abraham", "isaac") },
fact { "parent"("isaac", "jacob") },
fact { "parent"("jacob", "joseph") }
}
for (sol in solve("ancestor"("abraham", "X")))
if (sol is Solution.Yes)</p>
        <p>println(sol.substitution["X"])
The program creates a Prolog solver and initialises it with standard built-in predicates. Then
it loads a number of facts and rules representing the aforementioned Prolog theory about
Abraham’s family tree. Finally, it exploits the solver to find all the descendants of Abraham, by
issuing the query ancestor(abraham, X).</p>
        <p>It is worth noting how the simple code snippet exemplified above is adherent w.r.t. our
principles. In fact, the whole DSL is encapsulated (P3) within the</p>
        <p>prolog { ⟨ ⟩ }
In there, LP facilities can be exploited in combination with the imperative and functional
constructs ofered by the Kotlin language and its standard-library ( P1 and P2)—such as the
for-each-loop used to print solutions in the snippet above. Furthermore, within prolog
blocks, both theories and queries are expressed via a Kotlin-compliant syntax mimicking Prolog
(P4). The main idea behind such syntax is that each expression in the form
"functor"(⟨1⟩, ⟨2⟩, . . .)
is interpreted as a compound term (a.k.a. structure) in the form functor(1, 2, . . .), provided
that each Kotlin expression  can be recursively evaluated as the term —e.g. capital strings
such as "X" are interpreted as variables, whereas non-capital strings such as "atom" (as
well as Kotlin numbers) are interpreted as Prolog constants. In a similar way, expressions of the
form6
rule {"head"(⟨1⟩, . . .,</p>
        <p>⟨ ⟩) `if` (⟨+1⟩ and . . . and ⟨ ⟩)}
are interpreted as Prolog rules of the form</p>
        <p>head(1, . . . ,  ) :- +1, . . . , 
provided that  &gt;  and each Kotlin expression  can be recursively evaluated as the term .
A similar statement holds for fact expressions of the form fact { . . . } .</p>
        <p>To support our DSL for Prolog, a number of Kotlin classes and interfaces have been designed
on top of the 2P-Kt library, exploiting manifold extensions methods, overloaded operators,
etc. to provide the syntax described so far. The details of our solution – there including its
architecture, design, and implementation – are discussed in the reminder of this section.</p>
      </sec>
      <sec id="sec-3-3">
        <title>3.3. Architecture, Design, Implementation</title>
        <p>The Kotlin DSL for Prolog is essentially a compact means to instantiate and use objects from the
2P-Kt library, through a Prolog-like syntax. More precisely, it consists of a small software layer
6backticks make Kotlin parse words as identifiers instead of keywords</p>
        <p>Prolog DSL
2P-Kt</p>
        <p>Kotlin
JVM</p>
        <p>JS
(a) Layered view
showing the modules of
2P-Kt and our Kotlin
DSL for Prolog</p>
        <p>Scope
_: Var
varOf(String): Var
atomOf(String): Atom
structOf(String, Iterable&lt;Term&gt;): Struct
emptyList(): EmptyList
listOf(Iterable&lt;Term&gt;): List
numOf(Double): Real
numOf(Int): Integer</p>
        <p>Prolog
Any.toTerm(): Term
String.invoke(Any, vararg Any): Struct
Substitution.get(String): Term?</p>
        <p>PrologWithUnification
Any.mguWith(Any): Substitution
Any.matches(Any): Boolean</p>
        <p>Any.unifyWith(Any): Term?</p>
        <p>PrologWithResolution
solve(Any): Sequence&lt;Solution&gt;
solverOf(...)
member(Any, Any): Term
is(Any, Any): Term
!: Atom
fail: Atom</p>
        <p>extends</p>
        <p>PrologWithTheories
theoryOf(vararg Clause): Theory
rule(PrologWithTheories.() -&gt; Any): Rule
fact(PrologWithTheories.() -&gt; Any): Fact
and(Any, Any): Term
or(Any, Any): Term
(b) Structural view showing the API of our Kotlin DSL for Prolog, leveraging</p>
        <p>
          on the 2P-Kt API
built on top of Kotlin and 2P-Kt, as represented by fig. 2a. In particular, the DSL is enabled by
the five factory interfaces depicted in fig. 2b. We call factory interface a type definition whose
methods are aimed at instantiating objects of related types, as dictated by the Gang of Four’
abstract factory pattern [
          <xref ref-type="bibr" rid="ref25">25</xref>
          ].
        </p>
        <p>The five factory interfaces are: Scope, Prolog, PrologWithUnification, PrologWithTheories,
and PrologWithResolution. Each factory type extends the previous one with more
LPrelated functionalities. So, for instance, while instances of Scope simply provide the basic
bricks to create logic terms, instances of Prolog leverage on these bricks to enable the
exploitation of the Prolog-like syntax exemplified above. PrologWithUnification extends
Prolog with unification-related facilities, and it is in turn extended by
PrologWithTheories, which lets developers create both clauses and theories via a Prolog-like syntax. Finally
PrologWithResolution extends PrologWithTheories by adding resolution-related
facilities, plus some syntactic shortcuts for writing rules exploiting Prolog standard predicates such
as member/2, length/1, etc.</p>
        <p>In the following we provide further details about how each factory contributes to our DSL.
Scope The simplest factory type is Scope. As suggested by its name, it is aimed at building
terms which must share one or more variables. For this reason, it exposes a number of factory
methods – roughly, one for each sub-type of Term –, some of which are mentioned in fig. 2b.
The main purpose of a scope, however, is to enable the creation of objects reusing the same
logic Variables more than once. Thus, it includes a method – namely, varOf(String) – which
always returns the same variable if the same name is provided as input.</p>
        <p>For example, to create the term member(X, [X | T]), one may write:
val m = Scope.empty {</p>
        <p>structOf("member", varOf("X"), consOf(varOf("X"), varOf("T")))
} // m references the term member(X, [X | T])
There, the two calls to varOf("X") actually return the same object, if occurring within the
same scope—whereas they would return diferent variables if invoked on diferent scopes.
This mechanism is what enables developers to instantiate terms and clauses without having
to explicitly refresh variables among diferent clauses: it is suficient to create them within
diferent scopes.</p>
        <p>Prolog The Prolog factory type extends Scope by adding the capability of creating terms
through a Prolog-like syntax. To do so, it exposes a number of extension methods aimed at
automatically converting Kotlin objects into Prolog terms or using Kotlin objects in place of
Prolog terms. The most relevant extension methods are mentioned in fig. 2b. These methods
are:
• fun Any .toTerm(): Term , which is an extension method aimed at making any
Kotlin object convertible into a Prolog term, through the syntax obj.toTerm() . To
convert an object into a term, it leverages on the following type mapping: (i) a Kotlin
number is either converted into a Prolog real or integer number, depending on whether
the input number is floating-point or not, (ii) a Kotlin string is either converted into
a Prolog variable or atom, depending on whether the input string starts with a capital
letter or not, (iii) a Kotlin boolean is always converted into a Prolog atom, (iv) a Kotlin
iterable (be it an array, a list, or any other collection) is always converted into a Prolog
list, provided that each item can be recursively converted into a term, (v) a Kotlin object
remains unafected if it is already an instance of Term, (vi) an error is raised if the input
object cannot be converted into a Term.
• operator fun String.invoke(vararg Any): Struct , which is an extension
method aimed at overloading the function invocation operator for strings. Its purpose is to
enable the construction of compound terms through the syntax "f"(arg1, . . . , arg ) ,
which mimics Prolog. Its semantics is straightforward: assuming that each arg can
be converted into a term via the Any.toTerm() extension method above, this method
creates a  -ary Structure whose functor is "f" and whose ℎ is arg.toTerm(). So,
for instance, the expression</p>
        <p>"member"("X", arrayOf(1, true))
creates the Prolog term member(X, [1, true]).
• fun Substitution.get(String): Term? , which is an extension method aimed at
overloading the get method of the Substitution type in such a way that is can also
accept a string other than a Variable. This enables DSL users to write expressions such as
substitution.get("X")
instead of having to create a variable explicitly via varOf("X") . While we only discuss
this method, the main idea here is that every method in the 2P-Kt API accepting some basic
Prolog type – such as Var, Atom, or Real – as argument should be similarly overloaded
to accept the corresponding Kotlin type as well—e.g. String or Double. This is what
enables a fine-grained integration of our DSL with the Kotlin language and the 2P-Kt
library.
It is worth highlighting that every Prolog object is also a particular sort of Scope. So,
converting the same string into a Variable twice or more times, within the same Prolog object,
always yields the exact same variable.</p>
        <p>Prolog with unification The PrologWithUnification factory type extends Prolog by
adding the capability of (i) computing the most general unifier (MGU) among two terms,
(ii) checking whether two terms match or not according to logic unification – i.e., checking
if a MGU exists unifying the two terms –, and (iii) computing the term attained by unifying
two terms—assuming an MGU exists for them. To do so, it exposes a number of extension
methods aimed at providing unification-related support to Kotlin objects, provided that they
can be converted into terms. The most relevant extension methods are mentioned in fig. 2b.
Prolog with theories The PrologWithTheories factory type extends
PrologWithUnification by adding the capability of creating logic clauses (e.g. rules and facts) and theories.
To do so, it exposes a number of methods aimed supporting the conversion of Kotlin objects
into Prolog clauses – through the syntactic facilities presented so far –, and their combination
into theories. The most relevant methods, mentioned in fig. 2b, are the following:
• fun theoryOf(vararg Clause): Theory , an ordinary method aimed at creating
a logic Theory out of a variable amount of clauses.
• infix fun Any .`if`(Any): Rule , which is an extension method aimed at creating
logic rules via a Prolog-like syntax in the form head `if` body . Its semantics is
straightforward: assuming that both head and body can be converted into logic goals via
the Any.toTerm() extension method above, this method creates a binary Structure
whose functor is ‘:-’ and whose arguments are head.toTerm() and body.toTerm().
Similar methods exists – namely, and, or, etc. – to create conjunctions or disjunctions of
clauses.
• fun rule(PrologWithTheories.() -&gt; Any): Rule , an ordinary method aimed
at creating a rule in a separate scope, thus avoiding the risk of accidentally referencing
the variables created elsewhere. It creates a fresh, empty, and nested instance of
PrologWithTheories and accepts a function with receiver to be invoked on that nested
instance. The function is expected to return a Kotlin object which can be converted into
a Prolog rule. Any variable possibly created within the nested scope is guaranteed to be
diferent than any homonymous variable defined elsewhere.
• fun fact(PrologWithTheories.() -&gt; Any): Fact , analogous to the previous
method, except that it is aimed at creating Prolog facts. So, for instance, one may write
val r1 = fact {</p>
        <p>"member"("X", consOf("X", `_`))
}
val r2 = rule {
"member"("X", consOf(`_`, "T")) `if` "member"("X", "T")
while being sure that the X variable used in r1 is diferent than the one used in r2.
Prolog with resolution The PrologWithResolution factory type extends
PrologWithUnification by adding the capability of performing logic queries and consuming their
solutions attained through the standard Prolog semantics. To do so, it exposes a number of
methods aimed at supporting (i) the instantiation of Prolog Solvers, (ii) the loading of Prolog
theories, either as static or dynamic knowledge bases (KB), and (iii) the invocation of Prolog
queries on those KB. The most relevant methods, mentioned in fig. 2b, are the following:
• fun solve(Any, Long): Sequence&lt;Solution&gt; , which is an ordinary method
aimed at executing Prolog queries without requiring a new solver to be explicitly created.
It accept a Kotlin object as argument – which must be convertible into a Prolog query
–, and an optional timeout limiting the total amount of time the solver may exploit to
compute a solution. If the provided arguments are well formed, this method returns a
Sequence of Solutions which lazily enumerates all the possible answers to the query
provided as input, using Prolog’s SLDNF proof procedure.
• fun staticKb(vararg Clause) , which is an ordinary method aimed at loading the
static KB the solve method above will operate upon.
• fun dynamicKb(vararg Clause) , which is analogous to the previous method,
except that it loads the dynamic KB the solve method above will operate upon.
• fun member(Any, Any): Struct which is an ordinary method aimed at easily
creating invocations of the member/2 built-in predicate. While we only discuss this method,
the main idea here is that every standard built-in predicate in Prolog has a Kotlin
counterpart in PrologWithResolution accepting the proper amount or arguments and
returning an invocation to that built-in predicate. This is aimed easing the exploitation of
the standard Prolog predicates to developers leveraging our DSL. So, for instance, we also
provide facility methods for built-in such as is/2, +/2, -/2, !/0, fail/0, append/3,
etc.</p>
        <p>Instances of PrologWithResolution are created and used via the</p>
        <p>fun &lt;R&gt; prolog(PrologWithResolution.() -&gt; R): R
static method, which accepts a lambda expression letting the user exploit the DSL on the fly,
and returns the object created by this lambda expression. This is for instance what enables
developers to write the code snippet discussed in section 3.2.</p>
      </sec>
    </sec>
    <sec id="sec-4">
      <title>4. Case study: N-Queens</title>
      <p>We present now a brief example demonstrating how, by integrating multiple programming
paradigms, developers may easily produce compact and efective solutions. Suppose one is
willing to implement the following Kotlin method</p>
      <p>fun nQueens(n: Int): Sequence&lt;List&lt;Position&gt;&gt;
aimed at computing all the possible solutions to the N-Queens problem. More precisely, the
method is expected to enumerate all the possible dispositions of  queens on a  ×  chessboard,
such that no queen can be attacked by others. Each solution can be represented by a list of
queen positions, in the form [(1, 1), . . . , (, )], where each  represent the row occupied
by the ℎ queen—i.e., the one in column .</p>
      <p>Computing all solutions for the N-Queens problem may require a lot of code in most
programming paradigms. However, in LP, the solution is straightforward and compact. One may,
for instance, leverage the following Prolog code:
no_attack((X1, Y1), (X2, Y2))
:</p>
      <p>X1 =\= X2, % infix operator
Y1 =\= Y2,
(Y2 - Y1) =\= (X2 - X1),
(Y2 - Y1) =\= (X1 - X2). % arithmetic expression
no_attack_all(_, []).
no_attack_all(C , [H | Hs])
:no_attack(C, H),
no_attack_all(C, Hs).
solution(_, []).
solution(N, [(X, Y) | Cs])
:solution(N, Cs),
between(1, N, Y), % built-in predicate
no_attack_all((X, Y), Cs).
which can satisfy queries in the form</p>
      <p>?- solution(N, [(1, Y1), ..., (N, YN)])
(provided that some actual  is given) by instantiating each variable Yi.</p>
      <p>Thanks to our Kotlin DSL for Prolog, one may exploit the elegance of LP in implementing
the aforementioned nQueens method. For instance, one may implement it as follows:
fun nQueens(n: Int) = prolog {
staticKb(
rule {
"no_attack"(("X1" and "Y1"), ("X2" and "Y2")) `if` (
("X1" `=!=` "X2") and // infix operator
("Y1" `=!=` "Y2") and
(("Y2" - "Y1") `=!=` ("X2" - "X1")) and
(("Y2" - "Y1") `=!=` ("X1" - "X2")) // arithmetic expr
},
fact { "no_attack_all"(`_`, emptyList) },
rule {
"no_attack_all"("C", consOf("H", "Hs")) `if` (
"no_attack"("C", "H") and
"no_attack_all"("C", "Hs")
}
},
fact { "solution"(`_`, emptyList) },
rule {
"solution"("N", consOf(("X" and "Y"), "Cs")) `if` (
"solution"("N", "Cs") and
between(1, "N", "Y") and // built-in predicate
"no_attack_all"(("X" and "Y"), "Cs")
}</p>
      <p>)
)
return solve("solution"(n, (1 .. n).map { it and "Y$it" }))
This implementation produces a lazy stream of solutions to the N-Queens problem, given a
particular value of . In doing so, it takes advantages not only of logic- but also of
functionalprogramming paradigm. For instance, on the last line, it exploits the map high-order function
to build a list in the form [(1, 1), . . . , (, )], to be provided as argument of solution/2.</p>
      <p>A number of minutiae may be noted as well by comparing the Prolog code with its Kotlin
counterpart. For instance, Prolog operators (such as =∖=/2, -/2, etc.) retain their infix notations
in the Prolog DSL. This is possible because they are part of the DSL, in the same way as Prolog
built-in predicates (such as between/3). This implies the Kotlin compiler can prevent standard
operators and built-in from being silently mistyped.</p>
    </sec>
    <sec id="sec-5">
      <title>5. Conclusions and future works</title>
      <p>In this paper we propose a novel way of integrating the logic, object-oriented, functional, and
imperative programming paradigms into a single language, namely Kotlin. More precisely, we
describe how a domain-specific language (DSL) for Prolog can be built on top of the Kotlin
language by exploiting the 2P-Kt library. The proposed solution extends the Kotlin language
with LP facilities by only relying on its own mechanisms – therefore no external tool is necessary
apart from Kotlin itself and 2P-Kt –, even if, however, analogous extensions can in principle be
constructed for other high-level languages as well—such as Scala.</p>
      <p>
        Our DSL is currently implemented as part of the 2P-Kt project – namely, within the dsl-*
modules –, and it is hosted on both GitHub [
        <xref ref-type="bibr" rid="ref8">8</xref>
        ] and Maven Central. Other than being available to
the public for general purpose usage – under the terms of the Apache License 2.07 open-source
license –, the DSL is currently extensively exploited to implement the unit tests of 2P-Kt itself.
      </p>
      <p>While in this paper we discuss the design rationale and architecture of our DSL by explicitly
focusing on Kotlin as our target technology, in the future we plan to generalise our approach,
possibly tending to technology independence. Furthermore, we plan to provide other
implementations of our DSL, targeting other high-level languages and platforms, in order to make logic
programming and its valuable features available in general-purpose programming languages
and frameworks.</p>
      <p>
        As concerns possible research directions and applications, in the future, we plan to exploit
our DSL in several contexts. For instance, we argue our DSL may ease the usage of the logic
tuple spaces ofered by the TuSoW technology [
        <xref ref-type="bibr" rid="ref26">26</xref>
        ]. Similarly, we believe our DSL may be used
as a basic brick in the creation of logic-based technologies for MAS, as the MAS community is
eager of general-purpose, logic-based technologies targetting the JVM platform [
        <xref ref-type="bibr" rid="ref27">27</xref>
        ]. Along this
line, we argue logic-based languages for MAS may benefit from the integration of our DSL – or
a similar one – to support the reasoning capabilities of agents. Finally, we plan to exploit our
DSL within the scope of XAI [
        <xref ref-type="bibr" rid="ref28">28</xref>
        ], to ease the creation of hybrid (i.e., logic + machine-learning)
systems, and management on the symbolic side.
      </p>
    </sec>
    <sec id="sec-6">
      <title>Acknowledgments References</title>
      <p>R. Calegari has been supported by the H2020 ERC Project “CompuLaw” (G.A. 833647). A.
Omicini has been partially supported by the H2020 Project “AI4EU” (G.A. 825619).</p>
    </sec>
  </body>
  <back>
    <ref-list>
      <ref id="ref1">
        <mixed-citation>
          [1]
          <string-name>
            <given-names>K. R.</given-names>
            <surname>Apt</surname>
          </string-name>
          ,
          <source>The Logic Programming Paradigm and Prolog</source>
          , Cambridge University Press,
          <year>2001</year>
          , pp.
          <fpage>475</fpage>
          -
          <lpage>508</lpage>
          . doi:
          <volume>10</volume>
          .1017/CBO9780511804175.016.
        </mixed-citation>
      </ref>
      <ref id="ref2">
        <mixed-citation>
          [2]
          <string-name>
            <given-names>R. A.</given-names>
            <surname>Kowalski</surname>
          </string-name>
          ,
          <article-title>Predicate logic as programming language</article-title>
          , in: J. L.
          <string-name>
            <surname>Rosenfeld</surname>
          </string-name>
          (Ed.),
          <source>Information Processing, Proceedings of the 6th IFIP Congress</source>
          , North-Holland,
          <year>1974</year>
          , pp.
          <fpage>569</fpage>
          -
          <lpage>574</lpage>
          .
        </mixed-citation>
      </ref>
      <ref id="ref3">
        <mixed-citation>
          [3]
          <string-name>
            <given-names>R.</given-names>
            <surname>Calegari</surname>
          </string-name>
          ,
          <string-name>
            <given-names>G.</given-names>
            <surname>Ciatto</surname>
          </string-name>
          ,
          <string-name>
            <given-names>E.</given-names>
            <surname>Denti</surname>
          </string-name>
          ,
          <string-name>
            <given-names>A.</given-names>
            <surname>Omicini</surname>
          </string-name>
          ,
          <article-title>Logic-based technologies for intelligent systems: State of the art and perspectives</article-title>
          ,
          <source>Information</source>
          <volume>11</volume>
          (
          <year>2020</year>
          )
          <fpage>1</fpage>
          -
          <lpage>29</lpage>
          . doi:
          <volume>10</volume>
          .3390/ info11030167, special Issue “10th Anniversary of Information-Emerging Research Challenges”.
        </mixed-citation>
      </ref>
      <ref id="ref4">
        <mixed-citation>
          [4]
          <string-name>
            <given-names>A.</given-names>
            <surname>Omicini</surname>
          </string-name>
          ,
          <string-name>
            <given-names>R.</given-names>
            <surname>Calegari</surname>
          </string-name>
          ,
          <article-title>Injecting (micro)intelligence in the IoT: Logic-based approaches for (M)MAS</article-title>
          , in: D.
          <string-name>
            <surname>Lin</surname>
            ,
            <given-names>T.</given-names>
          </string-name>
          <string-name>
            <surname>Ishida</surname>
            ,
            <given-names>F.</given-names>
          </string-name>
          <string-name>
            <surname>Zambonelli</surname>
          </string-name>
          , I. Noda (Eds.), Massively
          <string-name>
            <surname>Multi-Agent Systems</surname>
            <given-names>II</given-names>
          </string-name>
          , volume
          <volume>11422</volume>
          of Lecture Notes in Computer Science, Springer,
          <year>2019</year>
          , pp.
          <fpage>21</fpage>
          -
          <lpage>35</lpage>
          . doi:
          <volume>10</volume>
          .1007/978-3-
          <fpage>030</fpage>
          -20937-
          <issue>7</issue>
          _2,
          <string-name>
            <surname>international</surname>
            <given-names>Workshop</given-names>
          </string-name>
          , MMAS 2018, Stockholm, Sweden, July
          <volume>14</volume>
          ,
          <year>2018</year>
          , Revised Selected Papers.
        </mixed-citation>
      </ref>
      <ref id="ref5">
        <mixed-citation>
          [5]
          <string-name>
            <given-names>A.</given-names>
            <surname>Colmerauer</surname>
          </string-name>
          ,
          <string-name>
            <given-names>P.</given-names>
            <surname>Roussel</surname>
          </string-name>
          ,
          <article-title>The birth of prolog</article-title>
          , in: J.
          <string-name>
            <given-names>A. N.</given-names>
            <surname>Lee</surname>
          </string-name>
          ,
          <string-name>
            <surname>J. E.</surname>
          </string-name>
          Sammet (Eds.),
          <article-title>History of Programming Languages Conference (HOPL-II)</article-title>
          , ACM,
          <year>1993</year>
          , pp.
          <fpage>37</fpage>
          -
          <lpage>52</lpage>
          . doi:
          <volume>10</volume>
          .1145/ 154766.155362.
        </mixed-citation>
      </ref>
      <ref id="ref6">
        <mixed-citation>
          [6]
          <string-name>
            <given-names>R.</given-names>
            <surname>Bagnara</surname>
          </string-name>
          ,
          <string-name>
            <given-names>M.</given-names>
            <surname>Carro</surname>
          </string-name>
          ,
          <article-title>Foreign language interfaces for Prolog: A terse survey</article-title>
          ,
          <source>ALP Newsletter 15</source>
          (
          <year>2002</year>
          ). URL: https://dtai.cs.kuleuven.be/projects/ALP/newsletter/may02/ index.html.
        </mixed-citation>
      </ref>
      <ref id="ref7">
        <mixed-citation>
          [7]
          <string-name>
            <given-names>E.</given-names>
            <surname>Denti</surname>
          </string-name>
          ,
          <string-name>
            <given-names>A.</given-names>
            <surname>Omicini</surname>
          </string-name>
          ,
          <string-name>
            <given-names>A.</given-names>
            <surname>Ricci</surname>
          </string-name>
          <article-title>, Multi-paradigm Java-Prolog integration in tuprolog</article-title>
          ,
          <source>Science of Computer Programming</source>
          <volume>57</volume>
          (
          <year>2005</year>
          )
          <fpage>217</fpage>
          -
          <lpage>250</lpage>
          . doi:
          <volume>10</volume>
          .1016/j.scico.
          <year>2005</year>
          .
          <volume>02</volume>
          .001.
        </mixed-citation>
      </ref>
      <ref id="ref8">
        <mixed-citation>
          [8]
          <fpage>2P</fpage>
          -Kt,
          <article-title>Github page</article-title>
          , https://github.com/tuProlog/2p-kt,
          <source>Last access: October</source>
          <volume>22</volume>
          ,
          <year>2020</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref9">
        <mixed-citation>
          [9] tuProlog, home page, http://tuprolog.unibo.it, Last access:
          <source>October</source>
          <volume>22</volume>
          ,
          <year>2020</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref10">
        <mixed-citation>
          [10]
          <string-name>
            <given-names>E.</given-names>
            <surname>Denti</surname>
          </string-name>
          ,
          <string-name>
            <given-names>A.</given-names>
            <surname>Omicini</surname>
          </string-name>
          ,
          <string-name>
            <given-names>A.</given-names>
            <surname>Ricci</surname>
          </string-name>
          ,
          <article-title>tuProlog: A light-weight Prolog for Internet applications and infrastructures</article-title>
          , in: I. Ramakrishnan (Ed.),
          <source>Practical Aspects of Declarative Languages, volume 1990 of Lecture Notes in Computer Science</source>
          , Springer Berlin Heidelberg,
          <year>2001</year>
          , pp.
          <fpage>184</fpage>
          -
          <lpage>198</lpage>
          . doi:
          <volume>10</volume>
          .1007/3-540-45241-9_
          <fpage>13</fpage>
          , 3rd International Symposium (PADL
          <year>2001</year>
          ), Las Vegas,
          <string-name>
            <surname>NV</surname>
          </string-name>
          , USA,
          <fpage>11</fpage>
          -
          <lpage>12</lpage>
          March
          <year>2001</year>
          . Proceedings.
        </mixed-citation>
      </ref>
      <ref id="ref11">
        <mixed-citation>
          [11]
          <string-name>
            <given-names>J. W.</given-names>
            <surname>Lloyd</surname>
          </string-name>
          (Ed.),
          <source>Computational logic</source>
          , Springer,
          <year>1990</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref12">
        <mixed-citation>
          [12]
          <string-name>
            <given-names>A.</given-names>
            <surname>Nerode</surname>
          </string-name>
          , G. Metakides,
          <article-title>Principles of Logic and Logic Programming, Elsevier Science Inc</article-title>
          ., USA,
          <year>1996</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref13">
        <mixed-citation>
          [13]
          <string-name>
            <given-names>A.</given-names>
            <surname>Martelli</surname>
          </string-name>
          ,
          <string-name>
            <given-names>U.</given-names>
            <surname>Montanari</surname>
          </string-name>
          ,
          <article-title>An eficient unification algorithm</article-title>
          ,
          <source>ACM Transactions on Programming Languages and Systems</source>
          <volume>4</volume>
          (
          <year>1982</year>
          )
          <fpage>258</fpage>
          -
          <lpage>282</lpage>
          . doi:
          <volume>10</volume>
          .1145/357162.357169.
        </mixed-citation>
      </ref>
      <ref id="ref14">
        <mixed-citation>
          [14]
          <string-name>
            <surname>K. L. Clark</surname>
          </string-name>
          ,
          <article-title>Negation as failure</article-title>
          , in: H.
          <string-name>
            <surname>Gallaire</surname>
          </string-name>
          , J. Minker (Eds.),
          <source>Symposium on Logic and Data Bases, Advances in Data Base Theory</source>
          , Plemum Press, New York,
          <year>1977</year>
          , pp.
          <fpage>293</fpage>
          -
          <lpage>322</lpage>
          . doi:
          <volume>10</volume>
          .1007/978-1-
          <fpage>4684</fpage>
          -3384-5\_
          <fpage>11</fpage>
          .
        </mixed-citation>
      </ref>
      <ref id="ref15">
        <mixed-citation>
          [15] BProlog, home page, http://www.picat-lang.org/bprolog, Last access:
          <source>October</source>
          <volume>22</volume>
          ,
          <year>2020</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref16">
        <mixed-citation>
          [16]
          <string-name>
            <surname>Ciao</surname>
          </string-name>
          ! Prolog, home page, https://ciao-lang.org,
          <source>Last access: October</source>
          <volume>22</volume>
          ,
          <year>2020</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref17">
        <mixed-citation>
          [17]
          <string-name>
            <given-names>ECLiPSe</given-names>
            <surname>Prolog</surname>
          </string-name>
          , home page, https://eclipseclp.org,
          <source>Last access: October</source>
          <volume>22</volume>
          ,
          <year>2020</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref18">
        <mixed-citation>
          [18]
          <string-name>
            <given-names>SICStus</given-names>
            <surname>Prolog</surname>
          </string-name>
          , home page, https://sicstus.sics.se,
          <source>Last access: October</source>
          <volume>22</volume>
          ,
          <year>2020</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref19">
        <mixed-citation>
          [19]
          <string-name>
            <given-names>SWI</given-names>
            <surname>Prolog</surname>
          </string-name>
          , home page, https://www.swi-prolog.org,
          <source>Last access: October</source>
          <volume>22</volume>
          ,
          <year>2020</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref20">
        <mixed-citation>
          [20]  Prolog, home page, http://tau-prolog.org,
          <source>Last access: October</source>
          <volume>22</volume>
          ,
          <year>2020</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref21">
        <mixed-citation>
          [21]
          <string-name>
            <given-names>XSB</given-names>
            <surname>Prolog</surname>
          </string-name>
          , home page, http://xsb.sourceforge.net,
          <source>Last access: October</source>
          <volume>22</volume>
          ,
          <year>2020</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref22">
        <mixed-citation>
          [22]
          <string-name>
            <given-names>R.</given-names>
            <surname>Calegari</surname>
          </string-name>
          ,
          <string-name>
            <given-names>E.</given-names>
            <surname>Denti</surname>
          </string-name>
          ,
          <string-name>
            <given-names>S.</given-names>
            <surname>Mariani</surname>
          </string-name>
          ,
          <string-name>
            <given-names>A.</given-names>
            <surname>Omicini</surname>
          </string-name>
          ,
          <article-title>Logic programming as a service</article-title>
          ,
          <source>Theory and Practice of Logic Programming</source>
          <volume>18</volume>
          (
          <year>2018</year>
          )
          <fpage>846</fpage>
          -
          <lpage>873</lpage>
          . doi:
          <volume>10</volume>
          .1017/S1471068418000364, special Issue “
          <article-title>Past and Present (and Future) of Parallel and Distributed Computation in (Constraint) Logic Programming”</article-title>
          .
        </mixed-citation>
      </ref>
      <ref id="ref23">
        <mixed-citation>
          [23]
          <string-name>
            <given-names>F.</given-names>
            <surname>Bergenti</surname>
          </string-name>
          , G. Caire,
          <string-name>
            <given-names>S.</given-names>
            <surname>Monica</surname>
          </string-name>
          ,
          <string-name>
            <given-names>A.</given-names>
            <surname>Poggi</surname>
          </string-name>
          ,
          <article-title>The first twenty years of agent-based software development with JADE</article-title>
          ,
          <source>Autonomous Agents and Multi Agent Systems</source>
          <volume>34</volume>
          (
          <year>2020</year>
          )
          <article-title>36</article-title>
          . doi:
          <volume>10</volume>
          .1007/s10458-020-09460-z.
        </mixed-citation>
      </ref>
      <ref id="ref24">
        <mixed-citation>
          [24]
          <string-name>
            <given-names>B. C.</given-names>
            <surname>Oliveira</surname>
          </string-name>
          ,
          <string-name>
            <given-names>A.</given-names>
            <surname>Moors</surname>
          </string-name>
          ,
          <string-name>
            <given-names>M.</given-names>
            <surname>Odersky</surname>
          </string-name>
          ,
          <article-title>Type classes as objects and implicits</article-title>
          ,
          <source>in: Proceedings of the ACM International Conference on Object Oriented Programming Systems Languages and Applications</source>
          , OOPSLA '10,
          <string-name>
            <surname>Association</surname>
          </string-name>
          for Computing Machinery, New York, NY, USA,
          <year>2010</year>
          , pp.
          <fpage>341</fpage>
          -
          <lpage>360</lpage>
          . doi:
          <volume>10</volume>
          .1145/1869459.1869489.
        </mixed-citation>
      </ref>
      <ref id="ref25">
        <mixed-citation>
          [25]
          <string-name>
            <given-names>E.</given-names>
            <surname>Gamma</surname>
          </string-name>
          ,
          <string-name>
            <given-names>R.</given-names>
            <surname>Helm</surname>
          </string-name>
          ,
          <string-name>
            <given-names>R. E.</given-names>
            <surname>Johnson</surname>
          </string-name>
          , J. Vlissides, Design Patterns:
          <article-title>Elements of Reusable Object-Oriented Software</article-title>
          ,
          <string-name>
            <surname>Addison-Wesley Professional</surname>
          </string-name>
          Computing Series, AddisonWesley, Reading, MA,
          <year>1995</year>
          . URL: https://www.safaribooksonline.com/library/view/ design-patterns-elements/0201633612/.
        </mixed-citation>
      </ref>
      <ref id="ref26">
        <mixed-citation>
          [26]
          <string-name>
            <given-names>G.</given-names>
            <surname>Ciatto</surname>
          </string-name>
          ,
          <string-name>
            <given-names>L.</given-names>
            <surname>Rizzato</surname>
          </string-name>
          ,
          <string-name>
            <given-names>A.</given-names>
            <surname>Omicini</surname>
          </string-name>
          , S. Mariani,
          <article-title>TuSoW: Tuple spaces for edge computing</article-title>
          ,
          <source>in: The 28th International Conference on Computer Communications and Networks (ICCCN</source>
          <year>2019</year>
          ), IEEE, Valencia, Spain,
          <year>2019</year>
          , pp.
          <fpage>1</fpage>
          -
          <lpage>6</lpage>
          . doi:
          <volume>10</volume>
          .1109/ICCCN.
          <year>2019</year>
          .
          <volume>8846916</volume>
          .
        </mixed-citation>
      </ref>
      <ref id="ref27">
        <mixed-citation>
          [27]
          <string-name>
            <given-names>R.</given-names>
            <surname>Calegari</surname>
          </string-name>
          ,
          <string-name>
            <given-names>G.</given-names>
            <surname>Ciatto</surname>
          </string-name>
          ,
          <string-name>
            <given-names>V.</given-names>
            <surname>Mascardi</surname>
          </string-name>
          ,
          <string-name>
            <given-names>A.</given-names>
            <surname>Omicini</surname>
          </string-name>
          ,
          <article-title>Logic-based technologies for multi-agent systems: A systematic literature review</article-title>
          ,
          <source>Autonomous Agents and Multi-Agent Systems</source>
          <volume>35</volume>
          (
          <year>2021</year>
          ) 1:
          <fpage>1</fpage>
          -
          <lpage>1</lpage>
          :
          <fpage>67</fpage>
          . doi:
          <volume>10</volume>
          .1007/s10458-020-09478-3, collection “
          <source>Current Trends in Research on Software Agents and Agent-Based Software Development”.</source>
        </mixed-citation>
      </ref>
      <ref id="ref28">
        <mixed-citation>
          [28]
          <string-name>
            <given-names>R.</given-names>
            <surname>Calegari</surname>
          </string-name>
          ,
          <string-name>
            <given-names>G.</given-names>
            <surname>Ciatto</surname>
          </string-name>
          ,
          <string-name>
            <given-names>A.</given-names>
            <surname>Omicini</surname>
          </string-name>
          ,
          <article-title>On the integration of symbolic and sub-symbolic techniques for XAI: A survey</article-title>
          ,
          <source>Intelligenza Artificiale</source>
          <volume>14</volume>
          (
          <year>2020</year>
          )
          <fpage>7</fpage>
          -
          <lpage>32</lpage>
          . doi:
          <volume>10</volume>
          .3233/IA-190036.
        </mixed-citation>
      </ref>
    </ref-list>
  </back>
</article>