foxfriends / lumber

Embeddable logic scripting language
5 stars 1 forks source link

Higher order predicates #1

Open foxfriends opened 3 years ago

foxfriends commented 3 years ago

Higher order predicates would help prevent unnecessary code duplication, the same way higher order functions are used in functional programming. For example, to check that all numbers in a list are greater than n, we need to write a specialized recursive predicate for each n:

allGt2([]).
allGt2([X, ..R]) :- gt(2, X), allGt2(R).

allGt3([]).
allGt3([X, ..R]) :- gt(3, X), allGt3(R).

:- allGt3([4, 5, 6]).

But with higher order predicates, a generic all<1>/1 could be written:

all<_>([]).
all<pred/1>([X, ..R]) :- pred(X), all<pred/1>(R).

gt3(X) :- gt(3, X).
gt2(X) :- gt(2, X).

:- all<gt3/1>([4, 5, 6]).
foxfriends commented 3 years ago

Better yet, stealing the templates/meta-predicates idea from Prolog but making the syntax more explicit gives a more powerful feature. The problem with Prolog's version is that there really isn't a way to syntactically differentiate a template passed to a higher-order function from a regular pattern. By either separating the template parameters into a separate parameter list, or using a different syntax for them, we could avoid this problem.

A few possibilities:

Put templates into their own arguments list, requiring to reference them with $.

all<_, _>([]).
all<P, C>([X, ..Xs]) :- $P =:= X, $C -> all<$P, $C>(Xs).

allGt3(Xs) :- all<A, (A > 3)>(Xs).

Prefix the template pattern with $, and require the template argument to also be prefixed (also with $). Note that reusing a template argument requires two $ (one for the reference and one for the syntax).

all($_, $_, []).
all($P, $C, [X, ..Xs]) :- $P =:= X, $C -> all($$P, $$C, Xs).

allGt3(Xs) :- all($A, $A > 3, Xs).

Could use any separator here, not necessarily the ugly #. Maybe remove named parameters (#31) and then use the :, or use a | instead (if that won't cause problems with expressions).

all(_, _ # []).
all(P, C # [X, ..Xs]) :- #P =:= X, #C -> all(#P, #C, Xs).

allGt3(Xs) :- all(A, A > 3 # Xs).

Might be a bit more difficult, but more powerful:

all($_, $_, []).
all($P, $C, [$P, ..Xs]) :- $C -> all($P, $C, Xs).

allGt3(Xs) :- all(A, A > 3, Xs).