Rabbits Example

Jekejeke Prolog allows the grouping of predicates to modules. The given example shows mutual recursion among modules. We are going to work on a problem of mathematical biology. A certain man put a pair of rabbits in between walls. How many pairs of rabbits can be produced from that pair in a year if it is supposed that every month each pair begets a new pair which from the second month on becomes productive?

We will model the problem by two predicates adults/2 and babies/2. Each predicate has a first argument for mount count starting with zero and second argument for the corresponding size of the population counted in rabbit pairs. The dynamics of the two populations is depicted below. In each month the number of adults grows by the number of babies. On the other hand in each month there are new babies in the number of adults.

We will place the predicate adults/2 in a module named cage and the predicate babies/2 in a module named nest. In Jekejeke Prolog a module resides in a Prolog text. The Prolog text has to start with the module/2 directive. This directive states the name of the module and the exported predicates. The declaration reads as follows for the cage module:

:- module(cage, [adults/2]).

As a next step the Prolog text is allowed to import other modules. This is done with the use_module/1 directive. The directive has to mention the file name of the module that has to be imported. We will place the module cage in a file ‘cage.p’ and the module nest in a file ‘nest.p’. The directive for the import of the module nest into the module cage thus reads as follows. Importing a module makes the public predicates visible.

:- use_module('nest.p').

The first rule for the adults/2 predicate reads as follows. It states that for the first month there is one adult pair in the cage:

adults(0, 1) :- !.

Without visibility a module predicate has to be accessed by qualifying the module name via the colon (:)/2 operator. So for example to access the babies/2 predicate inside the module nest we would need to call nest:babies(M, Z). When the module nest has been imported it is enough to write babies(M, Z). We can take advantage of the visibility when formulating our second rule for the predicate adults/2. It states that the population of the adults grows by the number of babies:

adults(N, X) :-
   N > 0, M is N-1,
   adults(M, Y), babies(M, Z), X is Y+Z.

The Prolog text of the other module nest can be found in the appendix. As a next step we can import the module we want to work with into the top-level. This is also done by the use_module/1 directive. We will import the cage module, since we are interested in adult population. The Jekejeke Prolog interpreter will show on the console the time and size of the loading of the module. The Jekejeke Prolog interpreter will also show the loading of the directly or indirectly dependent modules:

?- use_module('cage.p').
% 'nest.p' consulted, 19 lines in 4 ms.
% 'cage.p' consulted, 18 lines in 8 ms.

We can now determine the adult population after a year:

?- adult(12, X).
X = 233

The example shows that the Jekejeke Prolog interpreter will not loop when loading recursive module imports. Technically the interpreter performs a traversal of a possibly cyclic import graph. The example also shows that no special forward declarations are necessary to executed predicates from recursive module imports. Technically the interpreter lazily binds predicates to atoms via small local caches at runtime.

The example can as well be run with the help of the graphical user interface of the Jekejeke Prolog interpreter on the Swing or on the Android platform. The “Load File …” respectively “Load …” menu item can be used to find the file for the use_module/1 directive. When a consult or ensure loaded is called from within a Prolog text the directive will resolve a relative file name based on the location of the current Prolog text. This explains how the module cage finds the module nest as long as they reside in the same directory.