Do the Picat operators `=>` and `?=>` have a formal name in SWI-Prolog?

Since SWI-Prolog is now incorporating the Picat operator =>/2 I was wondering if it has a formal name for use in SWI-Prolog?

I checked the Picat User’s Guide and the closest I found was in

Chapter 4

Predicates and Functions

Picat has two types of pattern-matching rules: the non-backtrackable rule

Head, Cond => Body.
and the backtrackable rule

Head, Cond ?=> Body.

and later in

Appendix I

Appendix: Grammar

predicate_rule ->  
    head ["," condition] ("=>" | "?=>") body eor  
    head (":-" | "-->") body eor  
 
nonbacktrackable_predicate_rule ->  
    head ["," condition] "=>" body eor  

In the tutorial it notes

Core logic programming concepts

Implicit pattern-matching and explicit unification

I tend to find Implicit pattern-matching more descriptive than => but at present I take it that, that would still include both the backtracking and not-backtracking variations.

The other interesting item of note upon first reading is that Picat also uses the name predicate with => or ?=>.

Anyway, without a proper unique name for the operators in SWI-Prolog, this could become very confusing when trying to understand this when a less informed person ask a question about => or ?=> and does not specifically use the proper syntax for the operators.

So

Do the Picat operators => and ?=> have a formal name in SWI-Prolog?


EDIT

As I write this I see that Jan W. is also writing a reply.

The SWI-Prolog manual does use the name Single Sided Unification rules so I guess that will be the formal name for => and ?=> when used with SWI-Prolog. Still interested in what Jan W. notes.

Not yet. Well, for now I want to keep ?=> out because we have classical Prolog clauses if you want non-determinism. This could of course change. A good name is surely welcome. For now I normally refer to them as SSU (single side unification) rules. As they also commit this is still not ideal terminology. Ideas are welcome …

1 Like

Glad to hear that Single Sided Unification rules is not set in stone at this point.

While I don’t have a name to propose at this time, here are some of the thoughts I have when contemplating a name. I am writing this so that others can have a litmus test to see if their thoughts are similar to others, or at least mine at the moment.

Note: I have not used Picat or even used => in SWI-Prolog yet so this is all based on just what I have read.

If we exclude the backtrackable rule (?=>) for now and only consider the non-backtrackable rule (=>) then the properties to consider are

  1. Is a pattern matching (Pattern matching programming languages)
  2. Is not backtracking which I will take to mean that it is either determinate or fails.
  3. Is not unification so is one way, thus the syntax for the operator =>.
  4. Allows conditions on the left of the operator. While it does remind me of forward chaining, as Jan W. notes, => is not forward chaining. I wish I had practical experience with something similar in another programming language so that I can relate but do not. The only thing I know of that adds some clarity to adding conditions on the left of the operator is (ref)
p(V1,V2,...,Vn) :-
    Pattern = p(A1,A2,...,An),
    Args = p(V1,V2,...,Vn),
    subsumes_term(Pattern, Args),
    Pattern = Args,
    Guard,
    !,
    Body.
  1. Pattern matching also reminds me of Algebraic Data Types but I don’t know how this relates to => but understanding the relation between Algebraic Data Types and => could be used as a discriminator for understanding.
  2. This also reminds me of Modus ponens. Something more to be used as a discriminator for understanding.
  3. The description for Picat uses the phrase Implicit pattern-matching but does the adjective Implicit have meaning with the SWI-Prolog operator =>?
  4. Is like a rabbit hole in that once you use => for a predicate or rule (which I am using liberally at present) you are committed to using it for the remainder of the predicate or rule. (ref)

A predicate either uses :-/2 for all its clauses or =>/2 . Mixing is not allowed and raises a permission error for a clause that does not use the same neck as the first clause.

  1. Results in errors instead of silent failures. (ref). Which IMHO takes us out of the land of logic and into the land of procedural programming and possibly functional programming.

Unlike Picat, it is an error if no clause matches.


At present the idea that keeps popping into my head about this is F# pattern matching. When I get some time I will do comparisons as test cases but this is not even on the short list.

The other thing that keeps popping into my head about this is that one should look at ways to track the errors yet continue processing, think monads. In my mind once one starts using the SWI-Prolog operator => they are going to want a way to work with the errors that does not kill the process.

In F# the answer is Railway Oriented Programming, in Prolog the closest thing I know is DCGs or open list.


If others are wondering why I seem to have a fixation with a name for this operator it is that if you don’t have a meaningful and easily searchable name for something, it makes it much harder to learn about it and if you can’t learn about something because you can not find information about something, then it will be lost to the sands of time. Have you ever tried to search for the . operator when first learning about object-oriented modules, knowing the name makes it so much easier.

Sorry to jump in the middle here, but => is normally used as logical implication operator.
This is the case for instance in https://www.cpp.edu/~jrfisher/www/prolog_tutorial/logic_topics/geolog/geoprolog.pl which is now broken.
It is also broken in my recent work on SEcond EYE seeye/seeye.pl at master · josd/seeye (github.com) which is supporting PREM => CONC formulae like used in seeye/graph.pl at master · josd/seeye (github.com) or seeye/dpe.pl at master · josd/seeye (github.com)

Thanks for raising. To some extend this is of course unavoidable. Any operator we add is not unlikely to be in use by someone somewhere. This means that adding a feature like this either requires some weird ugly operator nobody ever uses or using modules as @j4n_bur53 would like to see.

I’m not (yet) a fan of requiring a module to support => rules. If this evolves into something that is heavily used you don’t want to import a library for it all over the place.

The operator is not the problem. You can just redefine this and any application using => will have to define this. The problem is that X=>Y is now compiled into something different than it used to. You can fairly easily work around that using term_expansion/2 to translate it into something else. Of course, that means => means something different in your context.

I’m not against using another operator either. Picat has been thinking about this before though …

Unexpansion is not really there. Well, listing/1 and portray_clause/1-3 can be hooked (used by e.g., Logtalk) and clause_info/5 used to get access to position information, variable names and environment stack layout to drive the graphical debugger can be hooked to make it handle conversions done by the rewrite system and compiler optimization. I’m not too happy with all of this. In the end, reverting these rewrites is complicated and often involves heuristics and assumptions and thus not always produce the correct result.

Syntax yes. You can do that already. You also need support in the VM to make it usable though. You either need to implement SSU directly as an alternative matching strategy for the head or you need transformation to use subsumes_term/2 and something clever in the compiler and VM to recognise this pattern and do something smart with it.

How about ‘=>>’ ? The two ‘>’ could be mnemonics for the two special properties:

  1. One way unification
  2. The rule commits and no other rules will be tried if condition and head match.

I think it is important to keep SWI-Prolog compatible with older code, code that users can’t change, most especially because they are just users who expect things to “simply work”. If we use ‘=>>’ or even ‘==>>’ there is much less probability of breaking existing code.

1 Like

I had a look at this. It isn’t broken due to =>, but due to the fact that old versions allowed for e.g. dynamic(exists(a)), while new versions require dynamic(exists/1) as required by The Standard (changed after some reports where the ambiguity of the old implementations caused too much confusion) . The =>/2 is no problem as the operator is (re)defined and the theory holding => terms is read and translated into something different.

It is also still possible to have clauses for a predicate =>/2, but you need to write them as

 (a=>b) :- true.

After which

?- X=>Y.
X = a,
Y = b.

So, in general this should suffice to load code using => as facts (rules work anyway).

term_expansion((X=>Y), ((X=>Y):-true)).

I thought this was a show-stopper. It seems it is not that bad though. It is a bit comparable to most of the other Prolog operators that you can redefine and overrule their predicate if you want. It may lead to some confusion if people are also allowed to write normal Prolog code in the same context.

1 Like

Now I see and am happy that Seeye is now also running with swipl
thanks to your hint to use term_expansion((X=>Y), ((X=>Y):-true)).
which is now used in seeye/seeye.pl at master · josd/seeye (github.com)
The test results for josd/seeye: Second eye - Seeye (github.com)
using ./test swipl look fine Seeye with SWI Prolog · josd/seeye@9944af1 (github.com)

Thanks!

2 Likes

From the manual:

A predicate either uses :-/2 for all its clauses or =>/2 . Mixing is not allowed and raises a permission error for a clause that does not use the same neck as the first clause.

Mixing is IMO not desirable and makes the error in case no rule matches rather awkward. You should really see these things as different from normal Prolog predicates. They help implementing pretty common Prolog predicates that are not much in Prolog’s spirit but typically needed to make applications work, notably single moded computations from some data structure.

See rule/2. Not very happy with the name (which connects to the lack of a name for such predicates/clauses anyway). Note that rule/2 has a different interface that can be extended to support any new type of rule and also deals with normal clauses.

As is, they do normal unification. I have often felt the need for something different, either subsumption or variant. Just consider

 p(_).
 p(a).

How am I going to retract p(a) in standard Prolog? In SWI-Prolog you can do this (or simply X == a, but this show the more general pattern). This approach is far from ideal as it looses clause indexing.

?- clause(p(X), true, Ref), p(X) =@= p(a), erase(Ref).

I guess this is best handled using variations on clause/2 and retract/2. Hmm. The rule/2,3 API could fix this. In the current implementation it merely uses the Head for getting the predicate. We could only return rules that unify without binding any variable in Head. So, you would get

?- rule(p(a), Rule).
 Rule = p(_) ;
 Pule = p(a).

but not a possible p(b). That allows for clause indexing, after which you can filter using =@=/2 or subsumes_term/2.