Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Organising data and code across modules in Prolog

Tags:

module

prolog

I'm developing a simple web service which is going to add user provided facts to my Prolog database (using assert). I thought it's better to keep these dynamic facts ("data") separate from my service rules that operate on these facts ("code"), hence split them into two different modules. Main reason was that I wanted to persist the dynamic facts to disk periodically, while being able to develop the code with no issues and independently of user data. I've been using assert(my_store:fact(...)) to add user data to the my_store module and then in the code module I started coding rules like

:- module (my_code, [a_rule/1, ...]).

a_rule(Term) :-
   my_store:fact(...), ...

All seems ok but with this approach my_store is hard-coded in the code module, which is a little worrying. For example, what if after some time I decide to change data module name or, perhaps, I'll need two separate data modules one with frequent persistence, another with persistence done only sporadically?

Can anyone advise what are the best practices in code and data organisation? Perhaps separation of code and data is against "the Prolog way"? Are there any good books that cover these issues in depth?

Cheers, Jacek

like image 968
Jacek Avatar asked Mar 12 '23 15:03

Jacek


2 Answers

That's a good question, touching on several very important topics.

I hope that the following comments help you to sort out most of your questions, possibly more so if you follow up on the points that interest you most with new questions that address the specific question in isolation.

  1. First, when accepting user code as input, make sure you only allow safe code to be added to your programs. In SWI-Prolog, there is safe_goal/1, which helps you to ensure some safety properties. It's not perfect, but better than nothing.

  2. Again in SWI-Prolog, there is library(persistency). Please carefully study the documentation to see how it works, and how you can use it to store dynamic data on disk.

  3. Regarding the module names, I have two comments:

    • Explicit module qualifications of goals are very rare. Simply load the module and use its clauses.
    • Note that you can load modules dynamically. That is, nothing prevents you from using use_module/1 with a parameter that was obtained from somewhere else. This allows you more flexibility to specify the module from which you want to fetch your definitions, without having to change and recompile your code.

Regarding separation of code and data: In Prolog, there is no such separation.

All Prolog clauses are Prolog terms.

Terms! TERMS ALL THE WAY DOWN!

like image 176
mat Avatar answered Mar 20 '23 04:03

mat


Thanks to @mat for his suggestions which made me to read and think a little more. Now, I can post a potential solution to my issue; not ideal, not using the persistency library but a simple, first attempt.

As mentioned, user data are stored with assert(my_store:fact(...)). That means module my_store is created dynamically and there's no file which would allow use_module to be used. There's, however, the import/1 predicate which I can use to import dynamically asserted facts, and so my solution looks like this:

:- module(my_code, [a_rule/1, ...]).

:- initialization import_my_store.

import_my_store :-
   import(my_store:fact/1),
   import(my_store:another_fact/1),
   ...

a_rule(Term) :-
   fact(...), ...

Note that I can use fact/1 without explicit specification of the my_store module. And I can also easily dump the user data to a file.

save_db(File) :-
   tell(File),
   my_store:listing,
   told.

The downside is that on initialization the import/1 calls generate warnings such as: import/1: my_store:fact/1 is not exported (still imported into my_code). But that's not a big issue because they are still imported into my_code and I can use the user facts without explicit module specification.

Looking forward to hearing any comments. Cheers,

like image 24
Jacek Avatar answered Mar 20 '23 04:03

Jacek