Defining your own operators....puzzled!

I have spent six hours learning about operators and while I get it for the most part, and apart from DCG → and term_expansion, I can’t really see where operators would add much value to my code other than making a call like this,

is_builtin(X) be typed into my code as is_builtin X or X is_builtin depending upon the op/3 type. So is that “it”, the docs say that:

Operators are defined to improve the readability of source code.

So maybe that’s as far as I need to think about it?! But then I read all my Prolog books about operators and started hacking about and now I am a bit confused about “operators”, “predicates” and “functions”… because I tried to create an operator that would work like + as in 1+2+3 being internally constructed as +( +(1,2), 3) but have so far failed. Is it even possible?

As I read the various books I have (COP, AOP, C&M, PPID) I really bottomed out as they don’t cover all the things you can do. I started thinking “monads” like Haskell, all sorts of things but my head just doesn’t seem to get it. There are no return values in Prolog, anything returned is through variables passed in, so how does:

        Z is 1 + 2 + 3 => Z is +( +(1,2), 3) 

actually work? I am guessing that because it is a built-in function it is handled differently down in the engine room ? I also tried to write an operator that would let me append a “!” to a string, and then I wanted to chain them to add multiple “!” on the end, again just for the learning exercise but again, I couldn’t figure out “the return bit”, is it even possible? Can I write my own “functions” (what makes a function a function? Can I have an operator chain a value down so that my silly operator to append a ! to a string could be written as ~~ ~~ “Hello” and produce “Hello!” etc.

I learned mercury for a while last years and that has “function” but it’s a sugared predicate form anyway so nothing surprising there really AIUI and it also had the notion of state variables that the compiler automatically threads through your code, like DCG-s do for the list manipulation operations.

Basically, I’d dearly love to understand the limitations of “op”. I know it is just telling the compiler how to treat the input for wrt. to associativity and precedence, I am, fine with all of that, I just want to know what can and can’t be done!

Here is some of my rambling efforts, I used listing/1 a lot to see what the compiler had done as well as display/1 to see how things were being evaluated internally but all to no avail!

:- op(500, yfx, meh).
meh(A1,A2) :- %shell(Arg).                                                                 
    string(A1), string(A2),
    string_concat(A1, "\\", Out0),
    string_concat(Out0, A2, Out),
    debug(ops,'A1: ~s, A2: ~s, Out: ~s~n',[A1,A2,Out]).

% op to add an ! to the end of a string                                                    
:- op(500, fy, ~~).
~~(Out,Arg) :-
    string(Arg),
    string_concat(Arg, "!", Out),
    debug(ops, '~~ with ~s => ~s', [Arg, Out]).

:- op(500, fx, ¬).                                                                        
¬(X) :- format('¬ ~p~n',[X]).

Thanks again for reading my mad rambling question.
Sean.
:expressionless:

1 Like

While I don’t use op/3 in my code, I am not opposed to using it.

My first suggestion to help you better understand about op/3 is to look for it in the GitHub swi-prolog repositories, all 74 of them.

My second suggestion is to note which ones use Prolog for what I consider modeling a specific problem or domain. These my also use term rewriting in combination with op/3. And when you examine them check to see if they have what I consider escape syntax, e.g. you are doing standard Prolog but then use an escape character or sequence to escape into a special set of syntax rules created for the problem domain. As I have noted before, I consider the the word is as in is/2 an escape and --> as used with DCGs an escape. Constraints also have this.

My third suggestion if you really want to push this for all it is worth is to create an emulator for either APL or one of the code golf programming languages which are full on one character operators.

My understanding is that operators in Prolog are indeed just sugar, purely a convenience for writing foo <=> bar instead of '<=>' (foo, bar).

is works not for any special reason having to do with operators; it’s “just” a predicate that does its own sort of interpreting of the RHS. e.g. X is 1 + 2 => is(X, 1+2) => is(X, +(1, 2), then you could define the is predicate to unify the first argument with the numeric value of the RHS (bottoming out at some primitive level) (or, for silly example, is(3, +(1,2)).).

EDIT: so the key difference between is and your ~~ operator is that it doesn’t have a second argument to unify with the new string.

Operators are purely syntactic sugar that let you use infix notation and fewer parentheses. Some IO predicates use operator declarations, some don’t:

?- Term = (foo :- bar, qqsv), write(Term), nl, write_canonical(Term), nl.
foo:-bar,qqsv
:-(foo,','(bar,qqsv))

Thanks. I learned Dyalog APL and “J” to the point where I could use them. Not sure how that helps me atm with what I wanted to do though, write a predicate that was chainable in the sense that + is chainable to append a ! to a string.

That reminds me of how Prolog converts [a,b,c] to .(a,.(b,.(c,[])))

Here is a hint.

?- write_term([a,b,c],[dotlists(true)]).
.(a,.(b,.(c,[])))
true.

I think the problem then is + isn’t really “chainable”: + isn’t a predicate, it just has an operator defined so you can construct a term like +(1, +(2, 3)) more ergonomically, then predicates like is and #= interpret those term structures to do math things.

So, if you want to be able to chain e.g. adding a prefix, you probably either want to write something that can use DCG notation – e.g.

bang(In, Out) :- string_concat(In, "!", Out).

run --> bang, bang. 
?- phrase(run, "foo", Res). % Res = "foo!!"

or write something like is that evaluates the input, e.g.

thingy(Res, X) :- string(X), !, Res = X.
thingy(Res, ~~(X)) :-
   thingy(X0, X), string_concat(X0, "!", Res).

?- thingy(X, ~~(~~"bar")).  % X = "bar!!"

That’s the conclusing I’ve been coming to @jamesnvc …I am relatiovely new to Prolog, today is the first time I even felt like tackling operators but with not much else to do during the lockdown I went for it.,

Again, thanks to all replies, I have a lot to read, work on and then to keep on rolling.

Cheers
Sean
:slight_smile:

1 Like