Refactor of doom: inter-module issue regarding caller / callee

I moved some code from one module into another, exported the operators as documented,

:- module(utils,
          [
              load_source/2,
              pos_linecol/3,
              get_dict/4,
              % operator exports
              op(600, xfx, =<<),
              op(600, fx, <<),
              op(700, yfx, ##),
              op(700, yfx, ###),
              (=<<)/4,
              (<<)/3,
              (##)/4,
              (###)/4
          ]).

but then 36 unit tests failed…they all are failing because now that the operators are defined in the “utils” module, when the operator does the call/1 it is saying that the callable is not found in the module… does this mean I have to explicitly provide the module name as well? I would have thought it knew where to call back to but maybe not?!

Here is the debugger error:
call-failed
basically Callable is in module “ast” and the operator is in module “utils”. one_term_value/3 is define in module “ast”.

I read By-module operators but I think my problem is different.
Thanks
:expressionless:

Operators only affect syntax. Seems (##)/2 is a meta predicate. Is there are declaration

:- meta_predicate ##(0,0,?,?).

somewhere?

I had no idea a “meta-predicate” even existed!!! I have no such declaration anywhere, I wouldn’t know what to do with one but now it seems I might!! :smiley:

OK…I went away and did some reading…

https://www.swi-prolog.org/pldoc/man?section=metapred

Thanks again Jan, I can probably figure it out…this is a first moment for me and Prolog, I felt cool enough defining the operators in the first place and now I go and get all fancy by trying to re-use them in other modules! That’ll teach me! :slight_smile: :+1:

This has nothing to do with operators. You need meta_predicate declarations if you put
a meta predicate in a module and export it. Traditionally the declaration must be before the code, typically after the module header and imports. SWI-Prolog execution doesn’t care, but the development tools only scan modules upto the first non-directive.

A meta predicate is a predicate that accepts one or more arguments that must be resolved to a predicate in the module of the caller. You’ll find lots of them in the library.

1 Like

Brilliant. Thanks Jan. Always more to learn… :slight_smile:

0…9
The argument is a term that is used to reference a predicate with N more arguments than the given argument term. For example: call(0) or maplist(1, +).

How do I know what value to use if I don’t know ahead of writing the code what I will be doing with it? I feel dumb today…

It depends what the meta predicate does with the argument. If it uses call/1 on it, the argument must be declared as 0. If it uses call/2, 1, etc. If it doesn’t call at all, but for some other reason wants to know the module to which the argument must be resolved, use :. If no module is involved for an argument, use *, ?, + or -. They are mode annotations that have traditional documentary value. The system does nothing with this.

Actually, the implementation only distinguishes meta from non-meta arguments. If the argument is a meta argument, the provided (Arg) value is transformed into a term SourceModule:Arg. Otherwise it is left untouched. The 0…9 argument tells the cross-referencer what happens. If it sees foo##bar(x), both annotated with 0, it knows this calls foo/0 and bar/1. If these would be annotated as 1, it knows this calls foo/1 and bar/2, etc.

I am nearly there…the ## and ### are fine now it is the << and =<< that are giving me errors still. I am reading my code -closely- to see what I have is what the mode declarations said.

Warning: The predicates below are not defined. If these are defined
Warning: at runtime using assert/1, use :- dynamic Name/Arity.
Warning: 
Warning: ast:<<</1, which is referenced by

<<< was <<, I didn’t realise it was a shift operator so I renamed for clarity in my own head, anyway the code for it…

:- meta_predicate <<<(+,+,-).
:- op(600, fx, <<<).
% exported as: (<<<)/3,

<<<(FunctorOrList) --> _ =<< FunctorOrList.


Term =<< FunctorOrList -->
    [Term],
    {   is_list(FunctorOrList)
    ->  functor(Term, F, _),
        memberchk(F, FunctorOrList)
    ;   functor(Term, FunctorOrList, _)
    }.

<<< is thus called from within a DCG rule so the arity is 3 because of the DCG lists on the end but the error message says <<</1 not <<<//1 so I still can’t see what’s wrong!

changed ## and ### to // for their arguments, that has reduced my error count…I guess this is all part of the rich tapestry of learning something new,…sorry for the noise

:- meta_predicate =<<(+,-,+,-).
:- meta_predicate <<<(+,+,-).
:- meta_predicate ##(//,?,?,?).
:- meta_predicate ###(//,?,?,?).

Did the trick…back to clean compile and only a few failing tests but that’s cool.

1 Like

I don’t see it as noise.

While I am aware that meta predicates exist, I know next to nothing about them in Prolog. Oh I do see them often when reviewing the source code for SWI-Prolog or in some other packs or packages but they are mostly nebulous.

This is actually an interesting topic. :grinning:

1 Like

It is @EricGT but I am fundamentally thick I guess and so some days it feels like I am banging my head harder than usual…I have made it work now and I think I understand why too which is always a bonus.

When I first learned Haskell it was hard as I am no mathematician. Same with Prolog, I am no logician either. Out of the two though I think Prolog has it for sheer beauty of appearance!

1 Like

This is a no-op. Doesn’t do any harm, but as none of the arguments is a true meta argument it does nothing.

The // is indeed correct. Forgot than call(Callable) is in a DCG body and thus effectively call/3. You should need the same for the 2nd argument though as that is handled the same. Higher order DCG is a rather tricky thing. I think you should not be using call(Callable), but simply Callable to allow for arbitrary DCG body terms. See library(dcg/high_order) for examples.

TBH Jan I am beginning to think that “Golden Hammer” may have applied…I am seriously looking at a way to make it simpler…now that I “understand” meta-predicates I feel like the guy who first discovered TNT but didn’t realise it’s volatility and potency for trouble :slight_smile:

Back to the bitmap for a while…

yeah, I didn’t realize SWI-Prolog stopped at first non-directive, and didn’t know about // as a sigil for meta_predicate