I have noticed the use of the operator -> in some Prolog programs, but its meaning is unknown to me.
This is an example of its use:
swish_add_user(User, Passwd, Fields) :-
phrase("$1$", E, _), % use Unix MD5 hashes
crypt(Passwd, E),
string_codes(Hash, E),
Entry = passwd(User, Hash, Fields),
absolute_file_name(swish(passwd), File,
[access(write)]),
( exists_file(File)
-> http_read_passwd_file(File, Data)
; Data = []
),
( selectchk(passwd(User, _, _), Data, Entry, NewData)
-> true
; append(Data, [Entry], NewData)
),
http_write_passwd_file(File, NewData).
What is its use in this clause? When should I use this operator and when not?
PS: The code segment is taken from the authenticate.pl file in the swish repository, an excellent implementation of a Prolog IDE online, by the way.
Joel76's answer gives what is probably by far the most common usage of the ->/2
control construct, which is defined in ISO Prolog. The description given for Goal1 -> Goal2
in the GNU Prolog manual is:
Goal1 -> Goal2
first executes Goal1
and, in case of success, removes all choice-points created by Goal1
and executes Goal2
. This control construct acts like an if-then (Goal1
is the test part and Goal2
the then part). Note that if Goal1
fails ->/2
fails also. ->/2
is often combined with ;/2
to define an if-then-else as follows: Goal1 -> Goal2 ; Goal3
. Note that Goal1 -> Goal2
is the first argument of the (;)/2
and Goal3
(the else part) is the second argument. Such an if-then-else control construct first creates a choice-point for the else-part (intuitively associated with ;/2
) and then executes Goal1
. In case of success, all choice-points created by Goal1
together with the choice-point for the else-part are removed and Goal2
is executed. If Goal1
fails then Goal3
is executed.
It doesn't act like a mathematical logical implication (as its symbolic form might suggest) because, in such an implication clause, F -> T
is true, whereas in Prolog, as mentioned above, if Goal1
fails, then the ->/2
expression fails.
It's important to note operator precedence in this case. In Prolog, the order of precedence is ,
, then ->
, then ;
. Thus, as stated in the description, in an if-then-else construct, Goal1 -> Goal2 ; Goal3
, the Goal1 -> Goal2
expression is the first argument of ;/2
. Here is what happens in various cases:
Goal1 -> Goal2 ; Goal3 if-then-else Notes
----- ----- ----- ------------ -----
Succeeds Succeeds NE* Succeeds Goal1 choice point removed
Goal2 choice point remains (if it exists)
Succeeds Fails NE Fails Goal1 choice point removed
Fails NE Succeeds Succeeds Goal3 choice point remains (if it exists)
Fails NE Fails Fails
*NE = not executed
Because of the precedence, the if-then-else constructs are often parenthesized, as in the example:
( selectchk(passwd(User, _, _), Data, Entry, NewData)
-> true
; append(Data, [Entry], NewData)
),
blah-blah
If the parentheses were not there, then blah-blah
would become part of the else and not executed if selectchk
succeeds.
There is also something interesting that happens to the choice points. If Goal1
succeeds, Prolog will call Goal2
, etc, but won't backtrack to more solutions, if they exist, for Goal1
. To illustrate:
a(1).
a(2).
test :-
( a(X)
-> write(X), nl
; write('no a'), nl
).
| ?- test.
1
yes
In the above, Prolog didn't go back to find X = 2
for a(X)
. On the other hand, backtracking can occur for the else
:
foo :-
( false
-> write('not gonna happen'), nl
; a(X),
write(X), nl
).
| ?- foo.
1
true ? ;
2
yes
Backtracking also occurs for Goal2
:
foo :-
( true
-> a(X), write(X), nl
; write('else'), nl
).
| ?- foo.
1
true ? ;
2
yes
But as you can see, once the Goal2
path is chosen, there's no backtracking to the else
(Goal3
) when solutions for Goal2
are exhausted. A might be expected, executions of Goal2
versus Goal3
are mutually exclusive depending upon the result of Goal1
.
It is the if/then/else in Prolog, for example
( X mod 2 =:= 0
-> writeln(even)
; writeln(odd)).