In general, we can represent arbitrary structures in Prolog by using nested terms. For instance, we can represent a Pascal record or a C struct of the form:
personal_data{ name : amit date_of_birth{ year : 1980 month : 5 day : 30 } }
by a Prolog term
personal_data(name(amit),date_of_birth(year(1980),month(5),day(30)))
The list type is built in to Prolog. Analogous to list decomposition by the expression (head:tail) in Haskell, we can decompose a list in Prolog as [Head|Tail]. For instance, we can write a function to check if an item belongs to a list as follows:
member(X,[X|T]). member(X,[Y|T]) :- member(X,T).
In this function, for the first rule to succeed, X must match the head of the list. Formally, this is done using unification, which we have seen in our discussion of type inference. The important thing to note is that unification permits implicit pattern matching of more than one part of the goal, a feature that we noted was not permitted in patterns as used in Haskell definitions. The second rule can be invoked only if the first rule fails, in which case we are guaranteed that the head of the list does not match X, so X and Y are different. In this case, we make a recursive call.
Notice that the names T in the first rule and Y in the second
rule are not really used. Prolog allows the use of a dummy
variable _
in such cases.
member(X,[X|_]). member(X,[_|T]) :- member(X,T).
All other variables are uniformly substituted in a rule--if X
appears in many places, all X's will be replaced by the same
value. However, for convenience, the ``don't care'' variable _
is not subject to this limitation, so each occurrence of _
in a
rule can take on a different value. Thus, we can use the same don't
care variable _
in many places in the same rule without
implicitly asserting that all these positions acquire the same value.
This is similar to the use of _
in Haskell patterns.