Confusion about effect of op/3 in a program

Hello,

For future expansion of my program, I need to use a third-party Prolog module to perform a set of desired operations. The third-party module has the following declarations (and other predicates I need to use).

:- op(500, xfy, user:(+)).
:- op(400, xfy, user:(*)).
:- op(500, xfy, user:(-)).
:- op(400, xfy, user:(/)).

After including the third-party module in my main program, some of the problems that the program used to solve are no longer solvable. When I comment the above op/3 declarations out, things get back to normal. So my guess is that these op/3 declarations are the culprit.

Can somebody please explain to me what possible conflict could these declarations be introducing? I checked the page about op/3 and the operator priority seems to be matching. My program performs arithmetic operations (amongst other things), and I have no op/3 declarations in my program – well except the ones in the third-party module after adding it. Also, the third-party module is not imported as module per se, I use ensure_loaded/1 to include it in my program.

Many thanks,
Amine.

Those operator directives change the standard associativity of those operators from yfx (i.e. left-associative) to xfy (right-associative). Never a good idea to mess with the definition of standard operators as it can introduce all sorts of bugs.

1 Like

Thank you :slight_smile:

So is there a way to use this third-party module with these associativity-changing directives? Can I assert these directives before calling the predicates I want in the module, and retract the directives after that?

Hard to tell without looking into that module code… Ideally, those operator redefinitions would be local to that module.

If the third-party module predicates that you’re calling have arguments using those redefined operators, maybe you can pass the arguments in canonical notation?

This is the code I want to use. So far, I am using ensure_loaded/1 to load it in my program. Do I need to make it as a module for the declarations to be local to it?

As a first experiment, you can comment out those op/3 directives and check if that code still works. If that’s not the case, you can try to convert the code into a module and dropping the user: prefix from the directives.

1 Like

No luck :frowning:

If op/3 are removed, the module does not work as expected, so they must be left. Having that in mind, I did the following.

I have defined a module for the code using:
:- module(algebra, [simplify/2,split/3]).

Then, I called it from my program using:
:- use_module(‘modules/algebra.pl’, [simplify/2, split/3]).

So far, my program only imports the module, it does not use any of its predicates.

Now:

  1. I left the op/3 as they are originally [1], and run my program with one of the problems that stopped working. The result is problem still unsolvable.
  2. I removed the “user” prefix as in [2] and [3]. The problem is solvable, but calling a predicate from the module shows it does not behave correctly.

Any thoughts?

[1]
:- op(500, xfy, user:(+)).
:- op(400, xfy, user:()).
:- op(500, xfy, user:(-)).
:- op(400, xfy, user:(/)).
[2]
:- op(500, xfy, (+)).
:- op(400, xfy, (
)).
:- op(500, xfy, (-)).
:- op(400, xfy, (/)).
[3]
:- op(500, xfy, +).
:- op(400, xfy, *).
:- op(500, xfy, -).
:- op(400, xfy, /).

You want [2]. As I mentioned previously, you need to call the module predicates ensuring that any argument that uses those operators is passed without using operator notation.

Where can I read about the role of “user” in op/3?

A directive such as:

:- op(500, xfy, user:(+)).

means that the operator is defined in user.

P.S. There are several issues with that RedEx.pl file that you’re trying to reuse as you may have already noticed. Running the Logtalk linter on it reports:

?- logtalk_load('RedEx.pl', [hook(object_wrapper_hook)]).
*     Singleton variable: A
*       while compiling object 'RedEx'
*       in file /Users/pmoura/Desktop/RedEx.pl at or above line 127
*     
*     Singleton variable: A
*       while compiling object 'RedEx'
*       in file /Users/pmoura/Desktop/RedEx.pl at or above line 128
*     
*     Singleton variable: A
*       while compiling object 'RedEx'
*       in file /Users/pmoura/Desktop/RedEx.pl at or above line 136
*     
*     Unknown predicate called but not defined: sumlist/2
*       while compiling object 'RedEx'
*       in file /Users/pmoura/Desktop/RedEx.pl between lines 8-15
*     
*     Unknown predicate called but not defined: append/3
*       while compiling object 'RedEx'
*       in file /Users/pmoura/Desktop/RedEx.pl between lines 8-15
*     
*     Unknown predicate called but not defined: append/3
*       while compiling object 'RedEx'
*       in file /Users/pmoura/Desktop/RedEx.pl between lines 59-63
*     
*     Deprecated predicate: not/1 (compiled as a call to (\+)/1)
*       while compiling object 'RedEx'
*       in file /Users/pmoura/Desktop/RedEx.pl at or above line 85
*     
*     Deprecated predicate: not/1 (compiled as a call to (\+)/1)
*       while compiling object 'RedEx'
*       in file /Users/pmoura/Desktop/RedEx.pl at or above line 86
*     
*     Unknown predicate called but not defined: append/3
*       while compiling object 'RedEx'
*       in file /Users/pmoura/Desktop/RedEx.pl between lines 91-95
*     
*     Unknown predicate called but not defined: append/3
*       while compiling object 'RedEx'
*       in file /Users/pmoura/Desktop/RedEx.pl between lines 97-102
*     
*     Deprecated predicate: not/1 (compiled as a call to (\+)/1)
*       while compiling object 'RedEx'
*       in file /Users/pmoura/Desktop/RedEx.pl between lines 164-167
*     
*     Deprecated predicate: not/1 (compiled as a call to (\+)/1)
*       while compiling object 'RedEx'
*       in file /Users/pmoura/Desktop/RedEx.pl between lines 173-176
*     
*     Deprecated predicate: not/1 (compiled as a call to (\+)/1)
*       while compiling object 'RedEx'
*       in file /Users/pmoura/Desktop/RedEx.pl between lines 177-180
*     
*     Missing predicate directive: :-discontiguous simplify/2.
*       while compiling object 'RedEx'
*       in file /Users/pmoura/Desktop/RedEx.pl at or above line 113
*     
*     Missing predicate directive: :-discontiguous rules/2.
*       while compiling object 'RedEx'
*       in file /Users/pmoura/Desktop/RedEx.pl at or above line 200
*     
*     Missing predicate directive: :-discontiguous identity/2.
*       while compiling object 'RedEx'
*       in file /Users/pmoura/Desktop/RedEx.pl at or above line 201
*     

If you’re converting that code into a module, add the missing use_module and discontiguous directives. Note also that sumlist/2 is a deprecated predicate.

Yeah I noticed the warnings. Thanks a lot for your help :slight_smile:

Thats not proper design of a third party module. Usually one would do the
following in the third party module ‘foobar’:

:- module(foobar, [
        op(500, xfy, +),
        op(400, xfy, *),
        Etc.. ]).

i.e. put the syntax operators into the export list of the module. Doing the above would put the operators into the namespace ‘foobar’, and while using the module should safely overshadow the already existing definitions inside the pseudo module ‘user’.

In ‘user’ the operators are usually yfx, so using the module will change this to xfy.

Here is a test:

Welcome to SWI-Prolog (threaded, 64 bits, version 8.1.22)

?- write_canonical(a+b+c).
+(+(a,b),c)
true.

?- use_module(foobar).
true.

?- write_canonical(a+b+c).
+(a,+(b,c))
true.

If you want to do the definitions inside a module further down in a more hidden module ‘baz’, you need to use reexport/1 inside the client module ‘foobar’:

:- module(baz, [
        op(500, xfy, +),
        op(400, xfy, *),
        Etc.. ]).

:- module(foobar, []).
:- reexport(baz).

Brilliant, thanks a lot!

You used a predicate I did not know about: write_canonical/1, and I also found write_canonical/2. Is there another predicate that I can use to get the prefix format of an arithmetic expression where the prefix expression is returned as a variable? e.g. infix_to_prefix(InfixExpression, PrefixExpression)? I searched the documentation for “canonical” but could not find what I am looking for.

A term is always stored in “canonical” form. Its only that
during writing it is displayed using the current operator
definitions. You can test yourself:

?- current_op(500, Y, +).
Y = yfx.

?- assertz(term(a+b+c)).
true.

?- op(500, xfy, +).
true.

?- term(X).
X = (a+b)+c.

Usually in Prolog books a term is displayed as a tree:

      +
   /     \
  +       c
 /  \
a   b

Thanks :slight_smile: