Difference between revisions of "Language Reference/Terms/Anonymous Predicates"

From wiki.visual-prolog.com
(Review)
 
(11 intermediate revisions by 3 users not shown)
Line 1: Line 1:
An anonymous predicate is an expression that evaluates to a predicate value. The predicate value can be bound to a variable, passed as arguments or returned as result, but the value does not have a name in any class, interface or implementation.
==== Anonymous Predicates ====


Anonymous predicates have the ability to capture values from the context in which the expression occurs, this is a rather powerful ability that can be used to avoid rather excessive amount of strange/unpleasant code.
An anonymous predicate is an expression that evaluates to a predicate value. The value can be bound to a variable, passed as an argument, or returned as a result, but it has no name in any class, interface, or implementation.


==== Syntax ====
Anonymous predicates have the ability to capture values from the context in which the expression occurs; this is a powerful ability that can be used to avoid a rather excessive amount of strange/unpleasant code.
 
===== Syntax =====


Anonymous predicates are terms:
Anonymous predicates are terms:
Line 15: Line 17:


<vipbnf><AnonymousPredicate> : one of
<vipbnf><AnonymousPredicate> : one of
     { ( <Arg>-comma-sep-list ) = <Term> }  
     { ( <Arg>-comma-sep-list ) = <Term> }
     { ( <Arg>-comma-sep-list ) = <Term> :- <Term> }  
     { ( <Arg>-comma-sep-list ) = <Term> :- <Term> }
     { ( <Arg>-comma-sep-list ) :- <Term> }
     { ( <Arg>-comma-sep-list ) :- <Term> }
     { = <Term> }  
     { = <Term> }
     { = <Term> :- <Term> }  
     { = <Term> :- <Term> }
     { :- <Term> }</vipbnf>
     { :- <Term> }</vipbnf>
      
      
Leaving out the argument list means "the required number of arguments" and can be used whenever the arguments are not used.
Leaving out the argument list means "the required number of arguments" and can be used whenever the arguments are not used inside the predicate expression.


==== Semantics ====
===== Semantics =====


An anonymous predicate expression evaluates to a predicate value.
An anonymous predicate expression evaluates to a predicate value.
Line 54: Line 56:
     inc(X) = X+1.</vip>
     inc(X) = X+1.</vip>
          
          
Where the clause <vp>(X) = X+1</vp> can be found in the last line.  I.e. this time in a named predicate.
Where the clause <vp>(X) = X+1</vp> can be found in the last line; i.e., this time in a named predicate.


Variables that are bound outside (i.e. before the occurrence of) an anonymous predicate can be used inside the anonymous predicate. The value of variable will be captured by the anonymous predicate.
Variables that are bound outside (i.e., before the occurrence of) an anonymous predicate can be used inside the anonymous predicate. The value of the variable will be captured by the anonymous predicate.


Variables that are bound in an anonymous predicate are local variables in the anonymous predicate.
Variables that are bound in an anonymous predicate are local variables in the anonymous predicate.
Line 64: Line 66:
An anonymous predicate can capture context, which means that it can refer to things that are defined in its context, especially facts and variables from the clause.
An anonymous predicate can capture context, which means that it can refer to things that are defined in its context, especially facts and variables from the clause.


===== Capturing Variables =====
===== Capturing variables =====


Anonymous predicate occurs in a clause, and this clause may contain variables. Those variables that are bound before the anonymous predicate is met can be used inside the anonymous predicate.
An anonymous predicate occurs in a clause, and this clause may contain variables. Those variables that are bound before the anonymous predicate is met can be used inside the anonymous predicate.
This code illustrates how a variable is captured:
This code illustrates how a variable is captured:


Line 84: Line 86:
         stdio::writef("A = %, B = %", A, B).</vip>
         stdio::writef("A = %, B = %", A, B).</vip>
          
          
We call <vp>createAdder</vp> with <vp>17</vp> as argument. So in the <vp>createAdder</vp> clause <vp>A</vp> is <vp>17</vp>, and therefore the result is <vp>{ (X) = X+17 }</vp>. We say that the anonymous predicate has captured the variable <vp>A</vp>.  
We call <vp>createAdder</vp> with <vp>17</vp> as argument. So in the <vp>createAdder</vp> clause <vp>A</vp> is <vp>17</vp>, and therefore the result is <vp>{ (X) = X+17 }</vp>. We say that the anonymous predicate has captured the variable <vp>A</vp>.  


Since <vp>Add17</vp> is a predicate that adds <vp>17</vp> to its argument, the output of the code will be:
Since <vp>Add17</vp> is a predicate that adds <vp>17</vp> to its argument, the output of the code will be:
Line 90: Line 92:
<source lang="text">A = 21, B = 37</source>
<source lang="text">A = 21, B = 37</source>


===== Capturing ellipsis (...) =====


===== Capturing ellipsis (…) =====
An anonymous predicate can capture the ellipsis variable (i.e., <vp>...</vp>):
 
An anonymous predicate can capture the ellipsis variable (i.e. ):


<vip>clauses
<vip>clauses
Line 100: Line 101:
         qqq(W).</vip>
         qqq(W).</vip>
          
          
<vp>W</vp> captures the ellipsis variable. <vp>qqq</vp> receives a zero-arity predicate, when this predicate is invoked the captured ellipsis variable will be written to the standard output device.
<vp>W</vp> captures the ellipsis variable. <vp>qqq</vp> receives a nullary predicate; when this predicate is invoked the captured ellipsis variable will be written to the standard output device.


===== Capturing Facts =====
===== Capturing facts =====


An anonymous predicate can access facts. If it is created by a class predicate it can access class facts. If it is created by an object predicate it can access both object and class facts.
An anonymous predicate can access facts. If it is created by a class predicate it can access class facts. If it is created by an object predicate it can access both object and class facts.
Consider this code that captures a class fact:
Consider this code that captures a class fact:


Line 120: Line 121:
         stdio::writef("B2 = %", B()).</vip>
         stdio::writef("B2 = %", B()).</vip>
          
          
Both <vp>A</vp> and <vp>B</vp> increment the class fact count, so the result is
Both <vp>A</vp> and <vp>B</vp> increment the class fact <vp>count</vp>, so the result is:


<source lang="text">A1 = 1, B1 = 2, A2 = 3, B2 = 4</source>
<source lang="text">A1 = 1, B1 = 2, A2 = 3, B2 = 4</source>
Line 139: Line 140:
         stdio::writef("B2 = %", B()).</vip>
         stdio::writef("B2 = %", B()).</vip>
          
          
In this case <vp>A</vp> and <vp>B</vp> comes from two different objects, which each have a count fact, so the output will be:
In this case <vp>A</vp> and <vp>B</vp> come from two different objects, which each have a <vp>count</vp> fact, so the output will be:


<source lang="text">A1 = 1, B1 = 1, A2 = 2, B2 = 2</source>
<source lang="text">A1 = 1, B1 = 1, A2 = 2, B2 = 2</source>


Technically, the class version actually doesn't capture anything, it merely have access to the fact. Likewise, the object version doesn't actually capture the fact, instead it captures This and through This it obtains access to the object facts.
Technically, the class version actually does not capture anything; it merely has access to the fact. Likewise, the object version does not actually capture the fact; instead it captures <vp>This</vp> and through <vp>This</vp> it obtains access to the object facts.


===== Capturing This =====
===== Capturing This =====


As described above it is possible to capture <vp>This</vp> and thereby gaining access to objects facts. The same mechanism gives access to calling object predicates.
As described above it is possible to capture <vp>This</vp> and thereby gain access to object facts. The same mechanism gives access to calling object predicates.


<vip>clauses
<vip>clauses
Line 154: Line 155:
clauses
clauses
     inc() :- count := count+1.</vip>
     inc() :- count := count+1.</vip>
<vip>This</vip> can also be used directly:
 
<vp>This</vp> can also be used directly:
 
<vip>clauses
<vip>clauses
     ppp() = { () = aaa::rrr(This) }.</vip>
     ppp() = { () = aaa::rrr(This) }.</vip>
Line 170: Line 173:
         stdio::writef("R(11) = %", R(11)).</vip>
         stdio::writef("R(11) = %", R(11)).</vip>
          
          
To obtain <vp>Q</vp> we call <vp>P</vp> with <vp>3300</vp>, so <vp>A</vp> is <vp>3300</vp> and <vp>Q</vp> therefore becomes <vp>{ (B) = 3300+B } }</vp>, likewise R becomes <vp>{ (B) = 2200+B } }</vp>. So, the output is:
To obtain <vp>Q</vp> we call <vp>P</vp> with <vp>3300</vp>, so <vp>A</vp> is <vp>3300</vp> and <vp>Q</vp> therefore becomes <vp>{ (B) = 3300+B }</vp>; likewise <vp>R</vp> becomes <vp>{ (B) = 2200+B }</vp>. So the output is:


<source lang="text">Q(11) = 3311, R(11) = 2211</source>
<source lang="text">Q(11) = 3311, R(11) = 2211</source>


===== Syntactic Sugar =====


==== Syntactic Sugar ====
If you do not need the arguments they can be skipped. 
So this code fragment:
 
<vip>
P = { (_) :- succeed },
Q = { (_, _) = 0 },
R = { (_, _, _) = _ :- fail }.
</vip>


If you don't need the arguments they can be skipped. 
can be shortened to:
So this code-fragment:


<vip>P = { (_) :- succeed },
<vip>
Q = { (_, _) = 0 },
R = { (_, _, _) = _ :- fail }
Can be shortened down to this:
P = { :- succeed },
P = { :- succeed },
Q = { = 0 },
Q = { = 0 },
R = { = _ :- fail }</vip>
R = { = _ :- fail }.
</vip>


Notice that the arguments are completely skipped. If you write <vp>()</vp> it means zero arguments, whereas skipping the arguments means "a suitable amount" of arguments.
Notice that the arguments are completely skipped. If you write <vp>()</vp> it means zero arguments, whereas skipping the arguments means "a suitable amount" of arguments.
Line 192: Line 200:
==== Examples of practical usage ====
==== Examples of practical usage ====


This section shows some cases where anonymous predicates are very handy.  The examples assume that the PFC scopes core, std, stdio, list and string are open.
The examples assume that the PFC scopes <vp>core</vp>, <vp>std</vp>, <vp>stdio</vp>, <vp>list</vp> and <vp>string</vp> are open.


===== Dummy predicates =====
===== Dummy predicates =====
Line 198: Line 206:
Anonymous predicates are good for creating dummy predicate values:
Anonymous predicates are good for creating dummy predicate values:


<vip>ppp( { = true } ), % don't filter (boolean)
<vip>
qqq( { :- succeed } ), % don't filter (determ)
ppp( { = true } ),     % do not filter (boolean)
rrr( { = 17 } ), % all rows must have height 17</vip>
qqq( { :- succeed } ), % do not filter (determ)
rrr( { = 17 } ).      % all rows must have height 17
</vip>


===== Adaptation =====
===== Adaptation =====
Line 206: Line 216:
In cases where you need a predicate and have one that is almost suitable, you can make the adaptation using an anonymous predicate.
In cases where you need a predicate and have one that is almost suitable, you can make the adaptation using an anonymous predicate.


===== Index adaptation =====
====== Index adaptation ======


Consider the predicate write3:
Consider the predicate write3:
Line 214: Line 224:
clauses
clauses
     write3(Indexer) :-
     write3(Indexer) :-
         foreach I = fromTo(0,2) do
         foreach I = std::fromTo(0,2) do
             write(Indexer(I), "\n")
             write(Indexer(I), "\n")
         end foreach.</vip>
         end foreach.</vip>
          
          
Indexer implements an "array" of strings, <vp>write3</vp> will write the three strings found at the indexes <vp>0</vp>, <vp>1</vp> and <vp>2</vp>. So <vp>write3</vp> assumes that the "array" index is zero-based.
Indexer implements an "array" of strings; <vp>write3</vp> will write the three strings found at the indexes <vp>0</vp>, <vp>1</vp> and <vp>2</vp>. So <vp>write3</vp> assumes that the "array" index is zero-based.
 
However, the "array" we have uses a one-based index:
However, the "array" we have uses a one-based index:


Line 230: Line 241:
         raiseError().</vip>
         raiseError().</vip>
          
          
But using an anonymous predicate we can easily adapt the one-based array to the zero-based usage:
Using an anonymous predicate we can easily adapt the one-based array to the zero-based usage:


<vip>% myArray is 0-based, write3 requires 1-based
<vip>% myArray is 1-based, write3 requires 0-based
Arr = { (N) = myArray(N+1) },
Arr = { (N) = myArray(N+1) },
write3(Arr)</vip>
write3(Arr).</vip>


So we get the expected output:
So we get the expected output:
Line 242: Line 253:
Third</source>
Third</source>


====== Parameter adaptation ======


===== Parameter adaptation =====
In this code <vp>listChildren</vp> will call a <vp>ChildWriter</vp> predicate for each "C is the child of P"-pair:
 
In this code <vp>listChildren</vp> will call a <vp>ChildWriter</vp> predicate for each "<vp>C</vp> is the child of <vp>P</vp>"-pair:


<vip>class predicates
<vip>class predicates
Line 255: Line 265:
         CW("Son2", "Father").</vip>
         CW("Son2", "Father").</vip>
          
          
We will however prefer to list the "<vp>P</vp> is the parent of <vp>C</vp>" using the predicate <vp>wParent</vp>:
We will however prefer to list the "P is the parent of C" using the predicate <vp>wParent</vp>:


<vip>class predicates
<vip>class predicates
Line 266: Line 276:


<vip>Swap = { (A,B) :- wParent(B,A) },
<vip>Swap = { (A,B) :- wParent(B,A) },
listChildren(Swap)</vip>
listChildren(Swap).</vip>


And then the out becomes the expected:
And then the output becomes the expected:


<source lang="text">Father is the parent of Son1
<source lang="text">Father is the parent of Son1
Line 284: Line 294:


<vip>Fewer = { (C,P) :- wKnowParent(C) },
<vip>Fewer = { (C,P) :- wKnowParent(C) },
listChildren(Fewer)</vip>
listChildren(Fewer).</vip>


The output will be:
The output will be:
Line 293: Line 303:
We can also supply dummy arguments:
We can also supply dummy arguments:


<vip>More = { (_,P) :- addChildren(P, 1) }
<vip>More = { (_,P) :- addChildren(P, 1) },
listChildren(More)</vip>
listChildren(More).</vip>


Here <vp>addChildren</vp> will "add a count of children to <vp>P</vp>". Since each invocation corresponds to one child we will call <vp>addChild</vp> supplying <vp>1</vp> as a "dummy" argument. The More is thus an adaptor that both throws away an argument and supplies a dummy argument.
Here <vp>addChildren</vp> will "add a count of children to P". Since each invocation corresponds to one child we will call <vp>addChildren</vp> supplying <vp>1</vp> as a "dummy" argument. The <vp>More</vp> is thus an adaptor that both throws away an argument and supplies a dummy argument.


===== Filters =====
===== Filters =====
Line 304: Line 314:
<vip>class predicates
<vip>class predicates
     writeFiltered :  
     writeFiltered :  
         (string L, filterPredicate{integer} Filter).
         (string L, predicate_dt{integer} Filter).
clauses
clauses
     writeFiltered(Label, Filter) :-
     writeFiltered(Label, Filter) :-
Line 311: Line 321:
         writef("%\t%\n", Label, FilteredList).</vip>
         writef("%\t%\n", Label, FilteredList).</vip>
          
          
Filter is used to filter the list <vp>[1,2,3,4,5,6,7,8,9]</vp>; the filtered list and the Label are written to the standard output.
<vp>Filter</vp> is used to filter the list <vp>[1,2,3,4,5,6,7,8,9]</vp>; the filtered list and the <vp>Label</vp> are written to the standard output.


First we use the allow-all filter:
First we use the allow-all filter:


<vip>All = { :- succeed },
<vip>All = { :- succeed },
writeFiltered("All", All)</vip>
writeFiltered("All", All).</vip>


This filter simply succeeds for any element, so the output is the entire list:
This filter simply succeeds for any element, so the output is the entire list:
Line 325: Line 335:


<vip>None = { :- fail },
<vip>None = { :- fail },
writeFiltered("None", None)</vip>
writeFiltered("None", None).</vip>


The output from this is the empty list:
The output from this is the empty list:
Line 331: Line 341:
<source lang="text">None    []</source>
<source lang="text">None    []</source>


We can also create filters for elements greater than <vp>3</vp> and elements dividable by <vp>3</vp>:
We can also create filters for elements greater than <vp>3</vp> and elements divisible by <vp>3</vp>:


<vip>GreaterThan3 = { (X) :- X > 3 },
<vip>GreaterThan3 = { (X) :- X > 3 },
writeFiltered("> 3", GreaterThan3),
writeFiltered("> 3", GreaterThan3),
Rem3 = { (X) :- 0 = X rem 3 },
Rem3 = { (X) :- 0 = X rem 3 },
writeFiltered("Rem3", Rem3)</vip>
writeFiltered("Rem3", Rem3).</vip>


The output from this is:
The output from this is:
Line 342: Line 352:
<source lang="text">> 3    [4,5,6,7,8,9]
<source lang="text">> 3    [4,5,6,7,8,9]
Rem3    [3,6,9]</source>
Rem3    [3,6,9]</source>


===== Sorting =====
===== Sorting =====


The list package has a sort predicate. But sometimes the default order is not what you need. Therefore the list package also has a predicate sortBy, which sorts the elements using a programmer defined compare operation.
The list package has a sort predicate. But sometimes the default order is not what you need. Therefore the list package also has a predicate <vp>sortBy</vp>, which sorts the elements using a programmer-defined compare operation.
Let us first consider string sorting, using this predicate:
Let us first consider string sorting, using this predicate:


Line 360: Line 369:
         Sorted = sortBy(C, List),
         Sorted = sortBy(C, List),
         write(Label, "\n"),
         write(Label, "\n"),
         foreach S = getMember_nd(Sorted) do
         foreach S = list::getMember_nd(Sorted) do
             writef("    %\n", S)
             writef("    %\n", S)
         end foreach.</vip>
         end foreach.</vip>
Line 369: Line 378:
writeStringsSorted("Normal", Normal),
writeStringsSorted("Normal", Normal),
Descending = { (A,B) = compare(B,A) },
Descending = { (A,B) = compare(B,A) },
writeStringsSorted("Descending", Descending)</vip>
writeStringsSorted("Descending", Descending).</vip>


The output looks like this:
The output looks like this:
Line 395: Line 404:


<vip>domains
<vip>domains
     person = p(string First, string Last).
     person = p(string First, string Last).</vip>
 
For the demonstration we will use this test predicate:
For the demonstration we will use this test predicate:
class predicates
 
<vip>class predicates
     writePersonsSorted :  
     writePersonsSorted :  
         (string Label, comparator{person} Comparator).
         (string Label, comparator{person} Comparator).
Line 412: Line 423:
         Sorted = sortBy(C, List),
         Sorted = sortBy(C, List),
         write(Label, "\n"),
         write(Label, "\n"),
         foreach p(F,L) = getMember_nd(Sorted) do
         foreach p(F,L) = list::getMember_nd(Sorted) do
             writef("    % %\n", F, L)
             writef("    % %\n", F, L)
         end foreach.</vip>
         end foreach.</vip>
Line 421: Line 432:
writePersonsSorted("Normal", Normal),
writePersonsSorted("Normal", Normal),
Descending = { (A,B) = compare(B,A) },
Descending = { (A,B) = compare(B,A) },
writePersonsSorted("Descending", Descending)</vip>
writePersonsSorted("Descending", Descending).</vip>


Since the compare predicate uses left-to-right lexicographic order on the <vp>p</vp>-functor, the result is the same as before:
Since the <vp>compare</vp> predicate uses left-to-right lexicographic order on the <vp>p</vp>-functor, the result is the same as before:


<source lang="text">Normal
<source lang="text">Normal
Line 447: Line 458:


<vip>LN = { (p(_,L1), p(_, L2)) = compare(L1,L2) },
<vip>LN = { (p(_,L1), p(_, L2)) = compare(L1,L2) },
writePersonsSorted("LastName", LN)</vip>
writePersonsSorted("LastName", LN).</vip>


The result is what we expect:
The result is what we expect:
Line 460: Line 471:
     Uma Thurman
     Uma Thurman
     John Wayne</source>
     John Wayne</source>
   


===== Capturing context =====
===== Capturing context (threads & callbacks) =====


As mentioned a very powerful feature of anonymous predicates is the ability to capture context. The examples in this section show some ways you can use this.  
As mentioned a very powerful feature of anonymous predicates is the ability to capture context. The examples in this section show some ways you can use this.  


===== Background treads =====
====== Background threads ======


The routine for starting a thread takes a null-ary predicate and run it in the new thread. But you nearly always need to pass some input data to the job in the new thread.
The routine for starting a thread takes a nullary predicate and runs it in the new thread. But you nearly always need to pass some input data to the job in the new thread.
This is possible in several ways, but the absolutely simplest way is to use anonymous predicates.
This is possible in several ways, but the absolutely simplest way is to use anonymous predicates.
The project <vp>bgDemo</vp> from the Visual Prolog example collection (that can be installed from the IDE) use this method.
The project <vp>bgDemo</vp> from the Visual Prolog example collection (that can be installed from the IDE) uses this method.
The project has a form that can start background job and display status information from the job in a <vp>jobControl</vp> that is added to the form.
The project has a form that can start a background job and display status information from the job in a <vp>jobControl</vp> that is added to the form.
A background job is a predicate that will receive a <vp>jobLog</vp>, which it can use to report status and completion degree:
A background job is a predicate that will receive a <vp>jobLog</vp>, which it can use to report status and completion degree:


<vip>domains
<vip>domains
     job = (jobLog Log).
     job = (jobLog Log).</vip>
A jobLog looks like this:
 
interface jobLog
A <vp>jobLog</vp> looks like this:
 
<vip>interface jobLog


properties
properties
Line 487: Line 499:
end interface jobLog</vip>
end interface jobLog</vip>
      
      
The job can report completion degree by setting the completion property (range <vp>0</vp> to <vp>1</vp>). Likewise, the status property can be used to reflect the current status of the job.
The job can report completion degree by setting the <vp>completion</vp> property (range <vp>0</vp> to <vp>1</vp>). Likewise, the <vp>status</vp> property can be used to reflect the current status of the job.


The status and completion will be shown in the form together with a job name.
The status and completion will be shown in the form together with a job name.
Line 503: Line 515:
         _ = thread::start(Action).</vip>
         _ = thread::start(Action).</vip>
          
          
In this context it is the last three lines that are interesting. <vp>thread::start</vp> takes a null-ary predicate as argument, but a job is a predicate that takes a <vp>jobLog</vp> as argument. Therefore we create an anonymous predicate <vp>Action</vp>, which takes no arguments but invokes <vp>Job</vp> on the <vp>JobLog</vp>. The anonymous predicate has captured both <vp>Job</vp> and <vp>JobLog</vp> from the context, and subsequently both these values are transferred to the new thread even though this thread only receives a null-ary predicate.
In this context it is the last three lines that are interesting. <vp>thread::start</vp> takes a nullary predicate as argument, but a job is a predicate that takes a <vp>jobLog</vp> as argument. Therefore we create an anonymous predicate <vp>Action</vp>, which takes no arguments but invokes <vp>Job</vp> on the <vp>JobLog</vp>. The anonymous predicate has captured both <vp>Job</vp> and <vp>JobLog</vp> from the context, and subsequently both these values are transferred to the new thread even though this thread only receives a nullary predicate.
The jobs in the <vp>bgDemo</vp> project are merely dummy jobs that only manipulate their <vp>jobLog</vp>. One of them looks like this:
 
The jobs in the <vp>bgDemo</vp> project are merely dummy jobs that only manipulate their <vp>jobLog</vp>. One of them looks like this:


<vip>clauses
<vip>clauses
Line 522: Line 535:
         Log:status := "finished".</vip>
         Log:status := "finished".</vip>
          
          
It has two loops which run from <vp>From</vp> to <vp>To</vp> and calculates the completion and sets it on the <vp>Log</vp>. It also sets the status text before, between and after the loops.
It has two loops which run from <vp>From</vp> to <vp>To</vp> and calculates the completion and sets it on the <vp>Log</vp>. It also sets the status text before, between and after the loops.
You may notice that the job does not have the proper job type, because a proper job only has one argument (the <vp>jobLog</vp>), this job has three arguments.
 
Again it is anonymous predicates that help us. The code that adds the jobs to the form looks like this:
You may notice that the job does not have the proper job type, because a proper job only has one argument (the <vp>jobLog</vp>); this job has three arguments.
Again it is anonymous predicates that help us. The code that adds the jobs to the form looks like this:


<vip>predicates
<vip>predicates
Line 541: Line 555:
         ...</vip>
         ...</vip>
          
          
In a more realistic program, it is most likely that <vp>From</vp> and <vp>To</vp> would not be constants, but rather parameters passed from some outer place. In that case these anonymous predicates would also capture variables from the context.
In a more realistic program, it is most likely that <vp>From</vp> and <vp>To</vp> would not be constants, but rather parameters passed from some outer place. In that case these anonymous predicates would also capture variables from the context.
The <vp>jobLog</vp> in the <vp>bgDemo</vp> illustrates one more usage of anonymous predicates. The <vp>jobLog</vp> pass the completion and the status information to a <vp>jobControl</vp>. The <vp>jobControl</vp> is a GUI control on the <vp>jobForm</vp> capable of doing a suitable rendering of the information. This however gives a synchronization problem, because GUI controls are not thread safe and here we want to update some controls from a background thread. This can lead to conflicts, because it is the main thread that draws the controls.
 
The solution is to make transfer the the update of the control to the GUI thread. We do this by posting actions to the control.
The <vp>jobLog</vp> in the <vp>bgDemo</vp> illustrates one more usage of anonymous predicates. The <vp>jobLog</vp> passes the completion and the status information to a <vp>jobControl</vp>. The <vp>jobControl</vp> is a GUI control on the <vp>jobForm</vp> capable of doing a suitable rendering of the information. This however gives a synchronization problem, because GUI controls are not thread safe and here we want to update some controls from a background thread. This can lead to conflicts, because it is the main thread that draws the controls.
 
The solution is to transfer the update of the control to the GUI thread. We do this by posting actions to the control.
The implementation of the status update looks like this:
The implementation of the status update looks like this:


Line 551: Line 567:
         jobCtrl:postAction(Action).</vip>
         jobCtrl:postAction(Action).</vip>
          
          
<vp>Action</vp> is a null-ary predicate that will set the status in the <vp>jobCtrl</vp>. We post this action to the <vp>jobCtrl</vp>. When the <vp>jobCtrl</vp> receives the action it invokes it and is thus updated. This way that actual update of the control will be performed by the GUI thread.
<vp>Action</vp> is a nullary predicate that will set the status in the <vp>jobCtrl</vp>. We post this action to the <vp>jobCtrl</vp>. When the <vp>jobCtrl</vp> receives the action it invokes it and is thus updated. This way, the actual update of the control will be performed by the GUI thread.
This anonymous predicate not only captures the <vp>Status</vp> variable it also captures the <vp>jobCtrl</vp> fact.
This anonymous predicate not only captures the <vp>Status</vp> variable it also captures the <vp>jobCtrl</vp> fact.


===== Asynchronous callbacks =====
====== Asynchronous callbacks ======


Assume that we send commands to a remote service. The command execution is asynchron-ous, so when we execute a command we also give a callback action which will be invoked when the execution of the command is finished. To execute a command we must call this predicate:
Assume that we send commands to a remote service. The command execution is asynchronous, so when we execute a command we also give a callback action which will be invoked when the execution of the command is finished. To execute a command we must call this predicate:


<vip>predicates
<vip>predicates
Line 562: Line 578:
         (command Cmd, predicate{} OnDone).</vip>
         (command Cmd, predicate{} OnDone).</vip>
          
          
Based on this predicate we want to create a similar predicate that can execute a list of commands. A certain command should be executed when the previous command completes.
Based on this predicate we want to create a similar predicate that can execute a list of commands. A certain command should be executed when the previous command completes.
We will also make our list executor asynchronous, so we supply an action that will be invoked when the entire script of commands are finished.
We will also make our list executor asynchronous, so we supply an action that will be invoked when the entire script of commands is finished.
Our script executer will have the form:
Our script executor will have the form:


<vip>predicates
<vip>predicates
Line 571: Line 587:
          
          
If the script is empty we simply invoke the <vp>OnDone</vp> action.  
If the script is empty we simply invoke the <vp>OnDone</vp> action.  
If the script has a command <vp>H</vp> and a rest script <vp>T</vp>, we must first execute <vp>H</vp>, and when it is finished we must execute the rest of the script <vp>T</vp>. So the <vp>OnDone</vp> action we supply when executing <vp>H</vp> must execute <vp>T</vp>.
If the script has a command <vp>H</vp> and a rest script <vp>T</vp>, we must first execute <vp>H</vp>, and when it is finished we must execute the rest of the script <vp>T</vp>. So the <vp>OnDone</vp> action we supply when executing <vp>H</vp> must execute <vp>T</vp>.
All in all, the implementation can look like this:
All in all, the implementation can look like this:


Line 581: Line 597:
         executeCommand(H, DoneH).</vip>
         executeCommand(H, DoneH).</vip>
          
          
We have used an anonymous predicate to perform the execution of the rest of the script. This anonymous predicate captures <vp>T</vp> and <vp>OnDone</vp>.
We have used an anonymous predicate to perform the execution of the rest of the script. This anonymous predicate captures <vp>T</vp> and <vp>OnDone</vp>.
 
<noinclude>{{LanguageReferenceSubarticle|Terms/Anonymous Predicates}}</noinclude>

Latest revision as of 13:05, 22 August 2025

Anonymous Predicates

An anonymous predicate is an expression that evaluates to a predicate value. The value can be bound to a variable, passed as an argument, or returned as a result, but it has no name in any class, interface, or implementation.

Anonymous predicates have the ability to capture values from the context in which the expression occurs; this is a powerful ability that can be used to avoid a rather excessive amount of strange/unpleasant code.

Syntax

Anonymous predicates are terms:

Term : one of
    ...
    AnonymousPredicate
    ...

An anonymous predicate is a nameless clause in curly brackets. Certain parts are optional, giving these forms:

AnonymousPredicate : one of
    { ( Arg-comma-sep-list ) = Term }
    { ( Arg-comma-sep-list ) = Term :- Term }
    { ( Arg-comma-sep-list ) :- Term }
    { = Term }
    { = Term :- Term }
    { :- Term }

Leaving out the argument list means "the required number of arguments" and can be used whenever the arguments are not used inside the predicate expression.

Semantics

An anonymous predicate expression evaluates to a predicate value. Consider this code:

clauses
    run() :-
        Inc = { (X) = X+1 },
        A = Inc(4),
        B = Inc(23),
        stdio::writef("A = %, B = %", A, B).

Inc becomes an increment predicate, so the program will write:

A = 5, B = 24

The code in this example corresponds to this code:

clauses
    run() :-
        Inc = inc,
        A = Inc(4),
        B = Inc(23),
        stdio::writef("A = %, B = %", A, B).
 
class predicates
    inc : (integer X) -> integer R.
clauses
    inc(X) = X+1.

Where the clause (X) = X+1 can be found in the last line; i.e., this time in a named predicate.

Variables that are bound outside (i.e., before the occurrence of) an anonymous predicate can be used inside the anonymous predicate. The value of the variable will be captured by the anonymous predicate.

Variables that are bound in an anonymous predicate are local variables in the anonymous predicate.

Capturing context

An anonymous predicate can capture context, which means that it can refer to things that are defined in its context, especially facts and variables from the clause.

Capturing variables

An anonymous predicate occurs in a clause, and this clause may contain variables. Those variables that are bound before the anonymous predicate is met can be used inside the anonymous predicate. This code illustrates how a variable is captured:

domains
    pred = (integer) -> integer.
 
class predicates
    createAdder : (integer A) -> pred Adder.
clauses
    createAdder(A) = { (X) = X+A }.
 
clauses
    run() :-
        Add17 = createAdder(17),
        A = Add17(4),
        B = Add17(20),
        stdio::writef("A = %, B = %", A, B).

We call createAdder with 17 as argument. So in the createAdder clause A is 17, and therefore the result is { (X) = X+17 }. We say that the anonymous predicate has captured the variable A.

Since Add17 is a predicate that adds 17 to its argument, the output of the code will be:

A = 21, B = 37
Capturing ellipsis (...)

An anonymous predicate can capture the ellipsis variable (i.e., ...):

clauses
    ppp(...)  :-
        W = { () :- stdio::write(...) },
        qqq(W).

W captures the ellipsis variable. qqq receives a nullary predicate; when this predicate is invoked the captured ellipsis variable will be written to the standard output device.

Capturing facts

An anonymous predicate can access facts. If it is created by a class predicate it can access class facts. If it is created by an object predicate it can access both object and class facts. Consider this code that captures a class fact:

class facts
    count : integer := 0.
clauses
    seq() = { () = count :- count := count+1 }.
clauses
    run() :-
        A = seq(),
        B = seq(),
        stdio::writef("A1 = %, ", A()),
        stdio::writef("B1 = %, ", B()),
        stdio::writef("A2 = %, ", A()),
        stdio::writef("B2 = %", B()).

Both A and B increment the class fact count, so the result is:

A1 = 1, B1 = 2, A2 = 3, B2 = 4

In object predicates we can capture object facts. So assuming that seq is an object predicate in myClass, this code illustrates the capture of an object fact:

facts
    count : integer := 0.
clauses
    seq() = { () = count :- count := count+1 }.
clauses
    run() :-
        A = myClass::new():seq(),
        B = myClass::new():seq(),
        stdio::writef("A1 = %, ", A()),
        stdio::writef("B1 = %, ", B()),
        stdio::writef("A2 = %, ", A()),
        stdio::writef("B2 = %", B()).

In this case A and B come from two different objects, which each have a count fact, so the output will be:

A1 = 1, B1 = 1, A2 = 2, B2 = 2

Technically, the class version actually does not capture anything; it merely has access to the fact. Likewise, the object version does not actually capture the fact; instead it captures This and through This it obtains access to the object facts.

Capturing This

As described above it is possible to capture This and thereby gain access to object facts. The same mechanism gives access to calling object predicates.

clauses
    seq() = { () = count :- inc() }.
 
clauses
    inc() :- count := count+1.

This can also be used directly:

clauses
    ppp() = { () = aaa::rrr(This) }.
Nesting

Anonymous predicates can be nested:

clauses
    run() :-
        P = { (A) = { (B) = A+B } },
        Q = P(3300),
        R = P(2200),
        stdio::writef("Q(11) = %, ", Q(11)),
        stdio::writef("R(11) = %", R(11)).

To obtain Q we call P with 3300, so A is 3300 and Q therefore becomes { (B) = 3300+B }; likewise R becomes { (B) = 2200+B }. So the output is:

Q(11) = 3311, R(11) = 2211
Syntactic Sugar

If you do not need the arguments they can be skipped. So this code fragment:

P = { (_) :- succeed },
Q = { (_, _) = 0 },
R = { (_, _, _) = _ :- fail }.

can be shortened to:

P = { :- succeed },
Q = { = 0 },
R = { = _ :- fail }.

Notice that the arguments are completely skipped. If you write () it means zero arguments, whereas skipping the arguments means "a suitable amount" of arguments.

Examples of practical usage

The examples assume that the PFC scopes core, std, stdio, list and string are open.

Dummy predicates

Anonymous predicates are good for creating dummy predicate values:

ppp( { = true } ),     % do not filter (boolean)
qqq( { :- succeed } ), % do not filter (determ)
rrr( { = 17 } ).       % all rows must have height 17
Adaptation

In cases where you need a predicate and have one that is almost suitable, you can make the adaptation using an anonymous predicate.

Index adaptation

Consider the predicate write3:

class predicates
    write3 : (function{integer, string} Indexer).
clauses
    write3(Indexer) :-
        foreach I = std::fromTo(0,2) do
            write(Indexer(I), "\n")
        end foreach.

Indexer implements an "array" of strings; write3 will write the three strings found at the indexes 0, 1 and 2. So write3 assumes that the "array" index is zero-based.

However, the "array" we have uses a one-based index:

class predicates
    myArray : (integer N) -> string Value.
clauses
    myArray(1) = "First" :- !.
    myArray(2) = "Second" :- !.
    myArray(3) = "Third" :- !.
    myArray(_) = _ :-
        raiseError().

Using an anonymous predicate we can easily adapt the one-based array to the zero-based usage:

% myArray is 1-based, write3 requires 0-based
Arr = { (N) = myArray(N+1) },
write3(Arr).

So we get the expected output:

First
Second
Third
Parameter adaptation

In this code listChildren will call a ChildWriter predicate for each "C is the child of P"-pair:

class predicates
    listChildren :
        (predicate{string,string} ChildWriter).
clauses
    listChildren(CW) :-
        CW("Son1", "Father"),
        CW("Son2", "Father").

We will however prefer to list the "P is the parent of C" using the predicate wParent:

class predicates
    wParent : (string Parent, string Child).
clauses
    wParent(P, C) :-
        writef("% is the parent of %\n", P, C).

wParent takes the arguments in the opposite order, but we can easily adapt using an anonymous predicate:

Swap = { (A,B) :- wParent(B,A) },
listChildren(Swap).

And then the output becomes the expected:

Father is the parent of Son1
Father is the parent of Son2

We can also throw away arguments, for example when calling this predicate that only needs a Child:

class predicates
    wKnowParent : (string Child).
clauses
    wKnowParent(C) :-
        writef("We know a parent of %\n", C).

The adaptation looks like this:

Fewer = { (C,P) :- wKnowParent(C) },
listChildren(Fewer).

The output will be:

We know a parent of Son1
We know a parent of Son2

We can also supply dummy arguments:

More = { (_,P) :- addChildren(P, 1) },
listChildren(More).

Here addChildren will "add a count of children to P". Since each invocation corresponds to one child we will call addChildren supplying 1 as a "dummy" argument. The More is thus an adaptor that both throws away an argument and supplies a dummy argument.

Filters

Assume this predicate:

class predicates
    writeFiltered : 
        (string L, predicate_dt{integer} Filter).
clauses
    writeFiltered(Label, Filter) :-
        List = [1,2,3,4,5,6,7,8,9],
        FilteredList = filter(List, Filter),
        writef("%\t%\n", Label, FilteredList).

Filter is used to filter the list [1,2,3,4,5,6,7,8,9]; the filtered list and the Label are written to the standard output.

First we use the allow-all filter:

All = { :- succeed },
writeFiltered("All", All).

This filter simply succeeds for any element, so the output is the entire list:

All     [1,2,3,4,5,6,7,8,9]

It is just as easy to create a filter that fails for all elements and thus allow-none:

None = { :- fail },
writeFiltered("None", None).

The output from this is the empty list:

None    []

We can also create filters for elements greater than 3 and elements divisible by 3:

GreaterThan3 = { (X) :- X > 3 },
writeFiltered("> 3", GreaterThan3),
Rem3 = { (X) :- 0 = X rem 3 },
writeFiltered("Rem3", Rem3).

The output from this is:

> 3     [4,5,6,7,8,9]
Rem3    [3,6,9]
Sorting

The list package has a sort predicate. But sometimes the default order is not what you need. Therefore the list package also has a predicate sortBy, which sorts the elements using a programmer-defined compare operation. Let us first consider string sorting, using this predicate:

class predicates
    writeStringsSorted :
        (string Label, comparator{string} Comp).
clauses
    writeStringsSorted(Label, C) :-
        List = ["John Wayne", "Uma Thurman",
            "Harrison Ford", "Nicolas Cage",
            "Elizabeth Taylor", "Cary Grant",
            "Jerry Lewis", "Robert De Niro"],
        Sorted = sortBy(C, List),
        write(Label, "\n"),
        foreach S = list::getMember_nd(Sorted) do
            writef("    %\n", S)
        end foreach.

We can call the predicate with the "normal" comparator, and using an anonymous predicate we can easily sort it descending as well:

Normal = compare,
writeStringsSorted("Normal", Normal),
Descending = { (A,B) = compare(B,A) },
writeStringsSorted("Descending", Descending).

The output looks like this:

Normal
    Cary Grant
    Elizabeth Taylor
    Harrison Ford
    Jerry Lewis
    John Wayne
    Nicolas Cage
    Robert De Niro
    Uma Thurman
Descending
    Uma Thurman
    Robert De Niro
    Nicolas Cage
    John Wayne
    Jerry Lewis
    Harrison Ford
    Elizabeth Taylor
    Cary Grant

Let us also sort some more complex elements. Here a person has a first name and a last name, using this domain:

domains
    person = p(string First, string Last).

For the demonstration we will use this test predicate:

class predicates
    writePersonsSorted : 
        (string Label, comparator{person} Comparator).
clauses
    writePersonsSorted(Label, C) :-
        List = [p("John","Wayne"),
            p("Uma","Thurman"),
            p("Harrison","Ford"),
            p("Nicolas","Cage"),
            p("Elizabeth","Taylor"),
            p("Cary","Grant"),
            p("Jerry","Lewis"),
            p("Robert","De Niro")],
        Sorted = sortBy(C, List),
        write(Label, "\n"),
        foreach p(F,L) = list::getMember_nd(Sorted) do
            writef("    % %\n", F, L)
        end foreach.

Again we can sort using the normal and a descending comparator:

Normal = compare,
writePersonsSorted("Normal", Normal),
Descending = { (A,B) = compare(B,A) },
writePersonsSorted("Descending", Descending).

Since the compare predicate uses left-to-right lexicographic order on the p-functor, the result is the same as before:

Normal
    Cary Grant
    Elizabeth Taylor
    Harrison Ford
    Jerry Lewis
    John Wayne
    Nicolas Cage
    Robert De Niro
    Uma Thurman
Descending
    Uma Thurman
    Robert De Niro
    Nicolas Cage
    John Wayne
    Jerry Lewis
    Harrison Ford
    Elizabeth Taylor
    Cary Grant

But with the more complex domain we can create a comparator that will sort on last name:

LN = { (p(_,L1), p(_, L2)) = compare(L1,L2) },
writePersonsSorted("LastName", LN).

The result is what we expect:

LastName
    Nicolas Cage
    Robert De Niro
    Harrison Ford
    Cary Grant
    Jerry Lewis
    Elizabeth Taylor
    Uma Thurman
    John Wayne
Capturing context (threads & callbacks)

As mentioned a very powerful feature of anonymous predicates is the ability to capture context. The examples in this section show some ways you can use this.

Background threads

The routine for starting a thread takes a nullary predicate and runs it in the new thread. But you nearly always need to pass some input data to the job in the new thread. This is possible in several ways, but the absolutely simplest way is to use anonymous predicates. The project bgDemo from the Visual Prolog example collection (that can be installed from the IDE) uses this method. The project has a form that can start a background job and display status information from the job in a jobControl that is added to the form. A background job is a predicate that will receive a jobLog, which it can use to report status and completion degree:

domains
    job = (jobLog Log).

A jobLog looks like this:

interface jobLog
 
properties
    completion : real (i).
 
properties
    status : string (i).
 
end interface jobLog

The job can report completion degree by setting the completion property (range 0 to 1). Likewise, the status property can be used to reflect the current status of the job.

The status and completion will be shown in the form together with a job name. A job is started by calling the form's addJob predicate:

clauses
    addJob(JobName, Job) :-
        JobCtrl = jobControl::new(This),
        JobCtrl:name := JobName,
        JobCtrl:show(),
        assert(jobCtrl_fact(JobCtrl)),
        arrange(),
        JobLog = jobLog::new(JobCtrl),
        Action = { :- Job(JobLog) },
        _ = thread::start(Action).

In this context it is the last three lines that are interesting. thread::start takes a nullary predicate as argument, but a job is a predicate that takes a jobLog as argument. Therefore we create an anonymous predicate Action, which takes no arguments but invokes Job on the JobLog. The anonymous predicate has captured both Job and JobLog from the context, and subsequently both these values are transferred to the new thread even though this thread only receives a nullary predicate.

The jobs in the bgDemo project are merely dummy jobs that only manipulate their jobLog. One of them looks like this:

clauses
    job(Log, From, To) :-
        Log:status := "Step 1",
        foreach N1 = std::fromTo(From, To) do
            Log:completion :=
                (N1-From) / (To-From) / 2,
            programControl::sleep(3)
        end foreach,
        Log:status := "Step 2",
        foreach N2 = std::fromTo(From, To) do
            Log:completion :=
                (N2-From) / (To-From) / 2 + 0.5,
            programControl::sleep(3)
        end foreach,
        Log:status := "finished".

It has two loops which run from From to To and calculates the completion and sets it on the Log. It also sets the status text before, between and after the loops.

You may notice that the job does not have the proper job type, because a proper job only has one argument (the jobLog); this job has three arguments. Again it is anonymous predicates that help us. The code that adds the jobs to the form looks like this:

predicates
    onFileNew : window::menuItemListener.
clauses
    onFileNew(_Source, _MenuTag) :-
        JF = jobForm::display(This),
        Job11 = {(L) :- job1::job(L, 1, 1000)},
        Job12 = {(L) :- job1::job(L, 200, 600)},
        Job13 = {(L) :- job1::job(L, 1200, 3000)},
        Job14 = {(L) :- job1::job(L, 1, 1000)},
        JF:addJob("job1.1", Job11),
        JF:addJob("job1.2", Job12),
        JF:addJob("job1.3", Job13),
        JF:addJob("job1.4", Job14),
        ...

In a more realistic program, it is most likely that From and To would not be constants, but rather parameters passed from some outer place. In that case these anonymous predicates would also capture variables from the context.

The jobLog in the bgDemo illustrates one more usage of anonymous predicates. The jobLog passes the completion and the status information to a jobControl. The jobControl is a GUI control on the jobForm capable of doing a suitable rendering of the information. This however gives a synchronization problem, because GUI controls are not thread safe and here we want to update some controls from a background thread. This can lead to conflicts, because it is the main thread that draws the controls.

The solution is to transfer the update of the control to the GUI thread. We do this by posting actions to the control. The implementation of the status update looks like this:

clauses
    status(Status) :-
        Action = { :- jobCtrl:status := Status },
        jobCtrl:postAction(Action).

Action is a nullary predicate that will set the status in the jobCtrl. We post this action to the jobCtrl. When the jobCtrl receives the action it invokes it and is thus updated. This way, the actual update of the control will be performed by the GUI thread. This anonymous predicate not only captures the Status variable it also captures the jobCtrl fact.

Asynchronous callbacks

Assume that we send commands to a remote service. The command execution is asynchronous, so when we execute a command we also give a callback action which will be invoked when the execution of the command is finished. To execute a command we must call this predicate:

predicates
    executeCommand :
        (command Cmd, predicate{} OnDone).

Based on this predicate we want to create a similar predicate that can execute a list of commands. A certain command should be executed when the previous command completes. We will also make our list executor asynchronous, so we supply an action that will be invoked when the entire script of commands is finished. Our script executor will have the form:

predicates
    executeScript :
        (command* Script, predicate{} OnDone).

If the script is empty we simply invoke the OnDone action. If the script has a command H and a rest script T, we must first execute H, and when it is finished we must execute the rest of the script T. So the OnDone action we supply when executing H must execute T. All in all, the implementation can look like this:

clauses
    executeScript([], OnDone) :-
        OnDone().
    executeScript([H|T], OnDone) :-
        DoneH = { :- executeScript(T, OnDone) },
        executeCommand(H, DoneH).

We have used an anonymous predicate to perform the execution of the rest of the script. This anonymous predicate captures T and OnDone.