Picat style matching

I mostly see two clauses I’d rather not write. Judging just about any supposed to be good Prolog code I’ve had in my hands, varying from system libraries to applications written by experienced Prolog programmers, adding these two extra clauses is an exception rather that the rule.

As is, we need to write the thing we want to handle (the middle two clauses) but we also need to prevent these clauses to handle things we don’t want to be handled by them (first clause) as well as an extra clause to handle everything else. I’d rather just write the things that need to be handled and turn anything else into an error. The less you need to type the fewer errors you can make :slight_smile:

Hi Jan,

Thank you for clarifying.

I agree that the less one needs to write the less errors can sneak in – however, it also means that more is left unsaid and part of the execution semantics – i.e. what the programmer (and language designer/implementor) has in mind.

The way i see it, it depends on the language / semantic / virtual machine support – if the intent and functionality of the other clauses can become infrastructure.

It seems that currently, this is the way to go, given current language support …

Do i understand this correctly?

Dan

I would also suggest not to miss the opportunity that this opens for (maybe automatic) parallelization, since you know that no other rule would be applied if the condition and one-way unification succeeds.

Provable is preferable to not-provable, where possible.
But having clearly understood semantics through annotations (and triggering failures at runtime if not satisfied), also sounds wonderful.

Apart from anything, it allows sensible reasoning about the expectation you have about code, and for those to be forced upon dependencies. For example, In C++ we have const modifiers, in which callers must promise not to modify the data (although it is not strictly enforced). In Dart, Python, … others, we have async functions, which force callers to be async, and so also guarantee correctness.

Determinism seems to be in a (similar but different) category.
If you want your predicate to be deterministic, each of its dependencies should also be deterministic.

Anything that creates that kind of clarity to the reader seems like a big win.

Yes please.

Does this allow indexing on arguments to work just like in rules that use :-?

Written as above, it is surely possible to extend the clause indexing code to deal with this particular problem. That is not how the implementation of =>/2 rules work though. At the moment we simply check that the head unification did not bind anything by checking the trail stack. Future versions may veto on the first unification. That will not help much when => is used as alternative for normal Prolog code. It will help a lot in cases where we have rules that really need be to distinguished using subsumes_term/2. As the VM code for matching the head is the same, the normal clause indexing does the job.

Unfortunately this is incorrect. As a variable is a perfectly valid goal the first clause should not throw an error but do something with the variable. Now a goal visitor((X;Y), Out) is translated as visitor(((_->_);Y), Out). So you need an intermediate variable, nonvar/1 and explicit unification to get this right. Single sided unification avoids such pitfalls. And no, I’m not promoting it in general. I’m only promoting it for (semi)det code and for processing non-ground terms.

I have a tree traversal that handles over 100 different node types, and it would benefit from Picat notation (I have quite a few predicates with cuts and an “else” for catching cases where I forgot one of the possible terms.) Indexing would probably help that code (I’m having some trouble interpretting output from profile/1, but jiti_list/0 shows that indexes were created).

@jan - maybe I could contribute to your experimental code?

2 Likes

Just reading the new “single sided unification” section in the manual. Regarding the max/3 example, there are two Prolog implementations

max(X,Y,X) :- X >= Y.
max(X,Y,Y) :- Y > X.

and

max(X,Y,X) :- X >= Y, !.
max(_,Y,Y).

both with issues as noted. But how about:

max(X,Y,X) :- X >= Y, !.
max(X,Y,Y) :- Y > X.

i.e., equivalent to if-then-else but in clausal form, with green-cut. I think I prefer this to the Picat solution.

The sum_list example is more interesting, but the intended semantics as a relation needs to be clarified before judging the implementation.

From the manual:

It is likely that in due time significant parts of the libraries will be migrated to use SSU rules, turning many silent failures on type errors into errors.

As I think I’ve stated elsewhere, I would consider this a big step back and a non-upwards compatible change, unless there’s some way of making it configurable.

This implementation suffers from at least one issue, it isn’t symmetric for its first two arguments. Like this:

?- max(X, 1, 1).
X = 1.

?- max(1, X, 1).
ERROR: Arguments are not sufficiently instantiated
ERROR: In:
ERROR:   [11] 1>=_9378

I am now not even questioning the first answer. I am not sure what it means.

EDIT: even if we got “false” instead of the error, it would still not be symmetric.

1 Like

Yes, you’re exactly right, and just the kind of mistake Picat notation is trying to avoid. The first answer happens because of of uniifcation in the head before the test is done, so probably an even better example of the value proposition.

Whether it’s an error or not is a different question. In this particular case, it’s an instantiation problem, i.e., undecidable because X or Y are unknown. In this case I might like the option of failing to a third clause which applies a constraint until they become known.

1 Like

member1/2 don’t work in Picat since using ?=>/2 (or =>/2) don’t allow unification in head. member2/2 is the proper way to go.

I’m not sure how you tested it, but here’s my test in Picat version 3.0:

Picat> cl()
member1(X, [X|_]) ?=> true.
member1(X, [_|Y]) => member1(X, Y).

member2(X, [Y|_]) ?=> X = Y.
member2(X, [_|Y]) => member2(X, Y).
<Ctr-D>
Picat> member1(X,[1,2,3])
no
Picat> member2(X,[1,2,3])
X = 1 ?;
X = 2 ?;
X = 3 ?;

no
1 Like

@j4n_bur53 I actually tried to port Beckert & Posegga’s algorithm (from https://stackoverflow.com/q/63798877/502187 ) in Picat using Picat’s Horn clauses. :slight_smile: However, it don’t work as expected. :frowning:

Unfortunately I cannot help you in your Prolog endeavor regarding this.

@j4n_bur53 Thanks for the tips about this. I’ll see if I can find some way around Picat’s (and B-Prolog’s) limitations regarding this.

One can’t still define new operators in Picat v3, but I try to come around this using functors; for example -(A) is replaced with neg(A) etc, and then $ escape them i the bodies (e.g. $neg(A)) so Picat don’t think that they are functions.

1 Like

I like it. Of course, I am now completely lost in the “imperative” prolog style, because if one rule of a predicate is => then every rule has to be => (I understand why, no complaint) so I end up in writing everything with =>. Oh no. But at least it is honestly imperative…

What I wanted to report is a little bug in SWISH formatting, see here:

https://swish.swi-prolog.org/p/55-picatstyle.pl

Basically, the definition of the guard is highlighted as “unused”.

The => operator is super interesting. How would one use it in other Prolog environments that don’t implement it natively?

I have not tried what you seek but a place to start is on this page look for This is semantically equivalent to the Prolog clause below.

Also there are GitHub commits that convert Prolog using :- to -> so if you find them, just do the reverse. Also see: Example of refactoring code to SSU

It’s not “imperative” style – it’s “deterministic”.
In a way, it’s like writing Haskell style in Prolog (without all the type-checking, which as both pluses and minuses).

I have an implementation for Ciao Prolog based on term/sentence expansion. It is not public yet and we are evaluating if it is worth using in our code. It would be good to have a common testsuite to ensure some level of compatibility.