As was pointed out, Prolog doesn't map well to procedural thought.
I find the best way to think of a Prolog program and its "database" as a tree (forest?). The analogy is a bit rough since the graph contains cycles (recursion).
When you ask the prolog engine to determine the truth or falseness of a particular assertion (predicate), it commences to do a depth-first, left-to-right traversal of the tree using unification (pattern matching) to guide the traversal. When the traversal reaches a leaf node, the predicate is true
. On backtracking, it... backtracks and continues the tree walk. When there are no more reachable leaf nodes, the predicate fails.
Prolog is a descriptive language: you describe the conditions for success in terms of predicate calculus. Then you simply let Prolog's inference engine find the applicable solutions. If you try to shoehorn procedural, imperative thought into the model, in addition to making things more difficult than they should otherwise be, in my experience, you're pretty much guaranteeing poor performance.
I found Leon Sterling and Eliot Shapiro's textbook, The Art of Prolog, to be invaluable and far more instructive and enlightening than Clocksin & Mellish's Programming in Prolog.

Edited to note: Your sample predicate
pred(X) :-
X = 1 -> do this , ! ; else do that ,
write('x =/= 1')
.
has some problems.
First, just like C or C# or other procedural languages where the and
and or
operators have different precedences so an expression like if ( a && b || c && d ) ...
probably doesn't bind the way you think it does, due to operator precedence, your example predicate is probably not doing what you think it's doing: as written, it binds as
pred(X) :-
X=1 ->
( do_this , ! )
;
( do_that , write( 'x =/= 1' ) )
.
When what you probably wanted was
pred(X) :-
( X=1 ->
( do_this , ! )
;
do_that ,
) ,
write( 'x =/= 1' )
.
You need to use parentheses to omake your intended binding explicit:
pred(X) :-
( X=1 ->
( do_this , ! )
;
do_that
),
write('x =/= 1')
.
Further, the cut (!
) in your example is unnecessary, since the implication operator ->
acts as if there were a cut involved. This:
foo(X) :-
truthy(X) ->
writeln('truthy!')
;
writeln('falsy')
.
is pretty much exactly the same thing as
foo(X) :- truthy(X) , ! ,
writeln( 'truthy' ) .
foo(_) :- writeln( 'falsy' ) .
Third, you should make use of unification and pattern matching in the head. Ignoring the write/1
, your example might make better sense as
pred(1) :- do_this , ! .
pred(X) :- do_that .
And in general, if you're learning prolog, I would say avoid the implication operator (and alternation (logical OR, ';'/2
) in general. Prefer explicit predicates with multiple clauses to express choice. Instead of something like
foo(X) :- ( try_this(X) ; try_that(X) ; finally(X) ) .
prefer
foo(X) :- try_this(X) .
foo(X) :- try_that(X) .
foo(X) :- finally(X) .
And instead of implication:
foo(X) :- X=1 -> try_this(X) ; try_that(X) .
prefer something like this:
foo(1) :- ! , try_this(X) .
foo(X) :- try_that(X) .
I think it makes it easier to grok what's going on since it makes the choice points (and their elimination) explicit.