Hi Hakan,
Thanks for useful tips! They will surely come in handy sooner or later.
I've come to a conclusion that it's easier to understand the logic behind Picat when one realizes that imperative programming in Picat is just syntactic sugar on top of Prolog. If foreach loop is translated into simple Prolog clauses, generators with resumption (aka findnext) are not easy to implement.
Another thing that puzzled me today:
I tried once to port to Picat a Prolog solution to the 8-Queen problem:
[listing 1 (not working)]
queens(N, Queens) :-
Queens = new_list(N),
place_queens(N, Queens, _, _).
place_queens(0, _, _, _) => true.
place_queens(N, Cs, Us, [_|Ds]) =>
place_queens(N - 1, Cs, [_|Us], Ds),
place_queen(N, Cs, Us, Ds).
place_queen(N, [N|_], [N|_], [N|_]) ?=> true.
place_queen(N, [_|Cs], [_|Us], [_|Ds]) =>
place_queen(N, Cs, Us, Ds).
main :-
writeln(findall(Queens, queens(8, Queens))).
I could not get it working until I found an explantation in your article from 2014 "Picat: A Logic-based Multi-paradigm Language":
"One difference between Picat’s pattern matching and Prolog’s is that no variables in calls can get bound
during Picat’s pattern matching, and such bindings must be explicitly done in the body of a rule. For example,
the definition of append/3 cannot be stated in Picat as nicely as in Prolog.
append2(Xs,Ys,Zs) ?=> Xs=[], Ys=Zs.
append2(Xs,Ys,Zs) => Xs=[X|XsR], Zs=[X|ZsR], append2(XsR,Ys,ZsR).
This version has the same non-deterministic behavior as in Prolog, but it does not deterministically branches on
the first argument in case it is non-variable."
The working solution was:
[listing 2 (working)]
queens(N, Queens) =>
Queens = new_list(N),
place_queens(N, Queens, _, _).
place_queens(0, _, _, _) => true.
place_queens(N, Cs, Us, Ds) =>
Ds = [_|DsT],
place_queens(N - 1, Cs, [_|Us], DsT),
place_queen(N, Cs, Us, DsT).
place_queen(N, Cs, Us, Ds) ?=>
Cs = [N|_], Us = [N|_], Ds = [N|_].
place_queen(N, Cs, Us, Ds) =>
Cs = [_|CsT], Us = [_|UsT], Ds = [_|DsT],
place_queen(N, CsT, UsT, DsT).
main =>
writeln(findall(Queens, queens(8, Queens))).
Today I discovered that Picat also supports Prolog-style Horn clauses (which made me happy):
[listing 3 (working)]
queens(N, Queens) :-
Queens = new_list(N),
place_queens(N, Queens, _, _).
place_queens(0, _, _, _) :- !.
place_queens(N, Cs, Us, [_|Ds]) :-
place_queens(N - 1, Cs, [_|Us], Ds),
place_queen(N, Cs, Us, Ds).
place_queen(N, [N|_], [N|_], [N|_]).
place_queen(N, [_|Cs], [_|Us], [_|Ds]) :-
place_queen(N, Cs, Us, Ds).
main :-
writeln(findall(Queens, queens(8, Queens))).
So I came to a conclusion that:
- when predicates are defined using => / ?=> variables in calls CANNOT get bound in pattern matching,
- when predicates are defined using :- variables in calls CAN get bound in pattern matching, in the same way as in Prolog.
However, the following example puzzled me again:
member2(X,[Y|_]) ?=> X=Y.
member2(X,[_|L]) => member2(X,L).
I wonder what is the difference compared to the approach in listing 1 that makes member2 work.
In particular:
member2(X,[_|L]) => member2(X,L).
is not different from:
place_queen(N, [_|Cs], [_|Us], [_|Ds]) =>
place_queen(N, Cs, Us, Ds).
But putting it into listing 2 in place of:
place_queen(N, Cs, Us, Ds) =>
Cs = [_|CsT], Us = [_|UsT], Ds = [_|DsT],
place_queen(N, CsT, UsT, DsT).
results in non-working code.
What is actually the reason for Picat to support both => and :- style, can you provide any tips when to use each?
Cheers, Leszek