How can the module for a term be identified?

How can the module for a term/goal/predicate be identified?

In looking at the source code for SWI-Prolog I often see M:Goal so tried

:- module(some_code,[]).

create(Stack) :-
    Stack = stack([]).

:- begin_tests(some_tests).

test(1) :-
    create(Stack),
    assertion( Stack == stack([]) ).

test(2,[fail]) :-
    create(Stack),
    Stack = Module:Structure,
    assertion( Module == some_code ),
    assertion( Structure == stack([]) ).

:- end_tests(some_tests).

test 1 succeeds, but test 2 fails at Stack = Module:Structure.

EDIT

Found predicate_property/2 (source code) which is working. Still looking for something more straight forward.

test(3) :-
    create(Stack),
    assertion( Stack == stack([]) ),
    predicate_property(create(Stack), implementation_module(Module)),
    assertion( Module == some_code ).

Predicates have modules. Terms are just terms.

Here, stack is expected to be module qualified. It’s not, it’s just stack/1, so it fails.

1 Like

@EricGT Have a look at the documentation about defining a meta-predicate. A meta_predicate/1 directive will cause the system to pass Module:Argument instead of just Argument, where Module is the module calling context the call is made in. Note that there is reduction, so that m1:(m2:foo) becomes m2:foo, etc. There’s a nice example at the bottom of the page.

1 Like

Thanks

Several days ago I posted a question about meta_predicate/1 and received no answers after a day so I deleted it and dug in deeper and found what I posted above. Your one line is now adding much more clarity; enough so that I plan to revisit the problem again using this insight.

This paper may help:

Meta-Predicate Semantics. Paulo Moura. Post-Proceedings of the 21st International Symposium on Logic-Based Program Synthesis and Transformation (LOPSTR 2011), 2012. Springer LNCS 7225.

3 Likes

I have read your paper, @pmoura , the other day when I was thinking it might be possible to (from the Web Prolog user’s point of view) get rid of modules and the complications with meta_predicate/1 altogether. (It doesn’t seem likely though, as long as the workspace of an actor is provided by a module.)

Today, I’m getting more and more frustrated by my so far failed attempts to get my receive/1-2 to work correctly when in a module. Unfortunately, reading your paper didn’t help. :frowning: As a reminder, here’s the predicate I’m talking about:

receive(Clauses, Options) :-
    thread_self(Mailbox),
    (   clause(deferred(Message), true, Ref),
        select_rclause_body(Clauses, Message, Body)
    ->  erase(Ref),
        call(Body)
    ;   receive(Mailbox, Clauses, Options)
    ).

receive(Mailbox, Clauses, Options) :-    
    (   thread_get_message(Mailbox, Message, Options)
    ->  (   select_rclause_body(Clauses, Message, Body)
        ->  call(Body)
        ;   assertz(deferred(Message)),
            receive(Mailbox, Clauses, Options)
        )
    ;   option(on_timeout(Body), Options, true),
        call(Body)
    ).

select_rclause_body({PatternGuard -> Body}, Message, Body) :-
    var(PatternGuard), !,
    PatternGuard = Message.
select_rclause_body({PatternGuard -> Body}, Message, Body) :-
    check(PatternGuard, Message).
select_rclause_body({PatternGuard -> Body ; _}, Message, Body) :-
    check(PatternGuard, Message), !.
select_rclause_body({_ ; Rules}, Message, Body) :-
    select_rclause_body({Rules}, Message, Body).

check(Pattern when Guard, Message) :- !,
    Message = Pattern,
    once(Guard).
check(Pattern, Pattern).

I use this with two test programs:

:- op(1000, xfy, when).

important(Messages) :-
    receive({
        Priority-Message when Priority > 10 ->
            Messages = [Message|MoreMessages],
            important(MoreMessages)
    },[ timeout(0),
        on_timeout(normal(Messages))
    ]).

normal(Messages) :-
    receive({
        _-Message ->
            Messages = [Message|MoreMessages],
            normal(MoreMessages)
    },[ timeout(0),
        on_timeout(Messages=[])
    ]).

and

wait_hello :-
    receive({
        hello ->
            writeln('Got hello!'),
            wait_goodbye
    }).

wait_goodbye :-
    receive({
        goodbye ->
            writeln('Got goodbye!')
    }).

I’ve managed to make the important/1 program to work (ab)using the following meta predicate declaration and code:

:- meta_predicate receive(:,:).
   
receive(Clauses) :-
    receive(Clauses, []).

receive(M:Clauses, M:Options) :-
    thread_self(Mailbox),
    (   clause(deferred(Message), true, Ref),
        select_rclause_body(Clauses, Message, Body)
    ->  erase(Ref),
        call(M:Body)
    ;   receive(Mailbox, M:Clauses, M:Options)
    ).

receive(Mailbox, M:Clauses, M:Options) :-    
    (   thread_get_message(Mailbox, Message, Options)
    ->  (   select_rclause_body(Clauses, Message, Body)
        ->  call(M:Body)
        ;   assertz(deferred(Message)),
            receive(Mailbox, M:Clauses, M:Options)
        )
    ;   option(on_timeout(Body), Options, true),
        call(M:Body)
    ).

select_rclause_body({PatternGuard -> Body}, Message, Body) :-
    var(PatternGuard), !,
    PatternGuard = Message.
select_rclause_body({PatternGuard -> Body}, Message, Body) :-
    check(PatternGuard, Message).
select_rclause_body({PatternGuard -> Body ; _}, Message, Body) :-
    check(PatternGuard, Message), !.
select_rclause_body({_ ; Rules}, Message, Body) :-
    select_rclause_body({Rules}, Message, Body).

check(Pattern when Guard, Message) :- !,
    Message = Pattern,
    once(Guard).
check(Pattern, Pattern).

(I know, it wont work if the second argument to when isn’t a built-in.)

The wait_hello/1 program doesn’t work though, although it’s actually simpler. When I run it, I get this error:

Got hello!
ERROR: Unknown procedure: receive:wait_goodbye/0
ERROR:   However, there are definitions for:
ERROR:         test:wait_goodbye/0
...

Since I’m calling Body in the module M everywhere, I don’t understand how this can happen!

Help with this would be much appreciated!

If Body is already qualified by the time of the call, then when calling M:Body, M will be ignored. For example:

?- assertz(baz:a).
true.

?- current_module(baz).
true.

?- foo:bar:baz:a.
true.

?- current_module(foo).
false.

?- current_module(bar).
false.

As a side note, you can never fall into this trap in Logtalk as it is not based on a predicate prefixing mechanism :stuck_out_tongue:

1 Like

Ah, I see, I have to check if that’s the problem. Thanks! (And I forgot to say that I thought your paper was very interesting and very clearly written. And I’m sorry if the answer to my problem was obvious from the paper – when I read it I didn’t realise that I was going to have a practical programming problem connected to module transparency only a few day’s later!)

:slight_smile:

I said it before and I say it again, Logtalk backed up by SWI-Prolog would probably serve as an excellent host language for Web Prolog. A real killer application – would make Logtalk famous! :wink: :slight_smile:

1 Like

My admittedly sloppy investigation seems to indicate that Body isn’t qualified by the time of the call. Anything else I’m missing?

Thanks. Are you seriously proposing that this will work? It’s an honest question, I’ve always felt handicapped in the presence of meta_predicate/1, so I’m glad if it turns out to be that easy.

I don’t know whether the message patterns get undesirably qualified.
So you might go from bad to worse. The problem is that you basically
want an ambigious -> since you also handle the actions:

  • ? -> 0: (in context 0, if there is meta_predicate {0} as well)
    for your Web Prolog receive

  • 0 -> 0: (in context 0 aka goal context according to ISO core standard)
    for ISO core standard control

You can do ambigious multi declarations of meta_predicate in more
advanced type systems, maybe even in Mercury, don’t know. But ? -> 0
is also poorly supported since most Prolog systems don’t have

meta_function/1 directive. One solution would be to use something
else than (->)/2. But so far I didn’t suggest that. Maybe you are lucky
and it works if you add the missing {}/1 declaration.

Yet another solution would be to use term expansion, and transform
the receive into something that uses 0 -> 0. But then you cannot make
cross referencing on the unexpanded source. This is also not advisable

to have pre-expansion expressions that are not typeable.

(*) For ISO core standard see section about body conversion, to get an idea.

I did this, but no change:

receive(M:Clauses, M:Options) :-
    thread_self(Mailbox),
    (   clause(deferred(Message), true, Ref),
        select_rclause_body(Clauses, Message, Body0),
        strip_module(Body0, _, Body)
    ->  erase(Ref),
        call(M:Body)
    ;   receive(Mailbox, M:Clauses, M:Options)
    ).

receive(Mailbox, M:Clauses, M:Options) :-
    (   thread_get_message(Mailbox, Message, Options)
    ->  (   select_rclause_body(Clauses, Message, Body0),
            strip_module(Body0, _, Body)
        ->  call(M:Body)
        ;   assertz(deferred(Message)),
            receive(Mailbox, M:Clauses, M:Options)
        )
    ;   option(on_timeout(Body), Options, true),
        call(M:Body)
    ).

So I still don’t understand how this can happen:

Got hello!
ERROR: Unknown procedure: receive:wait_goodbye/0
ERROR:   However, there are definitions for:
ERROR:         test:wait_goodbye/0
...

Let’s simplify your code. In a r.pl file:

:- module(r, [receive/1]).

:- meta_predicate(receive(:)).

receive(M:{Head -> Body}) :-
	format('Executing ~q: ', [Head]),
	M:Body.

In a s.pl file:

:- module(s, [wait_hello/0, wait_goodbye/0]).

:- use_module(r, [receive/1]).

wait_hello :-
    receive({
        hello ->
            (writeln('Got hello!'),
            wait_goodbye)
    }).

wait_goodbye :-
    receive({
        goodbye ->
            writeln('Got goodbye!')
    }).

Sample call:

?- [r,s].
true.

?- wait_hello.
Executing hello: Got hello!
Executing goodbye: Got goodbye!
true.

Ih the code you posted in your first message in this this thread you have:

:- meta_predicate receive(:,:).
   
receive(Clauses) :-
    receive(Clauses, []).

receive(M:Clauses, M:Options) :-
    ...

You’re missing the directive:

:- meta_predicate receive(:).

This is required so that the receive/1 calls from the wait_hello/0 and wait_goodbye/0 predicates will be qualified with the originating module (as receive/1 is being declared as a meta-predicate).

P.S. There might be other issues at play here; busy with work so I didn’t looked closely to your code.

Ok, thanks anyway. Adding :- meta_predicate(receive(:)) didn’t help, unfortunately. :frowning:

Now it works! (Well, with my two examples, at least…). Thanks, Paulo, our advise was part of the solution:

:- meta_predicate receive(:).
:- meta_predicate receive(:,:).
   
receive(_:Clauses) :-
    receive(Clauses, []).

receive(M:Clauses, M:Options) :-
    thread_self(Mailbox),
    (   clause(deferred(Message), true, Ref),
        select_rclause_body(Clauses, Message, Body)
    ->  erase(Ref),
        call(M:Body)
    ;   receive(Mailbox, M:Clauses, M:Options)
    ).

receive(Mailbox, M:Clauses, M:Options) :-
    (   thread_get_message(Mailbox, Message, Options)
    ->  (   select_rclause_body(Clauses, Message, Body)
        ->  call(M:Body)
        ;   assertz(deferred(Message)),
            receive(Mailbox, M:Clauses, M:Options)
        )
    ;   option(on_timeout(Body), Options, true),
        call(M:Body)
    ).

select_rclause_body({PatternGuard -> Body}, Message, Body) :-
    var(PatternGuard), !,
    PatternGuard = Message.
select_rclause_body({PatternGuard -> Body}, Message, Body) :-
    check(PatternGuard, Message).
select_rclause_body({PatternGuard -> Body ; _}, Message, Body) :-
    check(PatternGuard, Message), !.
select_rclause_body({_ ; Rules}, Message, Body) :-
    select_rclause_body({Rules}, Message, Body).

check(Pattern when Guard, Message) :- !,
    Message = Pattern,
    once(Guard).
check(Pattern, Pattern).

Now, there’s likely one problem left, and that’s when there is a guard which is defined in the test only. Have no test example for that.

This clause is puzzling. It should simply be:

receive(Clauses) :-
    receive(Clauses, []).

Strange, but both of my examples work only if I have that clause… Something else may well be wrong. Here’s my full test module:

:- module( test, [important/1, normal/1, wait_hello/0, wait_goodbye/0, test/0, test2/0]).

:- use_module(rec, [
    receive/1,
    receive/2
]).

:- op(800, xfx, !).
:- op(1000, xfy, when).

important(Messages) :-
    receive({
        Priority-Message when Priority > 10 ->
            Messages = [Message|MoreMessages],
            important(MoreMessages)
    },[ timeout(0),
        on_timeout(normal(Messages))
    ]).

normal(Messages) :-
    receive({
        _-Message ->
            Messages = [Message|MoreMessages],
            normal(MoreMessages)
    },[ timeout(0),
        on_timeout(Messages=[])
    ]).


wait_hello :-
    receive({
        hello ->
            writeln('Got hello!'),
            wait_goodbye
    }).

wait_goodbye :-
    receive({
        goodbye ->
            writeln('Got goodbye!')
    }).    


test :-
   thread_self(S), 
   thread_send_message(S, 15-high), 
   thread_send_message(S, 7-low), 
   thread_send_message(S, 1-low), 
   thread_send_message(S, 17-high).
   
test2 :-
   thread_self(S), 
   thread_send_message(S, goodbye), 
   thread_send_message(S, hello), 
   wait_hello.

The bug in the code for the receive/2 predicate. It’s head should be:

receive(M:Clauses, _:Options) :-

Otherwise you get a failed unification when this predicate is called from receive/1. Why? You have:

:- meta_predicate receive(:).
:- meta_predicate receive(:,:).

receive(Clauses) :-
    receive(Clauses, []).

When receive/1 is called from the test module, the clauses get prefixed by the module name (as you’re calling a meta-predicate). But when receive/1 calls receive/2, the second argument doesn’t originate in the test module and thus is qualified by the rec module. I.e. the call is:

receive(test:Clauses, rec:[])

which cannot unify with the head of the clause:

receive(M:Clauses, M:Options)

With the fix above, we get:

?- test2.
Got hello!
Got goodbye!
true.

And I’m also wondering why both the Clauses and Options arguments are meta arguments? Are there options that contain meta-sensitive values? In that case the declaration is ok and you should use meta_options/3 to qualify the appropriate option values. If there are no meta-sensitive values, please specify the argument as +.

Thanks, Paulo, it now works!