Yes, but call/n
can be defined as a first-order thing (just add a corresponding call/n
clause for every predicate in the program), so I don’t think of it as something special. Others might disagree.
“Thunk” is, I suppose an old-fashioned word for what is now called “closure” (although the definition of “closure” is somewhat variable, it seems) – basically, a pointer to code plus some environment. See also delimited continuations, which I don’t fully understand.
So, it might be useful to have something like term_closure/2
or term_callable/2
that maps between a term and something that can be called. Whether this would be more or less confusing than call/1, call/2, etc., I don’t know.
If you have the following code:
number_word(0, zero).
number_word(1, one).
number_word(2, two).
number_word(I, big) :- I > 2.
then there’s a predicate number_word/2
that has 4 clauses, and all of the clauses happen to have the syntax of a term (see: homoiconicity). There’s no intrinsic reason why this to be true – Picat, for example, does not represent its predicates in a form that is directly representable as terms. (Lisp is the canonical example of homoiconicity; but it has the opposite default from Prolog: everything is called unless specifically quoted (e.g., by the QUOTE
special form)).
It’s convenient to have the same syntax for predicates and terms, for things like macro-expansion or program analysis (see clause/2) or for dynamically adding facts/rules (see assertz/1); but it also confuses beginners. (Does the similar thing in Lisp also confuse beginners?)