100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Programming

Higher-Order Predicates in Prolog

How Prolog treats goals as ordinary data, enabling call/N, maplist, foldl, include, exclude, and partition to parameterize control flow.

Advanced PrologIntermediate10 min readJul 10, 2026
Analogies

Goals as Data: The Foundation of Higher-Order Prolog

Because a Prolog goal is just an ordinary term until it is executed, predicates can accept goals as arguments and invoke them later, giving the language a natural style of higher-order programming without any special syntax for functions-as-values. This is the mechanism behind library(apply) predicates like maplist/2..5 and foldl/4..6, and behind library(yall)'s lambda expressions, all of which let you parameterize control flow — 'do this for every element' or 'combine these into one value' — by a predicate supplied at the call site rather than hard-coded into the traversal logic.

🏏

Cricket analogy: It's like a captain who can hand any bowler the ball and trust the same 'bowl an over' routine to work regardless of who's holding it — a Prolog goal is just a term you can pass around and invoke later, the way any bowler slots into the same over-management routine.

call/N and Partial Application

call(Goal, Arg1, ..., ArgN) constructs a new goal by appending the extra arguments to Goal's existing arguments and then calls the result — so call(plus(1), 2, X) calls plus(1, 2, X), unifying X with 3. This lets a predicate like greater_than(5) act as a partially-applied comparison that maplist/3 or include/3 can later supply the missing argument to, which is how Prolog achieves something close to closures despite lacking first-class function syntax.

🏏

Cricket analogy: It's like a fielding coach saying 'catching drill' and letting the specific fielder's name be filled in later when the drill actually runs — call(catching_drill, Fielder) appends the missing argument at call time, just as plus(1) waits for its second number to complete the addition.

maplist/[2-5]: Mapping Across Lists

maplist(Goal, List1) calls Goal on every element of List1, failing if any call fails; the family extends to maplist/3, /4, and /5, which walk two, three, or four lists in lockstep, calling Goal with one element from each list at every step — so maplist(writeln, [a,b,c]) prints three lines, while maplist(plus(1), [1,2,3], Sums) computes Sums = [2,3,4] by pairing each input list element with the corresponding output. All the lists involved must be the same length, or the shorter one determines how many times Goal is called and the rest fail to unify.

🏏

Cricket analogy: It's like a fielding coach running the exact same catching drill on every player in the squad one after another — maplist(Goal, Players) applies one routine uniformly to every element, while maplist/3 pairs each player with a specific target catch count from a second list simultaneously.

prolog
?- maplist(writeln, [red, green, blue]).
red
green
blue

?- maplist([X,Y]>>(Y is X*X), [1,2,3,4], Squares).
Squares = [1, 4, 9, 16].

?- foldl([X,A0,A]>>(A is A0+X), [1,2,3,4,5], 0, Sum).
Sum = 15.

?- include([X]>>(X mod 2 =:= 0), [1,2,3,4,5,6], Evens).
Evens = [2, 4, 6].

?- partition([X]>>(X > 3), [1,5,2,8,3], Big, Small).
Big = [5, 8], Small = [1, 2, 3].

foldl/4, include/3, exclude/3, and partition/4

foldl(Goal, List, V0, V) threads an accumulator through the list, calling Goal(Elem, AccIn, AccOut) at each step so that the final AccOut becomes V — it is how you express sum, product, or any left-to-right reduction without writing an explicit recursive helper predicate. include(Goal, List, Included) and exclude(Goal, List, Excluded) filter a list by keeping or dropping elements for which Goal succeeds, while partition(Goal, List, Included, Excluded) does both passes in one traversal, splitting List into the elements that satisfy Goal and those that don't.

🏏

Cricket analogy: It's like a scorer running total-runs through an over ball by ball, carrying the running total from delivery to delivery — foldl mirrors this threading of an accumulator, while include/exclude are like separately filtering deliveries into 'boundaries' and 'dot balls' lists.

SWI-Prolog's library(yall) supplies the [Params]>>Goal lambda syntax used throughout these examples (e.g., [X,Y]>>(Y is X*X)); without it you must define a named auxiliary predicate for every one-off transformation passed to maplist/foldl, which is considerably more verbose for small, throwaway operations.

  • A Prolog goal is an ordinary term until executed, which lets predicates accept and later call goals supplied as arguments.
  • call(Goal, Arg1, ..., ArgN) appends extra arguments to Goal and calls the result, enabling partial application.
  • maplist/2..5 applies a goal to corresponding elements across one to four lists in lockstep.
  • foldl/4 threads an accumulator through a list left-to-right, expressing reductions without a hand-written recursive helper.
  • include/3 and exclude/3 filter a list by keeping or dropping elements for which a goal succeeds.
  • partition/4 splits a list into two lists in a single traversal based on a goal's success or failure.
  • library(yall) lambda syntax ([Params]>>Goal) avoids defining a named auxiliary predicate for small one-off transformations.

Practice what you learned

Was this page helpful?

Topics covered

#Programming#PrologStudyNotes#HigherOrderPredicatesInProlog#Higher#Order#Predicates#Prolog#StudyNotes#SkillVeris#ExamPrep