You said you would normally write "p if and only if q" (I will write q <=> q in the following) as
p :- q.
q :- p.
but that is only correct if
- there are no other clauses for p and q
- there are no variables involved
And under those circumstances, the code doesn't really say anything interesting, only that p and q are equivalent, but whether they are true or false is not stated.
If variables are involved, say
p(X) :- q(X).
q(X) :- p(X).
then the meaning is
forall X: p(X) <= q(X) and forall X': q(X') <= p(X')
which is the same as
forall X,X': p(X) <= q(X) and q(X') <= p(X')
when what you actually wanted to say was
forall X: p(X) <= q(X) and q(X) <= p(X)
On the other hand, as long as you have only a single clause for a predicate, then that clause alone can be read as an "if and only if". For example
p(X,Y) :- q(X,Z).
means
forall X,Y exists Z: p(X,Y) <=> q(X,Z)
and if q/2 is defined elsewhere, then this is a sensible thing to write. That way you can define aliases, projections, etc.
You haven't given any background to your question, but once you consider variables, it makes sense to ask "under which circumstances is p <=> q ?", where p and q defined arbitrarily. Then you would like to write something like
p_iff_q(X) :- p(X) <=> q(X).
which, in plain Prolog, you'd have to rewrite into something like
p_iff_q(X) :- p(X),q(X) ; \+p(X),\+q(X).