13.2 Calling Prolog from JavaScript
The Prolog
class provides several methods for calling
Prolog from JavaScript.
- Boolean Prolog.call(Goal)
- Processes a Prolog goal represented as a
String
and returnstrue
orfalse
. This simple calling pattern is intended for trivial goals such as setting a Prolog flag. For example, the call below limits the Prolog stacks to 10Mb.Prolog.call("set_prolog_flag(stack_limit, 10 000 000)");
- Query Prolog.query(Goal)
- Query Prolog.query(Goal, Input)
- Create a Prolog query from a
String
, optionally binding Prolog variables embedded in Goal from properties of theObject
Input. The returned object is an instance of classQuery
. This instance can be used as a JavaScript iterator. The value returned in each iteration is anObject
with properties for each variable in Goal that is not in Input and does not start with an underscore. For example, we can iterate over the members of a list like below. Further details on classQuery
are provided in section 13.2.1. The translation of data between Prolog and JavaScript is described in section 13.2.2.for(let r of Prolog.query("member(Elem,List)", {List: ["aap", "noot", "mies"]})) { console.log(r.Elem); }
This interface is also practical for calling (fast) Prolog predicates to compute a single answer from an input using the Query.once() method. Assuming a Prolog predicate fib/2 that computes the nth Fibonacci number, we can call this using the code below. Note that if the fib/2 fails or raises an exception the object returned by Query.once() does not contain the
Out
key and thus our function returnsundefined
.function fib(in, out) { return Prolog.query("fib(In,Out)", {In:in}).once().Out; }
The
.query()
method is indented for fast queries that do not require the yield mechanism, i.e., the execution should not require asynchronous operations and the browser is not responsive during the execution. - Promise Prolog.forEach(Goal)
- Promise Prolog.forEach(Goal, OnAnswer)
- Promise Prolog.forEach(Goal, Input)
- Promise Prolog.forEach(Goal, Input, OnAnswer)
- This method executes Goal asynchronously. This implies that
Goal may execute asynchronous operations and the browser
remains responsive while executing Goal. Goal and Input
are processed as with
.query()
. For each answer, OnAnswer is aFunction
that is called with aObject
that holds the bindings for the output arguments of Goal.The returned
Promise
is resolved when the query completes. The value passed to the.then()
method of thePromise
is the number of answers if OnAnswer is provided or anArray
of answers if OnAnswer is omitted. If Goal raises an exception thePromise
is rejected.Multiple calls to Prolog can be activated at any time. Prolog processes such queries in LIFO (Last In, First Out) mode. If queries need to be processed sequentially use JavaScript
await
or thePromise.finally()
method to wait for completion.
13.2.1 The JavaScript class Query
The method Prolog.query()
returns an instance of the JavaScript class Query
that may
be used to explore the solutions of the query. The Query
class implements the JavaScript iterator protocol.
- Object Query.next()
- Implements the iterator protocol. This returns an object
holding the keys
done
andvalue
. If exception handling is enabled it returns an object {done
:true
,error
:true
,message
:String}. - Object Query.once()
- Close the query after the first answer. Returns the
.value
of the object returned by.next()
on success and the complete object on failure or error. In addition, on a logical result (no error), a fieldsuccess
is added with a boolean value. Thus, the return value may contain these keys:- {Bindings}
- Query succeeded. Objects holds a key for each output variable. In
addition the
success
key is set totrue
. - {
success
:false} - Query failed. Note that the binding keys all start with an uppercase letter and this is thus not ambiguous.
- {
error
:true,message
:String} - Query raised an exception.
- Object Query.close()
- Closes the query. This can be used inside the iterator to stop further enumeration.
13.2.2 Translating data between JavaScript and Prolog
JavaScript and Prolog are both dynamically typed languages. The WASM module defines a faithful translation between JavaScript data and Prolog data that aims at completeness as well as keeping the data representation clean in the common cases. We describe the translation in two descriptions because round tripping does not always result in the original object.
13.2.2.1 Translating JavaScript data to Prolog
This section describes how data from JavaScript is translated into Prolog. The interface is primarily designed for passing JavaScript data as typically used to a natural Prolog representation. In addition a number of classes are provided to create Prolog specific data structures such as strings (as opposed to atoms), variables, compound terms, etc.
- Number
- Translate to a Prolog integer or floating point number.
- BigInt
- Translate to a Prolog integer.
- String
- Translate to a Prolog atom. Use
new Prolog.String(text)
to create a Prolog string. See below. - Boolean
- Translate to one of the Prolog atoms
true
orfalse
. - undefined
- Translate the Prolog atom
undefined
. - null
- Translate the Prolog atom
null
. - Array
- Translate to a Prolog list.
- Objects holding the key
$
:Type - Such objects are converted depending on the value for this key. The
interface defines classes to simplify creating such objects.
- s
- Represent a Prolog string. The key
v
holds the text. May be created usingnew Prolog.string(text)
. May be created usingnew Prolog.String(text)
. - r
- Represent a Prolog rational number. The keys
n
andd
represent the numerator and denominator. For example, to represent1r3
, use {$
:"r",n
:1,d
:3}. May be created usingnew Prolog.Rational(n, d)
, where n and d can be JavaScript numbers or big integers. - t
- Represent a Prolog compound term. The object should hold
exactly one key whose value is an array that holds the argument values.
For example a term
point(1,2)
is constructed using {$
:"t",point
:[1,2]}. May be created usingnew Prolog.Compound(functor, args)
- v
- Represent a variable. If the key
v
is present this identifies the variable. Two variables processed in the same translation with the same identifier represent the same Prolog variable. If thev
key is omitted the variable will be unique. May be created usingnew Prolog.Var(id)
. - l
- Represent a Prolog list. As a JavaScript
Array
we only need this typed object to create a partial list. Thev
key contains the “normal” elements and the keytail
contains the tail of the list. May be created usingnew Prolog.List(array, tail)
.
- Object of class
Object
- Plain JavaScript objects are translated into a Prolog
dict
. Note that JavaScript object keys are always strings and (thus) all dict keys are atoms. This, {1:"one"} is translated into_{'1': one}
. - ArrayBuffer
- Instances of
ArrayBuffer
are translated into a Prolog string that consists of characters in the range 0 ... 255. - Objects of a one class not being
Object
- Instances of non-plain JavaScript objects are translated into a Prolog blob.
Such objects are written as
<js_Class(id)>
. The Prolog interface allows for passing the objects back and calling methods on them. See section 13.3.
13.2.2.2 Translating Prolog data to JavaScript
Most of the translation from Prolog data to JavaScript is the reverse
of the translation described in section
13.2.2.1. In some cases however reverse translation is ambiguous.
For example, both
42
and 42n
(a JavaScript BigInt
)
translate to a simple Prolog integer. The other way around, as
JavaScript
Number
is a float, both Prolog 42
and 42.0
translate to 42
in JavaScript.
- Variable
- Translate to a JavaScript
Prolog.Variable
instance where the identifier is a unique number of each unique variable. - Integer
- Translate to a JavaScript
Number
when possible orBigInt
otherwise. Currently JavaScriptNumber
can represent integers upto 2^53 precisely. - Rational
- Translate to a JavaScript
Prolog.Rational
instance. - Float
- Translate to a JavaScript
Number
. - Atom
- Translate to a JavaScript
String
. - String
- Translate to a JavaScript
Prolog.String
instance. - List
- When a proper list create a JavaScript
Array
, otherwise create a JavaScriptProlog.List
instance. - Compound term
- Create a JavaScript
Prolog.Compound
instance. - Dict
- Create a plain JavaScript
Object
with the same keys. If the dict has a non-var tag, add a$tag
property.