Predicates vs Functions
Definitions
- Function: something that takes an input and returns an output
- Predicate: a relationship between some things.
Examples
Let's add some numbers and output them. We'll start with functions for adding:
- JavaScript:
function add(x, y) { return x + y }
- Python:
def add(x, y): return x + y
- Haskell:
add x y = x + y
In each language there is some input: (x, y) or just x y in the case of Haskell. Then some operation is carried out (x + y) and the result of this is returned. Thus they can be output like so:
- JavaScript:
console.log(add(1, 2)) or alert(add(1, 2))
- Python:
print(add(1, 2))
- Haskell:
putStrLn $ show $ add 1 2
In each case the result of the addition is passed to some other function without it being named. Our add function takes some input, produces some output, which is then passed to the function for output to the environment: (1, 2) → add → print
Let's compare this to a predicate.
The most obvious difference is the definition is much longer, we've got 3 clauses there and it's still not exhaustive. The reason why brings us to the most important point. add(X, Y, Z)
is not intended to be a function taking an input and producing an output, it is meant to define the add
relationship between X
, Y
, and Z
. It's non-exhaustive because we're only defining the relationship for cases where 2 or more numbers are provided. This is what gives us the ability to query in many different ways, unlike functions.
?-
?-
?-
?-
Predicates can also tell us when something is false:
?-
To output the variable bindings, we explicitly pass them into another predicate:
?-
Note how we can't just feed a predicate into another with the result being passed implicitly. Well if we weren't using alert/1
we can (try writeq/1
in your favourite dialect), but it'll do what you asked, not what you might expect if you're confused about predicates not being functions.
?-
Predicates define relationships, which also means they need more arguments than a function. Plus, by including the output of a function and considering all the modes in which the predicate will be queried, a function can be transformed into a predicate: add(x, y)
becomes add(x, y, z)
.
Predicate Pros
We're in Prolog, we like predicates! Not only can we run them backwards and forwards, but they also solve an assignment headache imperative and functional languages have to deal with. Let's say we want to calculate someone's age and tell them who they share a birthday with, we might write something like this:
function birthday_info(person) {
alert(age(birthday(person));
alert(shared_birthday(birthday(person));
}
Perhaps getting their birth-date involves some expensive call to a database over some network, that's expensive and we're repeating that calculation. So we'd improve it with assignment, locally scoped assignment, but assignment all the same:
function birthday_info(person) {
const bd = birthday(person);
alert(age(bd));
alert(shared_birthday(bd));
}
We might write that, or we might not, what should be done does not always equate with what is done. In Prolog, you have no choice but to write this in an efficient manner and we don't need any assignment and we don't need any special let/where syntax like some languages:
?-
With our predicates, a variable, bound or not, can be passed to multiple other predicates with no assignment or mutation in a manner that encourages efficient evaluation.
Conclusion
Hopefully this clears up a little confusion. Functions and predicates aren't the same thing, the behave quite differently. For a programmer who is fond of explicit code, predicates offer a number of useful advantages.