I used to buy and collect books about computer technologies since my childhood and in 1990 I bought a book titled as "Prolog programming for Artificial Intelligence" by Ivan Bratko but didn't have enough time to read it for about 20 years. In 2009 I found some free time to read the book and was amazed by the power of the computer language. To learn it better I wrote small Java based Prolog embeddable engine and called it JProl.
In 2014 Yuki Katsura made iPad version.
Initially the engine was a mono-block with embedded GUI part, but later it reworked and split solid-module into two modules: the engine and the GUI editor.
The engine is published in the maven central and can be used in maven projects as a dependency. It supports JDK 1.8+ and also I keep it compatible with Android API.
<dependency>
<groupId>com.igormaznitsa</groupId>
<artifactId>jprol-core</artifactId>
<version>2.1.2</version>
</dependency>
It uses another project java-prolog-parser which initially was a part of the JPprol engine but then it was extracted into autonomous module because sometime it is useful to have only prolog parser for data files.
The engine communicates with Java methods marked by special annotationas and having special signature.
For instance, below I show how implemented the <</2
predicate of the core JProl library. As you can see, the method is just marked by @JProlPredicate
annotation. It is imp[ossible just mark any method but only methods with special signature and arguments JProlChoicePoint
and TermStruct
because they provided during a call.
@JProlPredicate(evaluable = true, signature = "<</2", args = {
"+evaluable,+evaluable"}, reference = "Bitwise left shift")
public static Term predicateSHIFTL2(final JProlChoicePoint goal, final TermStruct predicate) {
final NumericTerm left = calculatEvaluable(goal, predicate.getElement(0).findNonVarOrSame());
final NumericTerm right = calculatEvaluable(goal, predicate.getElement(1).findNonVarOrSame());
final long value = left.toNumber().longValue();
final long shift = right.toNumber().longValue();
return Terms.newLong(value << shift);
}
Sample below shows how it is easy to inject a prolog based Eight Queens puzzle resolver into Java and get all solutions.
JProlContext context = new JProlContext(
"test-context",
new JProlCoreLibrary()
);
context.consult(new StringReader(
"solution([]). solution([X/Y|Others]):-solution(Others),member(Y,[1,2,3,4,5,6,7,8]),notattack(X/Y,Others). notattack(_,[]). notattack(X/Y,[X1/Y1 | Others]):- Y=\\=Y1, Y1-Y=\\=X1-X, Y1-Y=\\=X-X1, notattack(X/Y,Others). member(Item,[Item|Rest]). member(Item,[First|Rest]):-member(Item,Rest). template([1/Y1,2/Y2,3/Y3,4/Y4,5/Y5,6/Y6,7/Y7,8/Y8])."));
JProlChoicePoint goal = new JProlChoicePoint(
"solution([1/Y1,2/Y2,3/Y3,4/Y4,5/Y5,6/Y6,7/Y7,8/Y8]),Res = [Y1,Y2,Y3,Y4,Y5,Y6,Y7,Y8].",
context);
Term result;
while((result = goal.prove()) != null) {
TermList solution = (TermList) goal.findVar("Res").get().getValue();
System.out.println(solution.toSrcString());
}
The bootstrap library of the engine has two predicates set_prolog_flag/2
and current_prolog_flag/2
. They can be used to tune work of the engine or get extra info. Not all of them still supported.
- Flags:
- address_bit (read only) address size of the hosting machine. Typically 32 or 64.
- allow_variable_name_as_functor (read only)
- os.name (read only)
- os.arch (read only)
- bounded (read only) if true, integer representation is bound by min_integer and max_integer
- debug switch debugging mode true/false
- dialect (read only) fixed to
jprol
- encoding default encoding used for opening files in text mode
- gc (read only) if true (default), the garbage collector is active
- max_arity (read only) flag describing there is no maximum arity to compound terms
- max_integer (read only) maximum integer value if integers are bounded
- min_integer (read only) minimum integer value if integers are bounded
- cpu_count (read only) number of physical CPUs or cores in the system
- unknown determines the behaviour if an undefined procedure is encountered. If fail, the predicate fails silently. Allowed
error
,fail
orwarning
by defaulterror
- home (read only) notion of the user home directory
- verify
- version_data (read only)
Prolog is a declarative language and it is well for multi-threading. I have added pair predicates to span new threads (fork/1
and async/1
), also the engine provides special predicate waitasync/0
to make inter-process synchronization to wait completion of all spawned threads. The waitasync/0
predicate can be used only from the main thread (the root goal). Also there is support for locks. You can create critical sections with lock/1
, unlock/1
and trylock/1
predicates. Take a look at the prolog program below, it draws 3000000 color dots through three threads.
threadRed(P) :- for(_, 0, P), rnd(500, X), rnd(400, Y), lock(gfx), pencolor(red), dot(X, Y), unlock(gfx), fail.
threadGreen(P) :- for(_, 0, P), rnd(500,X), rnd(400, Y), lock(gfx), pencolor(green), dot(X, Y), unlock(gfx), fail.
threadBlue(P) :- for(_, 0, P), rnd(500, X), rnd(400, Y), lock(gfx), pencolor(blue), dot(X,Y), unlock(gfx), fail.
?- P = 1000000, time((graphics(500,400), fork([threadRed(P), threadGreen(P), threadBlue(P)]))).
For demo I have written a small GUI application. It is a version of Conway's Life game where all business rules for colony described in prolog part.
For my prolog learning purposes I maded small GUI script editor which allows to execute prolog scripts and even make some graphics. It is not very strong in debugging but can be used for start of small scripts and test programs.
Its pre-built version for misc OS can be downloaded from the release page. It contains as standcalone versions as versions with embedded JDK.
JProl GUI editor module provides predicates to work with graphics and save generated images. As an example, below you can find a program to draw a Koch snowflake fractal.
kochsnowflake(N,L) :- settitle('Koch snowflake'), brushcolor(white), graphics(300,300), pencolor(red), time((fractal(N,30,80,L,0,X1,Y1), fractal(N,X1,Y1,L,2.0943951024,X2,Y2), fractal(N,X2,Y2,L,4.1887902048,_,_))).
fractal(0,X,Y,L,A,XN,YN):- !, XN is X+L*cos(A), YN is Y+L*sin(A), XX is round(X),XXN is round(XN), YY is round(Y),YYN is round(YN), plot(XX,YY,XXN,YYN).
fractal(I,X,Y,L,A,XN,YN):- II is I-1, LL is L/3, A2 is A-1.0471975512, A3 is A+1.0471975512, fractal(II,X,Y,LL,A,X1,Y1), fractal(II,X1,Y1,LL,A2,X2,Y2), fractal(II,X2,Y2,LL,A3,X3,Y3), fractal(II,X3,Y3,LL,A,XN,YN).
?-kochsnowflake(5,300).