prologdcglogtalk

Accessing SWI-Prolog libraries from Logtalk


I'm having a lot of fun using Logtalk, but ran into an issue using phrase_from_file. Specifically, my case looks something like this:

:- object(scan_parser).

   :- public(scanlist//1).
   scanlist([Scan|Scans]) --> scan(Scan), dcg_basics:blanks, scanlist(Scans).
   scanlist([]) --> [].

   :- public(scan_file/2).
   :- mode(scan_file(+filename, -scans), one).
   scan_file(Filename, Scans) :- pio:phrase_from_file(scanlist(Scans), Filename).

   ...
:- end_object.

The trouble is all in that call to phrase_from_file. It's unable to find scanlist, presumably because it is local to this object, so I get this error:

?- scan_parser::scan_file('input.txt', Scans).
ERROR: phrase/3: Undefined procedure: pio:scanlist/3

But, if I try to aggrandize it with a module reference like so:

scan_file(Filename, Scans) :- pio:phrase_from_file(::scanlist(Scans), Filename).

I get this error:

?- scan_parser::scan_file('input.txt', Scans).
ERROR: phrase/3: Undefined procedure: pio: (::)/3

Same if I use pio:phrase_from_file(this::scanlist(Scans), Filename) or pio:phrase_from_file(scan_parser::scanlist(Scans), Filename). If I use a single colon instead in emulation of SWI's module facility, I get messages like ERROR: phrase/3: Undefined procedure: scan_parser:scanlist/3.

I assume that the problem here is that SWI's PIO library is trying to construct something to hand to phrase and it's just not intelligent enough. But this is something that comes up for me a lot, using phrase_from_file/2, and I'm sure there will be other times I want to excavate something from SWI's library and borrow it. What's the right way forward? I'd like to preserve the encapsulation of Logtalk as much as possible.

Thanks!


Solution

  • I'm designing a general solution for Logtalk 3.x to support Prolog module meta-predicates that take closures as meta-arguments. Meanwhile, can you try the following (ugly) workaround:

    % ensure the module is loaded
    :- use_module(library(pio)).
    
    
    :- object(scan_parser).
    
        % override the non-standard meta-arguments declarations
        :- meta_predicate(pio:phrase_from_file(2,*)).
    
       :- public(scanlist//1).
       scanlist([Scan|Scans]) --> scan(Scan), dcg_basics:blanks, scanlist(Scans).
       scanlist([]) --> [].
    
       :- public(scan_file/2).
       :- mode(scan_file(+filename, -scans), one).
       scan_file(Filename, Scans) :- pio:phrase_from_file(user:scan_parser_scanlist(Scans), Filename).
    
        {scan_parser_scanlist(Scans, A, B)} :-
            phrase(scanlist(Scans), A, B).
    
        ...
    
    :- end_object.
    

    I cannot test as you only posted part of the object code.