mthom / scryer-prolog

A modern Prolog implementation written mostly in Rust.
BSD 3-Clause "New" or "Revised" License
2.06k stars 123 forks source link

use_module and raw file inclusion #583

Open cduret opened 4 years ago

cduret commented 4 years ago

I would like to split a module file into a module declaration file and a raw file in order to be able to load the raw file with other prolog engines (I use tuprolog with java) that do not implement module system.

[I] cydu@darkstar ~/s/p/p/test_module> ls
test.pl  test_raw.pl
[I] cydu@darkstar ~/s/p/p/test_module> more test.pl 
:- module(test, [
  foo/1
]).

:- [test_raw].
[I] cydu@darkstar ~/s/p/p/test_module> more test_raw.pl 
foo(bar).

If I try to load that file it fails :

[I] cydu@darkstar ~/s/p/p/test_module> scryer-prolog
?- use_module(test).
caught: error(syntax_error(inconsistent_entry),use_module/1)
?-

But it works with swipl :

[I] cydu@darkstar ~/s/p/p/test_module> swipl
Welcome to SWI-Prolog (threaded, 64 bits, version 8.0.3)
SWI-Prolog comes with ABSOLUTELY NO WARRANTY. This is free software.
Please run ?- license. for legal details.

For online help and background, visit http://www.swi-prolog.org
For built-in help, use ?- help(Topic). or ?- apropos(Word).

?- use_module(test).
true.

?- foo(X).
X = bar.

?-

Is it a case that should be supported in the module system or is it an edge case ?

thanks

triska commented 4 years ago

In my opinion, use_module/1 should require a proper module.

You can still easily consult the file also in Scryer Prolog even without a module declaration, by specifying it on the command line:

$ scryer-prolog test.pl

From within Prolog, you can use consult/1 to consult the file:

?- consult('test.pl').
   true.

You can use the initialization/1 directive to load the file from other files:

:- initialization(consult('test.pl')).
cduret commented 4 years ago

It works except that you cannot export some predicates from within the consulted file, the module throw a module_does_not_contain_claimed_export error.

The most annoying problem is that I have to set an absolute path to the consulted file otherwise I could not run a file from another working directory.

pmoura commented 4 years ago

Two common solutions are (1) to use the standard include/1 directive or (2) de facto standard compilation directives. For example:

:- module(foo, [bar/1]).

:- include(raw).

Or:

:- if(current_prolog_flag(prolog_dialect, screyer)).
    :- module(foo, [bar/1]).
:- endif.

% raw contents

But I don't think these directives are already supported in either Scryer Prolog or Tu-Prolog. But please correct me if I'm wrong.

A third alternative would be to use Logtalk and then run the code as-is on multiple Prolog systems. But there are still missing bits in Scryer Prolog for that to be possible. More so with Tu-Prolog. Btw, have you looked into other Java-based Prolog implementations such as JIProlog?

cduret commented 4 years ago

I have tested the include directive but is not working (working on swipl).

It could be nice to have the include directive implemented when we want to share files betweeen prolog systems.

For now I will look at JIProlog, it appears to support the module/use_module so will be easier to share files with scryer, thanks ;-)

cduret commented 4 years ago

I am using jekejeke for my java backend and I still haven't found a clean way to share prolog files between jekejeke and scryer.

Just read #511 and I hope we will have a flag to recognize scryer implementation.

Still need some kind of preprocessor

I have seen this interesting module in jekejeke sample repository : preprocessor.pl

Would it be feasible to port this module to scryer ?

triska commented 4 years ago

The include/1 directive would be useful to implement this, I have filed #634 for it.

Please note that it ought not to be necessary to recognize any specific Prolog implementation in order for this standard feature to work. In my experience, having a flag to detect the Prolog system leads to programs that are less portable, and blocks the path to stronger cooperation between systems by shifting the work of system implementors to Prolog application programmers.

triska commented 4 years ago

Regarding the preprocessor: As is, this is very limited in practice, because it cannot be used within Prolog terms, and that is often required in practice.

For instance, suppose a portable library(crypto), which exports certain predicates based on which lower-level features are provided in a Prolog system. Conceptually, what is needed to support this is a preprocessor that can be used like this:

:- module(crypto,
          [hex_bytes/2,
           crypto_n_random_bytes/2,
           crypto_data_hash/3,
           crypto_data_hkdf/4,
           crypto_password_hash/2,
           crypto_password_hash/3,
           crypto_data_encrypt/6,
           crypto_data_decrypt/6,
:- if(ed25519_supported).
           ed25519_new_keypair/1,
           ed25519_keypair_public_key/2,
           ed25519_sign/4,
           ed25519_verify/4,
:- endif.
           crypto_name_curve/2,
           crypto_curve_order/2,
           crypto_curve_generator/2,
           crypto_curve_scalar_mult/4
          ]).

However, this cannot be supported with the preprocessor in its current form, severely limiting its applicability in practice.

cduret commented 4 years ago

I agree preprocessor directives are not the best things to use. I remember huge C code base from the 90's was hell to maintain with platform specific code scattered everywhere.

triska commented 4 years ago

I have a similar experience with supporting different versions of OpenSSL in code.

In my experience, at least one important takeaway is to test for features instead of specific systems and versions. This makes support much more general, because all systems that have a feature are then automatically supported.