Picat style rules and functions

Currently SWI-Prolog does not support (=)/2 in the head of (=>)/2. I get:

?- [user].
|: foo(bar)=baz => true.
ERROR: user://1:8:
ERROR:    No permission to modify static procedure `(=)/2'

But I guess Picat would allow (See 1.3 Defining Functions in the
user manual of Picat) the same, and then treat it as follows I guess:

foo(bar, X) => X = baz.

Allowing this function syntax would allow to write many examples more consciese.

But they will not be always double safe,
I guess I will still have a difference between:

?- intersection([], X, L).

?- intersection(X, [], L).

Although for library(ordsets), I did even some extra
checking there. Things can really get eclectic!

See previous discussion on what is now rule/2,3 and how this could be (and probably will be) extended.

There is also problem that the stackoverflow doesn’t get reported. Usually
SWI-Prolog doesn’t abort silently. I guess it would also happen for (:-)/2 rules,
independent from (=>)/2, since it happens inside the meta-interpreter.

But I don’t know yet a trick to keep this behaviour:

?- reverse(X,Y).
X = Y, Y = [] ;
X = Y, Y = [_12714] ;
X = [_12714, _12720],
Y = [_12720, _12714] ;

And nevertheless introduce some error checking? I tried this implementation:

reverse(X, Y) :- reverse(X, [], Y).

reverse(X, _, _) :- var(X), throw(error(instantiation_error,_)).
reverse([], X, R) :- !, R = X.
reverse([X|Y], Z, R) :- !, reverse(Y, [X|Z], R).
reverse(X, _, _) :- throw(error(type_error(list,X),_)).

Everything works fine, until I try reverse/2 as a generator:

?- reverse([1,2,3],X).
X = [3, 2, 1].

?- reverse(2,X).
ERROR: Type error: `list' expected, found `2' (an integer)

?- reverse(X,Y).
ERROR: Arguments are not sufficiently instantiated

Is it useful to have a bidirectional reverse/2? I think this suffices (a straightforward translation of library(lists) reverse/2, using =>):

reverse(List, Reversed) =>
    reverse(List, [], Reversed, Reversed).

reverse([], Ys, Reversed, Tail) =>
    Reversed = Ys,
    Tail = [].
reverse([X|Xs], Rs, Reversed, Tail) =>
    Tail = [_|Bound],
    reverse(Xs, [X|Rs], Reversed, Bound).

Doesn’t work on my side. Still gives:

?- reverse(X,Y). 
ERROR: No rule matches reverse(_2624,[],_2628,_2628) 

Question is how the generator reverse(X,Y) could still work?

I thought that the intent was to raise an exception if reverse/2 has an uninstantiated first argument. If you want to handle an uninstantiated first argument (although I don’t know why this would be wanted), you could just wrap reverse/4 with a freeze/2 or when/2.

For steadfastness (Definition and its relation to logical purity and termination):

?- reverse([1,2],X).
X = [2, 1].

?- reverse(X,Y), X = [1,2].
X = [1, 2],
Y = [2, 1] .

Doesn’t work anymore with single sided unification. But there are possibly better examples for steadfastness. Generally when we view exceptions as type errors, then a logical variable is not yet a type error, since it can be instantiated with the right type in the future.

So passing a logical variable to reverse shouldn’t be an error, while passing an atom a is an error, since the type of reverse/2 first argument is intended to be a list. But a logical variable can be anything also a list, so throwing an error for a logical variable is somehow unsound.

But this here is also clean design!!!

?- reverse(X,Y). 
ERROR: No rule matches reverse(_2624,[],_2628,_2628) 

Since it doesn’t commit prematurely to some failure!!!
We might start a new thread “Failure considered harmful”,
but we are not in the mood to channel our inner Niklaus Wirth.

The original title of the letter, as submitted to CACM, was “A Case Against the Goto Statement”, but CACM editor Niklaus Wirth changed the title to “Go To Statement Considered Harmful”. Frank Rubin published a criticism of Dijkstra’s letter in the March 1987 CACM where it appeared under the title “‘GOTO Considered Harmful’ Considered Harmful”.
Considered harmful - Wikipedia

So maybe the new thread should have title
“Error Considered Harmful’ Considered Harmful”.

1 Like

Take these two variants. Either this:

/* Variant 1 */
norm((A;B), R) => norm(A, C), norm(B, D), R = or(C,D).
norm((A,B), R) => norm(A, C), norm(B, D), R = and(C,D).

Or then:

/* Variant 2 */
norm((A;B), R) => R = or(C,D), norm(A, C), norm(B, D).
norm((A,B), R) => R = and(C,D), norm(A, C), norm(B, D).

Which one is better? If I look at occurs check I have the feeling Variant 1 might be quadratic, whereas Variant 2 might be linear. What is the recommended Picat style?

Variant 2 looks also better to me, since it is more suitable to tail recursion than Variant 1. Picat style rules require (=)/2 to return something, are there recommendations where to put it?