relations and functions in mathematics and in Prolog
(If you are looking for the Prolog implementation of mathematical functions like sin, cos, sqrt, log, exp, etc., look under built-in functions.)
A binary relation, in mathematics, is a set of ordered pairs. For example, if we are thinking about the 3 animals: mouse, rabbit, and sheep, and the "smaller" relation, then the set of pairs would be {(mouse, rabbit), (mouse, sheep), (rabbit, sheep)}. In mathematical notation, you would write, e.g., (mouse, rabbit) ∈ smaller, or mouse smaller rabbit. Or you might use a symbol instead of smaller, perhaps "<" - mouse < rabbit, or σ - mouse σ rabbit. This is referred to as an infix notation, because the name of the relation is written in-between the two objects (mouse and rabbit).

In Prolog, instead, by default we use prefix notation: smaller(mouse, rabbit). See op if you want to define an infix operator in a Prolog program.

This view of a relation is sometimes called the extensional view - you can lay out the full "extent" of the relation, at least in the case of a relation over a finite number of items, like our "smaller" example. The alternative view is called intensional, where we focus on the meaning of the relation. In Prolog, this corresponds to a rule expressing the relation, such as:

smaller(X, Y) :-
    volume(X, XVolume),
    volume(Y, YVolume),
    XVolume < YVolume.
This, of course, only defines a meaning or intension for smaller relative to the unspecified meaning of the relation volume(_,_), and the meaning of <, which is defined by the Prolog implementation.

When we come to non-binary relations, such as unary ones (like is_a_dog(fido)) or ternary ones (like gives(john, mary, book) or between(rock, john, hard_place)) the infix relation doesn't make any sense, so prefix notation is normal. (Postfix notation - fido is_a_dog can work for unary relations.)

While we often don't think of it this way, a function is a kind of relation. If you are thinking about the function f(x) = x2, then the essential thing is that for each value of x, there is a value of f(x) (namely x2). In fact, there is a unique value of f(x). So a function (of a single variable) can be viewed as a binary relation such that for every relevant first component x, there is exactly one pair in the relation that has that first component*: the pair is (x, f(x)). In the case of f(x) = x2, the pairs are (x, x2) for every applicable value of x. We need to specify what the applicable values of x are - the domain of the function. If the domain is {1, 2, 3}, then the pairs are {(1,1), (2,4), (3,9)}. If the domain is all natural numbers, then we can't write out the extension of the function in full, but we can use a set expression such as {(n, n×n) | nN} where N signifies the set of all natural numbers.

In Prolog, despite the fact that it uses a relational notation (except for arithmetic expressions), functions are common, but expressed using the relational notation that depends on the definition/convention in the previous paragraph. In our Prolog code defining smaller(X, Y), above, the relation called volume is in fact a function written relationally. We are saying that for every relevant object X there is a value XVolume that is the volume of X. This is a function-type relationship.

The notion of domain applies to relations as well as functions: a more detailed definition of a binary relation on a set A says that such a relation is a subset of the set A×A of all pairs of elements of A. A is the domain of the relation. A binary relation between sets A and B is a subset of A×B. And so on for ternary relations and beyond. A unary relation on A is just a subset of A. Thus is_a_dog is a subset of the set of all Animals. Or a subset of the set of all Things, depending on just how broadly you want to consider the concept of being a dog.

However, in Prolog, domains of relations are not explicitly addressed.# The notion of domain corresponds exactly to that of type in typed programming languages like, C, C++, Java, Haskell, etc. Prolog has no built-in way of confining the definition of a relation (whether extensionally or by a rule) to a particular domain. If you want to, you can build type-checking into your rules, e.g. by giving an definition of a type as a unary relation. For example, if you wanted to define the type of all popes, you could enumerate them all, though the list would be long:

pope(peter).
…
pope(john_paul_ii).
pope(benedict_xvi).
pope(francis).
Then the goal pope(X) would check if X was/is a pope.

Another example: when defining the relation sister in Prolog, you would usually write something like:

sister(Person1, Person2) :-
    female(Person1), female(Person2),
    mother(Person1, Mother), mother(Person2, Mother),
    father(Person1, Father), father(Person2, Father),
    Person1 \== Person2.
female(Person1) and female(Person2) are type-checks - without them, you are defining sibling, not sister. In practice, you can't enumerate all females, unlike all popes, but in many cases you would be able to enumerate all the relevant ones.

In some cases, you might be able to write rules to do type-checking. Prolog includes some built-in predicates for type-checking: number(X) succeeds if X is a number, while integer(X) succeeds only if X is a an integral (whole) number. Here is a rule to check if a number is divisible by 3.

div_by_3(X) :-
    X mod 3 =:= 0.

There is no special syntax for creating functions in Prolog (though there are special arrangements for built-in mathematical functions). To create a function in Prolog, you have to use the relation syntax and create a "relation that happens to be a function". You can do this extensionally, as in

beats(rock, scissors).
beats(paper, rock).
beats(scissors, paper).
or intensionally, with a rule, as with the function f(x) = x2, which could be coded in Prolog like this (using the name square_of):
square_of(X, XSquared) :-
  number(X),
  XSquared is X * X.
used as in the next examples:
?- square_of(3.2, Y).
Y = 10.24
The number(X) test implicitly limits the domain of the function to numbers.

Note that while we defined square_of with a functional usage in mind, it can still be used in a relational sense if desired:

?- square_of(3, 9).
true.
There are limits to this - while some relations can be queried omnidirectionally:
?- beats(scissors, X).
X = paper
?- beats(Y, scissors).
Y = rock
square_of cannot, because the goal XSquared is X*X that is part of its definition cannot:
?- square_of(X, 4).
fail.
even though there are solutions (2 and –2).

Footnotes:
* if there is, not exactly, but at most one pair in the relation that has x as its first member, we say that the relation is a partial function. A partial function is like a function that has "holes" in it - f(x) never has more than one value, but for some x in the domain of the function, f(x) might not have any value. A real-life example is the partial function eldest_child_of, defined on the domain of all adult humans.

# there are a couple of areas of Prolog where there is some built-in type-checking - for example, the built-in predicate < will generate an error message if you try to use it compare a string like 'mouse' with a number like 9.

?- mouse < 9.
ERROR: </2: Arithmetic: `mouse/0' is not a function
That is, the only way Prolog could make sense of this would be if mouse were a built-in function, like sin, cos, or log, but with no arguments. Compare
?- pi < 9.
true.
Here pi/0 is a 0-argument function (i.e. a constant) whose value is an approximation to π, so Prolog can cope.