2

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!

Daniel Lyons
  • 22,421
  • 2
  • 50
  • 77
  • Hi Daniel. Which Logtalk version are you using? – Paulo Moura May 15 '13 at 17:31
  • I'm using 2.44.1, but I will try against 3.0 if you think it may not be a problem there. You may also need to create 'input.txt' before the error becomes evident. – Daniel Lyons May 15 '13 at 17:32
  • I strongly suggest that you move to Logtalk 3.x. But you stumble on a Logtalk limitation (which is independent of the version you use): module meta-predicates that take closures are not supported (see the Prolog migration section on the User Manual). Let me think for a moment on a alternative solution, however. – Paulo Moura May 15 '13 at 17:40
  • Same behavior under 3.0 HEAD. – Daniel Lyons May 15 '13 at 17:43
  • The root of the problem is that Logtalk compiles object predicates by (among other transformations) adding a single argument at the end (used to pass the execution context). This clashes with using module meta-predicates that take closures as the module will (usually with a direct or indirect call to call/N) append the additional arguments to go from a closure to a goal and the hidden execution context argument will be caught in the middle (Prolog module systems are not Logtalk-aware). – Paulo Moura May 15 '13 at 17:59
  • Btw, I tried to simply compile them module "pio" as an object but the chain of dependencies (some of them undeclared and some containing SWI-Prolog only proprietary stuff) for this module makes that hard. – Paulo Moura May 15 '13 at 18:00

1 Answers1

3

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.

Paulo Moura
  • 18,373
  • 3
  • 23
  • 33
  • Thanks! I am happy to have a workaround in case I run into other similar situations. – Daniel Lyons May 15 '13 at 20:16
  • The just updated Logtalk 3.x HEAD should provide you with faster generated code for (:)/2 non-terminals as in the code you posted. – Paulo Moura May 15 '13 at 21:52
  • The limitation described above (regarding calling Prolog module meta-predicates that take closures as meta-arguments) is lifted in the latest Logtalk 3.x HEAD. – Paulo Moura May 16 '13 at 00:55
  • Logtalk 3.00.0 Alpha 17 include the changes to support calling from within object and categories both Prolog built-in meta-predicates and Prolog module meta-predicates that take closures as meta-arguments. The code you posted should work as-is without needing any workaround. – Paulo Moura May 20 '13 at 20:38