Search code examples
moduleprologswi-prologprolog-assertprolog-directive-dynamic

Modules with dynamic predicates


This is a follow up question to : Adapting csv reading for multiple tables

If I have the following module defined:

:- module(csv_load_mod,[prepare_db/3]).
:- use_module(library(csv)).
:- set_prolog_stack(global, limit(4*10**9)).

prepare_db(File, Column_Key,Relation) :-
   Column_Key_Term =.. [Column_Key,_],
   Relation_Term =.. [Relation,_,_,_],
   retractall(Column_Key_Term),
   retractall(Relation_Term),
   forall(read_row(File, Row), store_row(Row,Column_Key,Relation)).

store_row(Row,Column_Key,Relation) :-
   Column_Key_Test =.. [Column_Key,ColKeys],
   Row =.. [row|Cols],
   (   call(Column_Key_Test)
   ->  Cols = [RowKey|Values],
       maplist(store_relation(Relation,RowKey), ColKeys, Values)
       ;   ( Cols = [_H|T],
             Column_Key_Term =.. [Column_Key,T],
             assertz(Column_Key_Term)
           )
   ).

store_relation(Relation,RowKey, ColKey, Values) :-
    Relation_Term =.. [Relation,RowKey,ColKey,Values],
    assertz(Relation_Term).

read_row(File, Row) :-
    csv_read_file_row(File, Row, []).

Then I am able to read a table from a csv file.

For example:

:? prepare_db('my_table.csv',mt_col_key, mt_relation).

I would then have a fact mt_col_key([col1,col2,...,coln]) and a set of facts mt_relation/3. But these will be local to the module and would not be exported. I need to use csv_load_mod:mt_relation/3 etc. Is there a way to get a module to export a dynamic predicate or adapt prepare_db/3 so the facts it asserts are not local or they get asserted to the module that called it?


Solution

  • I've simplified the applicative logic, to better illustrate the interesting points. We need 3 things: a module 'driver', i.e. test_csv.pl, the generic loader, i.e. csv_module_test.pl, and at least a file, i.e. file.csv

    the driver:

    :- module(test_csv, [test_csv/0]).
    :- use_module(csv_module_test).
    
    test_csv :-
        context_module(CM),
        prepare_db(CM, 'file.csv').
    

    the loader:

    :- module(csv_module_test, [prepare_db/2]).
    :- use_module(library(csv)).
    
    prepare_db(CM, File) :-
        forall(csv_read_file_row(File, Row, []), store_row(CM, Row)).
    
    store_row(CM, Row) :-
        Row =.. [row,RelName|Cols],
        Record =.. [RelName|Cols],
        CM:assertz(Record).
    

    test data, file.csv:

    key,desc,col1,col2,col3,col4,col5
    key_x,desc_x,1,2,3,4,5
    key_y,desc_y,10,20,30,40,50
    

    then,

    ?- test_csv.
    true.
    
    ?- test_csv:listing.
    
    :- dynamic rel/3.
    
    
    test_csv :-
        context_module(A),
        prepare_db(A, 'file.csv').
    
    :- dynamic key/1.
    
    
    :- dynamic key_y/6.
    
    key_y(desc_y, 10, 20, 30, 40, 50).
    
    :- dynamic key_x/6.
    
    key_x(desc_x, 1, 2, 3, 4, 5).
    
    :- dynamic key/6.
    
    key(desc, col1, col2, col3, col4, col5).
    true.
    

    that is, relations have been declared as dynamic and asserted in the driver module...

    note: the relations' names are fake, since I started with an attempt to follow your applicative logic, and steered to a simplified approach later...