Thats an interesting question. First I think there are two
kinds of predicate_property/2
predicates. The first kind
takes a callable and is intended to work smoothly with
for example vanilla interpreters and built-ins such as
write/1
, nl/0
, etc.., i.e.:
solve((A,B)) :- !, solve(A), solve(B).
solve(A) :- predicate_property(A, built_in), !, A.
solve(A) :- clause(A,B), solve(B).
For the first kind, I guess the 0 meta argument specifier
would work fine. The second kind of predicate_property/
2
predicates works with predicate indicators. Callable
and predicate indicators are both notions already defined
in the ISO core standard.
A predicate indicator has the form F/N, where F is an atom
and N is an integer. Matters get a little bit more complicated
if modules are present, especially because of the operator
precedence of (:)/2 versus (/)/2. If predicate property works
with predicate indicators, we can still code the vanilla
interpreter:
solve((A,B)) :- !, solve(A), solve(B).
solve(A) :- functor(A,F,N), predicate_property(F/N, built_in), !, A.
solve(A) :- clause(A,B), solve(B).
Here we loose the connection of a possible meta argument
0, for example of solve/1
, with predicate property. Because
functor/3
has usually no meta predicate declaration. Also
to transfer module information via functor/3
to
predicate_property/2
is impossible, since functor/3
is agnostic to
modules, it usually has no realization that can deal with
arguments that contain module qualification.
There are now two issues:
1) Can we give typing and/or should we give typing to predicates
such as functor/3
.
2) Can we extend functor/3
so that it can convey module
qualification.
Here are my thoughts:
1) Would need a more elaborate type system. One that would
allow overloading of predicates with multiple types. For
example functor/3
could have two types:
:- meta_predicate functor(?,?,?).
:- meta_predicate functor(0,?,?).
The real power of overloading multiple types would only
shine in predicates such as (=)/2. Here we would have:
:- meta_predicate =(?,?).
:- meta_predicate =(0,0).
Thus allowing for more type inference, if one side of
(=)/2 is a goal we could deduced that the other side
is also a goal.
But matter are not so simple, it would possibly make
sense to have also a form of type cast, or some other
mechanism to restrict the overloading. Something which
is not covered by introducing just a meta predicate
directive. This would require further constructs inside
the terms and goals.
Learning form lambda Prolog or some dependent type
system, could be advantageous. For example (=)/2 can
be viewed as parametrized by a type A, i.e.:
:- meta_predicate =(A,A).
2) For Jekejeke Prolog I have provided an alternative
functor/3 realization. The predicate is sys_modfunc_site/2
.
And it works bidirectionally like functor/3
, but returns
and accepts the predicate indicator as one whole thing.
Here are some example runs:
?- sys_modfunc_site(a:b(x,y), X).
X = a:b/2
?- sys_modfunc_site(X, a:b/2).
X = a:b(_A,_B)
The result of the predicate could be called a generalized
predicate indicator. It is what SWI-Prolog already understands
for example in listing/1
. So it could have the same meta argument
specification as listing/1
has. Which is current : in SWI-Prolog.
So we would have, and subsequently predicate_property/2
would
take the : in its first argument:
:- meta_predicate sys_modfunc_site(?,?).
:- meta_predicate sys_modfunc_site(0,:).
The vanilla interpreter, that can also deal with modules, then
reads as follows. Unfortunately a further predicate is needed,
sys_indicator_colon/2
, which compresses a qualified predicate
indicator into an ordinary predicate indicator, since our
predicate_property/2
does not understand generalized predicate
indicators for efficiency reasons:
solve((A,B)) :- !, solve(A), solve(B).
solve(A) :-
sys_modfunc_site(A,I),
sys_indicator_colon(J,I),
predicate_property(J, built_in), !, A.
solve(A) :- clause(A,B), solve(B).
The above implements a local semantic of the colon (:)/2,
compared to the rather far reaching semantic of the colon
(:)/2 as described in the ISO module standard. The far
reaching semantic imputes a module name on all the literals
of a query. The local semantic only expects a qualified
literal and just applies the module name to that literal.
Jekejeke only implements local semantic with the further
provision that call-site is not changed. So under the hood
sys_modfunc_site/2
and sys_indicator_colon/2
have also to
transfer the call-site so that predicate_property/2
makes
the right decision for unqualified predicates, i.e. resolving
the predicate name by respecting imports etc..
Finally a little epilog:
The call-site transfer of Jekejeke Prolog is a pure runtime
thing, and doesn't need some compile time manipulation, especially
no ad hoc adding of module qualifiers at compile time. As a result
certain algebraic properties are preserved. For example assume
we have the following clause:
?- [user].
foo:bar.
^D
Then the following things work fine, since not only sys_modfunc_site/2
is bidirectional, but also sys_indicator_colon/2
:
?- S = foo:bar/0, sys_indicator_colon(R,S), predicate_property(R,static).
S = foo:bar/0,
R = 'foo%bar'/0
?- predicate_property(R,static), sys_indicator_colon(R,S), S = foo:bar/0.
R = 'foo%bar'/0,
S = foo:bar/0
And of course predicate_property/2
works with different input and
output modes. But I guess the SWI-Prolog phaenomenom has first an
issue that a bare bone variable is prefixed with the current module. And
since false
is not in user
, but in system
, it will not show false
.
In output mode it will not show predicates which are equal by resolution.
Check out in SWI-Prolog:
?- predicate_property(X, built_in), write(X), nl, fail; true.
portray(_G2778)
ignore(_G2778)
...
?- predicate_property(user:X, built_in), write(X), nl, fail; true.
prolog_load_file(_G71,_G72)
portray(_G71)
...
?- predicate_property(system:X, built_in), write(X), nl, fail; true.
...
false
...
But even if the SWI-Prolog predicate_property/
2 predicate would
allow bar bone variables, i.e. output goals, we would see less
commutativity in the far reaching semantic than in the local
semantic. In the far reaching semantic M:G
means interpreting G
inside the module M
, i.e. respecting the imports of the module M
,
which might transpose the functor considerable.
The far reaching semantic is the cause that user:false
means
system:false
. On the other hand, in the local semantics, where M:G
means M:G
and nothing else, we have the algebraic property more often.
In the local semantics user:false
would never mean system:false
.
Bye