Creating prolog clauses with prolog_load_file/2

Hi,
How can I create one or more prolog clauses using hook “prolog_load_files”?
According to the documentation, prolog_load_file/2 is used to

…deal with Prolog code that is not represented as a Prolog source text…

However, I could not find any documentation how to create the clauses I would need.
Example: I have the following file test.pl:

:- multifile mydata/1.

user:prolog_load_file(Spec, _Options) :-
	strip_module(Spec, Mod, File)
	, atom(File)
	, file_name_extension(_Base, mydat, File)
	, writeln(creating_clauses_from_file(Mod, File))
	
	**% here we want to parse File and create facts in the form "mydata(a)."**
	.

:- load_files('test.mydat').

Which I then call with “swipl test.pl”, with the following result:

*swipl test.pl*
**creating_clauses(user,test)**

Welcome to SWI-Prolog (threaded, 64 bits, version 8.3.4)
SWI-Prolog comes with ABSOLUTELY NO WARRANTY. This is free software.
Please run ?- license. for legal details.
For online help and background, visit https://www.swi-prolog.org
For built-in help, use ?- help(Topic). or ?- apropos(Word).
?-

My question is: Once I have parsed my file, how can I add the resulting clauses to my program?
Thanks for any support

Not an exact answer but maybe of use.

In searching the GitHub SWI-Prolog repositories for prolog_load_file/2 found one example.

Thanks for this feedback.
I came across this example, too, but in the end, this one does nothing more than open an http-stream, then load_file from this stream…
Not really what I am looking for.


setup_call_cleanup(
http_open(FinalURL, In,
[ cert_verify_hook(ssl_verify)
]),
load_files(Module:FinalURL, [stream(In)|Options]),
close(In)).

Before I try to answer your question, I need to ask something:

Is there a particular reason why you are writing text to a text file? Without understanding the issue you are solving, there are at least two other things you could try:

  1. Directly use assertz/1 (also works with modules I think);
  2. Use term_expansion/2.

From what I see (correct me if I am wrong) it seems that you are writing text to a file and then loading the contents as if it were Prolog code; this feels like an overkill for most use cases (and it will be not easy to debug either…)

That is completely up to you. Most of the implementations of this hook use it to get a handle to a stream containing Prolog source and than call load_files/2 using the stream(Input) option to do the actual loading. This is typically used to load code from (web)servers, databases, etc. You can do anything you want in the hook though.

You do not need this hook if your only problem is the file name extension. You can load any file containing Prolog code simply by adding the file name extension or use prolog_file_type/2 to define your extension as a valid alternative extension. There is an extensive example on tweaking input in library/dialect/xsb to read XSB’s .P files.

1 Like

Well, my file does not contain prolog code at all. It has some structured data from an external source, that I can easily transform into prolog code.
So I could actually do the following:

  1. read my external file “test.mydat”,
  2. transform it into a corresponding prolog file eg: “test_mydat.pl”, and
  3. load “test_mydat.pl” in my prolog application.

The problem with this approach is that I would have to update “test_mydat.pl” each time the underlying source would change.

But if I understand you well, 1 could do the following within my “prolog_load_file” hook:

  1. read my external file “test.mydat”,
  2. transform it into a corresponding prolog code and write this to some temporary file,
  3. open the temporary file
  4. and give this stream to load_files with the stream(In) option

I would have thought there is something less convoluted to add the clauses to the program but I will give this a try.
Thanks

That should work. You do not really need to create a file, you can also put it in a memory file or string and open that. You can also parse the data to clauses and assert them, but that skips all the source administration and thus makes reloading the modified original file less obvious. You probably also want to use the modified(Time) option when loading the temporary file.

Tested this approach, and is working well.
However, loading a bunch of files (using load_files with option expand(true)) seems very slow.
But anyway, thanks for your valuable input

Perhaps it is loading them sequentially, try using the new concurrent_forall/2 and see if you can load them in different threads.

EDIT: What I mean is something like this:

parallel_load(ListOfFiles) :-
   concurrent_forall(  member(File,ListOfFiles),
                       load_files([File])).
2 Likes

For concurrent usage, combine with directory_member/3. First step I’d try is to do the load under profile/1 and see why it takes so long.

1 Like

I can’t reproduce the slowness anymore!
So the following now seems to work for me:

In “load.pl

 :- use_module(lib/mydat).
 :- load_files('mydata/*/*.mydat', [expand(true), if(changed)]).

and in module mydat:

:- multifile fact/4.
 
user:prolog_load_file(Spec, _Options) :-
 	strip_module(Spec, _Mod, File)
 	, atom(File)
 	, file_name_extension(_Base, mydat, File)
 	, time_file(File, TimeStamp)
 	, read_mydat(File, Facts)
 	, setup_call_cleanup(
 		new_memory_file(Handle)
 		, (
 			setup_call_cleanup(
 				open_memory_file(Handle, write, MemOut)
 				, maplist(write_fact(MemOut), Facts)
 				, close(MemOut)
 			)
 			, open_memory_file(Handle, read, MemIn)
 			, load_files(Spec, [stream(MemIn), modified(TimeStamp)])
 		)
 		, free_memory_file(Handle)
 	)
 	.

While you can use image to make source code appear different, a better way with Discourse and most sites that use Markdown is to use ``` fences, e.g.

This is the raw code

lines([H|T]) :-
   line(H),
   lines(T).
lines([]).

This is the code as it appears using image

lines([H|T]) :-
line(H),
lines(T).
lines().

This is the code as it appears using ``` fences,

lines([H|T]) :-
   line(H),
   lines(T).
lines([]).

This is how it is entered into the Discourse editor using ```.

```
lines([H|T]) :-
   line(H),
   lines(T).
lines([]).
```

:smiley:

happy you told me. Next time I will do
Thanks

1 Like