Synopsis

Description

This module allows one to apply ($/2) and compose (of/2) terms as if
they were functions. One often uses predicates as these functions,
but one can define function behavior for arbitrary terms. See
"What is a function" and "Defining functions" below.

Why? Prolog predicates are more powerful than functions, but sometimes
the syntax is awkward or requires meaningless effort from the
developer (generating and maintaining intermediate variable names and
goals). Using library(func) often results in more succinct, clearer
code. For example, the use of atom_codes/2 in the Synopsis above.

At compile time, library(func) converts function application and
composition into standard predicate calls. There should be no
performance penalty and one can still use nondeterminism.

What is a function?

For our purposes, a function is any term which can be converted into a
predicate call that accepts input in a single variable and produces
output by binding a single variable. The following sections describe
terms which library(func) can natively treat as functions. See
further below for instructions on defining function behavior for
additional terms.

Predicates as functions

Any predicate whose final argument can be viewed as an "output" and
whose penultimate argument can be viewed as "input" can be used,
without modification, as a function.

For example, succ/2 can be seen as accepting an input as the first
argument and producing an output, in the second argument, that's one
greater. Similarly, the term plus(3) can be seen as a predicate
which takes an integer input and generates an integer output that's
three larger.

Because Prolog predicates often follow a convention of having "inputs"
before "outputs", many predicates can be applied and composed as
functions as is. This includes length/2, reverse/2, maplist/3,
append/3, etc.

Dicts

An SWI-Prolog 7 dictionary is considered a function from its keys to its values. Applying the function to a non-existent key fails.

This is similar to SWI Prolog's dot notation but doesn't throw an exception for missing keys. Dicts as functions can be composed and applied just like other functions.

Arithmetic expressions of one variable

Any arithmetic expression of a single variable can be applied and
composed as a function. For example, 2*_+3 is the function which
multiplies a number by two and then adds three. Similarly,
sqrt(X)*X + X/2 is a function even though it uses the input in
three different places.

A format string acceptable as the first argument to format/2 can be
used as a function. It generates an atom, list of codes or string
as output.
The template's type determines the output's type. This offers a
powerful string interpolation syntax visually similar to Python's.

In this next example, X might hold any of the values codes,
chars, number or length.

call('atom_~w' $ X, Atom, Term)

One might also use this interpolation syntax to build a file path:

Path = "/home/~w/src/~w/.git/config" $ [User, Project]

Tilde Terms

A compound term with a single ~ argument is considered a function which takes no input values and produces an output at the ~ position. For example,

atom(atom_string(~,"hello world")).

produces code that's equivalent to

atom_string(X,"hello world"),
atom(X).

This can be conveniently employed with arithmetic expressions.

length(List, ~ is X + Y).

Because tilde terms take no inputs, they can't be used with $/2 or of/2.

Defining functions

Any term can behave as a function by defining additional clauses for
the multifile hook compile_function/4. See the full
documentation for greater detail. In this example, we'll define a
list term as a function from a 0-based index to the corresponding
element of that list.