From 6b13523f17dac0a2bef3dd69bdf508de04c477fa Mon Sep 17 00:00:00 2001 From: Doug Date: Mon, 22 Sep 2025 10:47:35 -0700 Subject: [PATCH 01/20] added examples. --- doclog.config.pl | 4 +- learn/reif_examples/reif_examples.dj | 303 +++++++++++++++++++++++++++ 2 files changed, 306 insertions(+), 1 deletion(-) create mode 100644 learn/reif_examples/reif_examples.dj diff --git a/doclog.config.pl b/doclog.config.pl index d245d801b..f20151171 100644 --- a/doclog.config.pl +++ b/doclog.config.pl @@ -7,8 +7,10 @@ learn_pages_categories(["First steps", "Tutorials"]). learn_pages([ page("Let's play Brisca", "Tutorials", "lets-play-brisca.dj"), - page("Test page", "First steps", "test-page.dj") + page("Test page", "First steps", "test-page.dj"), + page("reif_examples", "Tutorials", "reif_examples/reif_examples.dj") ]). copy_file("logo/scryer.png", "scryer.png"). copy_file("learn/Spanish_deck_Fournier.jpg", "learn/Spanish_deck_Fournier.jpg"). copy_file("learn/brisca-interactive.png", "learn/brisca-interactive.png"). + \ No newline at end of file diff --git a/learn/reif_examples/reif_examples.dj b/learn/reif_examples/reif_examples.dj new file mode 100644 index 000000000..4d4957a7f --- /dev/null +++ b/learn/reif_examples/reif_examples.dj @@ -0,0 +1,303 @@ +# reif Cookbook +This tutorial shows some examples of using the predicates in [reif](/reif). +Load lambda, dif, clpz, and reif into your toplevel to evaluate the examples in your toplevel. + +If you have an improvement (such as a better example or an explanation) to this tutorial, please [submit a PR](https://github.com/mthom/scryer-prolog/pulls) or +[github issue](https://github.com/mthom/scryer-prolog/issues). + +## Introduction and meta_predicate Review +Review the summary at 25:45 [Power of Prolog meta_predicate declarations](https://www.youtube.com/watch?v=m3cbgebcKng). You will need some understanding of meta_predicate declarations. + +The meta-predicate declarations in reif.pl typically additional arguments are required for a supplied predicate as an argument; the last additional argument will be a reified binary value. + + In this case, the first argument of tfilter is required to be a predicate taking two additional arguments. + +```prolog +:- meta_predicate(tfilter(2, ?, ?)). +''' + +You will probably need to inspect reif.pl to fully understand how to use the module, and to learn +some prolog skills you may not already have. + +There is some code that you may find confusing, which shows a predicate expecting 2 arguments, but only one is provided: +```prolog + if_(call(C_2, E), Fs0 = [E|Fs], Fs0 = Fs), +''' +Fortunatley, you don't need to understand that mechanism to use the reif module. Below there examples of how to use if_. + + +Also, don't be confused when something like this works: +```prolog +?- call( tfilter,=(3),[3],X). + X = [3]. +?- +''' +but this breaks. + +```prolog +?- call( tfilter,#>(3),[3],X). + error(existence_error(procedure,(#>)/3),(#>)/3). +?- +''' +What is happening is that reif has provided =/3, and that is what is being used in tfilter (not the usual =/2). + +But (in my system at the time of writing) #>/2 exists, but there is no #>/3. +A workaround is to define a three-arity predicate to do the comparison (it could be #>/3, but I choose pound_gt): +```prolog +pound_gt(X,Y,true):- X #>Y,!. +pound_gt(_,_,false). + +?- tfilter(pound_gt(3),[1,3],T). + T = [1]. +?- +''' +pound_gt is not very robust, using "!" for example. If you are writing predicates to use with reif, look at the implementation =/3 in reif. + +## memberd_t/3 + +Query holds if a single item 'c' is a member of a list. + +```prolog +?- memberd_t('c',"branch free",true). + true. +''' +Query holds if single item is excluded from a list. Exclusion or inclusion is determined by the third argument. + +```prolog +?- memberd_t('c',"branch free",false). + true. +''' + +Query holds, and X is true if and only if a single item is included in the list. Note single quotes are normally not needed around atoms like 'c'. + +```prolog +?- memberd_t(c,"branch free",X). + X = true. +?- +''' +Query holds for Y in a member of the list and X is false or Y is not a member of the list and X is true. + +```prolog +?- memberd_t(Y,[1,2,3],X). + Y = 1, X = true +; Y = 2, X = true +; Y = 3, X = true +; X = false, dif:dif(1,Y), dif:dif(2,Y), dif:dif(3,Y). +?- +``` +Lists of length 2, where A is a member of exactly one of those lists. + +```prolog +length(L1,2),length(L2,2),memberd_t(A,L1,X), memberd_t(A,L2,Y), dif(X,Y). + L1 = [A,_C], L2 = [_A,_B], X = true, Y = false, dif:dif(_A,A), dif:dif(_B,A) +; L1 = [_A,A], L2 = [_B,_C], X = true, Y = false, dif:dif(_A,A), dif:dif(_B,A), dif:dif(_C,A) +; L1 = [_A,_B], L2 = [A,_C], X = false, Y = true, dif:dif(_A,A), dif:dif(_B,A) +; L1 = [_A,_B], L2 = [_C,A], X = false, Y = true, dif:dif(_A,A), dif:dif(_B,A), dif:dif(_C,A) +; false. +?- +''' +## (=)(A,B,T). + +reif introdces =/3, which holds if and only if the first two arguments are equal, and the third argument T is true or the first two arguments are not equal the the third argument is false. T is the 'reified' truth value of A. + +``` prolog +?- (reif:(=)(1,2,false). + true. +?- (reif:(=)(2,2,false). + false. +?- (reif:(=)(2,2,true). + true. +?- + +''' + +You may find it useful to write predicates where the last argument is a boolean variable containing the result of whether some relation holds, for use with other reif predicates. + + + +## if_/3 + +if_1 calls predicate If_1, adding on the last argument the predicate expects, which is a boolean value, T. +if T then calls the predicate Then_0 +otherwise, calls the predicate Else 0. + +It is worth looking at the source. +```prolog +if_(If_1, Then_0, Else_0) :- + call(If_1, T), + ( T == true -> call(Then_0) + ; T == false -> call(Else_0) + ; nonvar(T) -> throw(error(type_error(boolean, T), _)) + ; throw(error(instantiation_error, _)) + ). +''' +```prolog +?- if_( (X=3),(Y=4),(Z=5)), (X=4;X=3). + X = 3, Y = 4 +; X = 4, Z = 5 +; false. +?- +''' +In the above example, only Y or Z are instantiated variables, but not both. + +Example with a differrent comparison predicate. + +```prolog +greater_than_two(Y,T) :- Y #> 2 ,T=true. +greater_than_two(Y,T) :- Y #< 2 ,T=false. +?- if_(greater_than_two(3),X="works", X="fails"). + X = "works". +?- +''' + +## tfilter/3 + +tfilter/3 is a predicate for filtering lists. + +Again, worth reading the source, because it shows some prolog technique that +may be new to you (unless you are a prolog expert already). + +```prolog +:- meta_predicate(tfilter(2, ?, ?)). +tfilter(_, [], []). +tfilter(C_2, [E|Es], Fs0) :- + if_(call(C_2, E), Fs0 = [E|Fs], Fs0 = Fs), + tfilter(C_2, Es, Fs). +``` +The meta_predicate directive is important. It specifies the first parameter to tfilter is a predicate needing two more arguments. The first required argument is the value to be compared from the list, and the second is the reified result of the comparison (true, or false). + +If your prolog system has #3: +```prolog +?- tfilter(#>(10),[1,20,20], Xs). + error(existence_error(procedure,(#>)/3),(#>)/3). +?- + +''' + +Relating a list, to a list without a certain element: +```prolog +?- tfilter(dif(10),[1,2,3,X],Es). + X = 10, Es = [1,2,3] +; Es = [1,2,3,X], dif:dif(10,X). +?- ''' + +See which predicates produce a non-empty list with tfilter. + +```prolog +?- (X= =(3); X= #<(4); X= =(100) ), tfilter(X,[1,2,3,4,6],Y), Y\=[]. + X = =(3), Y = [3] +; X = #<(4), Y = [6] +; false. +?- ''' + + +## tmember/2 +Just like tfilter, tmembers first argument is a paritally applied 2-arity predicate. +- meta_predicate(tmember(2, ?)). + +```prolog +?- tmember(=(3),[1,2,3,4,6]). + true. +?- + +''' + +It can be used to test for existance of an element for which the supplied predicate holds. +```prolog +?- tmember(#<(1),[1,2,3,4,6]). + true. +?- tmember(#<(12),[1,2,3,4,6]). + false. +?- +''' + ## tmember/3 + +tmember_t can be used to test the existance or non-existance of an element of the list, for which a predicate holds. + +```prolog +?- tmember_t(#<(1),[1,2,3,4,6],T). + T = true. +?- tmember_t(#<(100),[1,2,3,4,6],T). + T = false. +?- + +''' + + ## tpartition/4 +Partition a list based on a partially applied 2-arity predicate. + + ```prolog + :- meta_predicate(tpartition(2, ?, ?, ?)). + ''' + Partition a list based on larger than 4 + ```prolog +?- tpartition(#<(4),[1,2,3,4,5,6,3,4,5],A,B). + A = [5,6,5], B = [1,2,3,4,3,4]. +?- +''' +Find Z, for which #<(Z) partions [1,2,4,5] into [4,5], and [1,2]: +```prolog +?- tpartition(#<(Z),[1,2,4,5],[4,5],[1,2]), indomain(Z). + Z = 2 +; Z = 3 +; false. +?- + +''' +## if_/3 + +[if_/3](https://www.metalevel.at/prolog/metapredicates) "correctly commits to one of two alternatives when admissable …". + +The first argument is a predicate requiring one more argument, which is the reified truth value of a comparsion . + +The prolog system provides some help here. The prolog system uses =/3 in this example, because =/3 wouldn't match. + +```prolog +?- if_(X=8, Z=3, M=4). +''' +or +```prolog +?- if_(X#<8, Z=3, M=4). + M = 4, clpz:(X in 8..sup), clpz:(X+1#=_A), clpz:(_A in 9..sup) +; Z = 3, clpz:(X in inf..7), clpz:(X+1#=_A), clpz:(_A in inf..8). +?- +''' + + +Attemtping to use a 0-ary predicate fails: +```prolog +?- Y=true, if_(Y, Z=3, M=4). + error(existence_error(procedure,true/1),true/1). +?- +''' +Which you can easily work around with: +```prolog +?- Y=true,if_(Y=true, Z=3, M=4). + Y = true, Z = 3. +?- +''' + +## cond_t/3 + +Seems to be an if construct without an else. + +```prolog +?- cond_t(A=B,format("A is B",[]), true),A=west,B=west. +A is B A = west, B = west +; false. +``` + +```prolog +?- cond_t(A=B,format("A is not B",[]), false),A=wild,B=one. +A is not B A = wild, B = one. +``` +## ,/3 and ;/3 + +Todo: Explain how ,/3 and ;/3 are to be used, and how they are used internally withing reif. + \ No newline at end of file From a2986146103d10ffd18930ea88e4d2447b35f4c0 Mon Sep 17 00:00:00 2001 From: Doug Date: Mon, 22 Sep 2025 18:00:22 -0700 Subject: [PATCH 02/20] move file --- doclog.config.pl | 8 +++++++- learn/{reif_examples => }/reif_examples.dj | 0 2 files changed, 7 insertions(+), 1 deletion(-) rename learn/{reif_examples => }/reif_examples.dj (100%) diff --git a/doclog.config.pl b/doclog.config.pl index f20151171..474bf0d91 100644 --- a/doclog.config.pl +++ b/doclog.config.pl @@ -4,12 +4,18 @@ websource("https://github.com/mthom/scryer-prolog/tree/master/src/lib"). omit(["ops_and_meta_predicates.pl", "tabling"]). learn_pages_source_folder("learn"). +reif_learn_source_folder("learn/reif_examples"). learn_pages_categories(["First steps", "Tutorials"]). +reif_learn_pages_categories( ["First steps", "Tutorials"]). learn_pages([ page("Let's play Brisca", "Tutorials", "lets-play-brisca.dj"), page("Test page", "First steps", "test-page.dj"), - page("reif_examples", "Tutorials", "reif_examples/reif_examples.dj") + page("reif", "Tutorials", "reif_examples/reif_examples.dj") ]). +reif_learn_pages([ + page("reif", "Tutorials", "reif_examples.dj") +]). + copy_file("logo/scryer.png", "scryer.png"). copy_file("learn/Spanish_deck_Fournier.jpg", "learn/Spanish_deck_Fournier.jpg"). copy_file("learn/brisca-interactive.png", "learn/brisca-interactive.png"). diff --git a/learn/reif_examples/reif_examples.dj b/learn/reif_examples.dj similarity index 100% rename from learn/reif_examples/reif_examples.dj rename to learn/reif_examples.dj From a1897a51a96302e5b3775bd70883cf3b01f0e39e Mon Sep 17 00:00:00 2001 From: Doug Date: Wed, 24 Sep 2025 16:04:53 -0700 Subject: [PATCH 03/20] some edits --- doclog.config.pl | 6 +- learn/reif_examples.dj | 200 +++++++++++++++++++++++++++-------------- 2 files changed, 132 insertions(+), 74 deletions(-) diff --git a/doclog.config.pl b/doclog.config.pl index 474bf0d91..b1d73df6d 100644 --- a/doclog.config.pl +++ b/doclog.config.pl @@ -4,16 +4,12 @@ websource("https://github.com/mthom/scryer-prolog/tree/master/src/lib"). omit(["ops_and_meta_predicates.pl", "tabling"]). learn_pages_source_folder("learn"). -reif_learn_source_folder("learn/reif_examples"). learn_pages_categories(["First steps", "Tutorials"]). reif_learn_pages_categories( ["First steps", "Tutorials"]). learn_pages([ page("Let's play Brisca", "Tutorials", "lets-play-brisca.dj"), page("Test page", "First steps", "test-page.dj"), - page("reif", "Tutorials", "reif_examples/reif_examples.dj") -]). -reif_learn_pages([ - page("reif", "Tutorials", "reif_examples.dj") + page("reif", "Tutorials", "reif_examples.dj") ]). copy_file("logo/scryer.png", "scryer.png"). diff --git a/learn/reif_examples.dj b/learn/reif_examples.dj index 4d4957a7f..060f19ab9 100644 --- a/learn/reif_examples.dj +++ b/learn/reif_examples.dj @@ -1,83 +1,102 @@ # reif Cookbook -This tutorial shows some examples of using the predicates in [reif](/reif). + +This tutorial shows some examples of using the predicates in [reif](/reif.html). Load lambda, dif, clpz, and reif into your toplevel to evaluate the examples in your toplevel. If you have an improvement (such as a better example or an explanation) to this tutorial, please [submit a PR](https://github.com/mthom/scryer-prolog/pulls) or [github issue](https://github.com/mthom/scryer-prolog/issues). ## Introduction and meta_predicate Review + Review the summary at 25:45 [Power of Prolog meta_predicate declarations](https://www.youtube.com/watch?v=m3cbgebcKng). You will need some understanding of meta_predicate declarations. -The meta-predicate declarations in reif.pl typically additional arguments are required for a supplied predicate as an argument; the last additional argument will be a reified binary value. +Typically, reif predicates provided as arguments typically require the last additional argument will be a reified binary value. In this case, the first argument of tfilter is required to be a predicate taking two additional arguments. -```prolog +``` :- meta_predicate(tfilter(2, ?, ?)). -''' +``` You will probably need to inspect reif.pl to fully understand how to use the module, and to learn some prolog skills you may not already have. There is some code that you may find confusing, which shows a predicate expecting 2 arguments, but only one is provided: -```prolog + +``` if_(call(C_2, E), Fs0 = [E|Fs], Fs0 = Fs), -''' +``` Fortunatley, you don't need to understand that mechanism to use the reif module. Below there examples of how to use if_. Also, don't be confused when something like this works: -```prolog +``` ?- call( tfilter,=(3),[3],X). X = [3]. ?- -''' +``` but this breaks. -```prolog +``` ?- call( tfilter,#>(3),[3],X). error(existence_error(procedure,(#>)/3),(#>)/3). ?- -''' +``` What is happening is that reif has provided =/3, and that is what is being used in tfilter (not the usual =/2). But (in my system at the time of writing) #>/2 exists, but there is no #>/3. A workaround is to define a three-arity predicate to do the comparison (it could be #>/3, but I choose pound_gt): -```prolog + +``` pound_gt(X,Y,true):- X #>Y,!. pound_gt(_,_,false). ?- tfilter(pound_gt(3),[1,3],T). T = [1]. ?- -''' +``` + pound_gt is not very robust, using "!" for example. If you are writing predicates to use with reif, look at the implementation =/3 in reif. +You might wonder if you can write a predicate to relate a predicate with a reified value as the last parameter. + +``` +unreif_reif(P1, P2). +``` + +so that P1(A1, A2, ...AN) holds when P2(A1, A2, ..., AN, T) holds and T is the reified value of P1(A1...AN). + +[Probably not](https://github.com/mthom/scryer-prolog/discussions/2557), but you can probably learn something about writing reified versions of predicates by reading that discussion. + +You can look at predicates like dif/3 or =/3 in [reif](/reif.html) to get ideas about writing predicates that include a reifed boolean value. +``` + ## memberd_t/3 Query holds if a single item 'c' is a member of a list. -```prolog +``` ?- memberd_t('c',"branch free",true). true. -''' +``` Query holds if single item is excluded from a list. Exclusion or inclusion is determined by the third argument. -```prolog +``` ?- memberd_t('c',"branch free",false). true. -''' +``` Query holds, and X is true if and only if a single item is included in the list. Note single quotes are normally not needed around atoms like 'c'. -```prolog +``` ?- memberd_t(c,"branch free",X). X = true. ?- -''' +``` + Query holds for Y in a member of the list and X is false or Y is not a member of the list and X is true. -```prolog +``` ?- memberd_t(Y,[1,2,3],X). Y = 1, X = true ; Y = 2, X = true @@ -85,9 +104,10 @@ Query holds for Y in a member of the list and X is false or Y is not a member of ; X = false, dif:dif(1,Y), dif:dif(2,Y), dif:dif(3,Y). ?- ``` + Lists of length 2, where A is a member of exactly one of those lists. -```prolog +``` length(L1,2),length(L2,2),memberd_t(A,L1,X), memberd_t(A,L2,Y), dif(X,Y). L1 = [A,_C], L2 = [_A,_B], X = true, Y = false, dif:dif(_A,A), dif:dif(_B,A) ; L1 = [_A,A], L2 = [_B,_C], X = true, Y = false, dif:dif(_A,A), dif:dif(_B,A), dif:dif(_C,A) @@ -95,8 +115,9 @@ length(L1,2),length(L2,2),memberd_t(A,L1,X), memberd_t(A,L2,Y), dif(X,Y). ; L1 = [_A,_B], L2 = [_C,A], X = false, Y = true, dif:dif(_A,A), dif:dif(_B,A), dif:dif(_C,A) ; false. ?- -''' -## (=)(A,B,T). +``` + +## (=)(A,B,T). reif introdces =/3, which holds if and only if the first two arguments are equal, and the third argument T is true or the first two arguments are not equal the the third argument is false. T is the 'reified' truth value of A. @@ -109,7 +130,7 @@ reif introdces =/3, which holds if and only if the first two arguments are equal true. ?- -''' +``` You may find it useful to write predicates where the last argument is a boolean variable containing the result of whether some relation holds, for use with other reif predicates. @@ -122,7 +143,7 @@ if T then calls the predicate Then_0 otherwise, calls the predicate Else 0. It is worth looking at the source. -```prolog +``` if_(If_1, Then_0, Else_0) :- call(If_1, T), ( T == true -> call(Then_0) @@ -130,25 +151,27 @@ if_(If_1, Then_0, Else_0) :- ; nonvar(T) -> throw(error(type_error(boolean, T), _)) ; throw(error(instantiation_error, _)) ). -''' -```prolog +``` + +``` ?- if_( (X=3),(Y=4),(Z=5)), (X=4;X=3). X = 3, Y = 4 ; X = 4, Z = 5 ; false. ?- -''' +``` + In the above example, only Y or Z are instantiated variables, but not both. Example with a differrent comparison predicate. -```prolog +``` greater_than_two(Y,T) :- Y #> 2 ,T=true. greater_than_two(Y,T) :- Y #< 2 ,T=false. ?- if_(greater_than_two(3),X="works", X="fails"). X = "works". ?- -''' +``` ## tfilter/3 @@ -157,99 +180,134 @@ tfilter/3 is a predicate for filtering lists. Again, worth reading the source, because it shows some prolog technique that may be new to you (unless you are a prolog expert already). -```prolog +``` :- meta_predicate(tfilter(2, ?, ?)). tfilter(_, [], []). tfilter(C_2, [E|Es], Fs0) :- if_(call(C_2, E), Fs0 = [E|Fs], Fs0 = Fs), tfilter(C_2, Es, Fs). ``` + The meta_predicate directive is important. It specifies the first parameter to tfilter is a predicate needing two more arguments. The first required argument is the value to be compared from the list, and the second is the reified result of the comparison (true, or false). -If your prolog system has #3: -```prolog +``` + +Oddly, scryer prolog 0.9.4-614 has + +``` +#3 + ```: + +``` ?- tfilter(#>(10),[1,20,20], Xs). error(existence_error(procedure,(#>)/3),(#>)/3). ?- -''' +``` Relating a list, to a list without a certain element: -```prolog +``` ?- tfilter(dif(10),[1,2,3,X],Es). X = 10, Es = [1,2,3] ; Es = [1,2,3,X], dif:dif(10,X). -?- ''' +?- +``` See which predicates produce a non-empty list with tfilter. -```prolog +``` ?- (X= =(3); X= #<(4); X= =(100) ), tfilter(X,[1,2,3,4,6],Y), Y\=[]. X = =(3), Y = [3] ; X = #<(4), Y = [6] ; false. -?- ''' - +?- +``` ## tmember/2 -Just like tfilter, tmembers first argument is a paritally applied 2-arity predicate. -- meta_predicate(tmember(2, ?)). -```prolog +Just like tfilter, tmember's first argument is a paritally applied 2-arity predicate: + +``` +:- meta_predicate(tmember(2, ?)). +``` + +``` ?- tmember(=(3),[1,2,3,4,6]). true. ?- -''' +``` + It can be used to test for existance of an element for which the supplied predicate holds. -```prolog + +``` ?- tmember(#<(1),[1,2,3,4,6]). true. ?- tmember(#<(12),[1,2,3,4,6]). false. ?- -''' - ## tmember/3 -tmember_t can be used to test the existance or non-existance of an element of the list, for which a predicate holds. +``` + -```prolog +## tmember_t + + tmembert can be used to test the existance or non-existance of an element of the list, + for which a predicate holds. + + +``` ?- tmember_t(#<(1),[1,2,3,4,6],T). T = true. ?- tmember_t(#<(100),[1,2,3,4,6],T). T = false. ?- -''' +``` ## tpartition/4 -Partition a list based on a partially applied 2-arity predicate. - ```prolog +Partition a list based on a predicate requiring two more arguments. + + ``` :- meta_predicate(tpartition(2, ?, ?, ?)). - ''' - Partition a list based on larger than 4 - ```prolog + ``` + Partition a list based on larger than 4: + + ``` ?- tpartition(#<(4),[1,2,3,4,5,6,3,4,5],A,B). A = [5,6,5], B = [1,2,3,4,3,4]. ?- -''' +``` + Find Z, for which #<(Z) partions [1,2,4,5] into [4,5], and [1,2]: -```prolog + +``` ?- tpartition(#<(Z),[1,2,4,5],[4,5],[1,2]), indomain(Z). Z = 2 ; Z = 3 ; false. ?- -''' +``` + ## if_/3 [if_/3](https://www.metalevel.at/prolog/metapredicates) "correctly commits to one of two alternatives when admissable …". @@ -258,42 +316,46 @@ The first argument is a predicate requiring one more argument, which is the rei The prolog system provides some help here. The prolog system uses =/3 in this example, because =/3 wouldn't match. -```prolog +``` ?- if_(X=8, Z=3, M=4). -''' +``` + or -```prolog + +``` ?- if_(X#<8, Z=3, M=4). M = 4, clpz:(X in 8..sup), clpz:(X+1#=_A), clpz:(_A in 9..sup) ; Z = 3, clpz:(X in inf..7), clpz:(X+1#=_A), clpz:(_A in inf..8). ?- -''' +``` Attemtping to use a 0-ary predicate fails: -```prolog + +``` ?- Y=true, if_(Y, Z=3, M=4). error(existence_error(procedure,true/1),true/1). ?- -''' +``` Which you can easily work around with: -```prolog + +``` ?- Y=true,if_(Y=true, Z=3, M=4). Y = true, Z = 3. ?- -''' +``` ## cond_t/3 Seems to be an if construct without an else. -```prolog +``` ?- cond_t(A=B,format("A is B",[]), true),A=west,B=west. A is B A = west, B = west ; false. ``` -```prolog +``` ?- cond_t(A=B,format("A is not B",[]), false),A=wild,B=one. A is not B A = wild, B = one. ``` From 7195310a01ff9a154f4d7d32396fdfa5db536e30 Mon Sep 17 00:00:00 2001 From: Doug Date: Thu, 25 Sep 2025 17:41:55 -0700 Subject: [PATCH 04/20] updated based on pr feedback --- learn/reif_examples.dj | 61 +++++++++++++++++++++++++----------------- 1 file changed, 37 insertions(+), 24 deletions(-) diff --git a/learn/reif_examples.dj b/learn/reif_examples.dj index 060f19ab9..677436c29 100644 --- a/learn/reif_examples.dj +++ b/learn/reif_examples.dj @@ -6,35 +6,59 @@ Load lambda, dif, clpz, and reif into your toplevel to evaluate the examples in If you have an improvement (such as a better example or an explanation) to this tutorial, please [submit a PR](https://github.com/mthom/scryer-prolog/pulls) or [github issue](https://github.com/mthom/scryer-prolog/issues). -## Introduction and meta_predicate Review +## Introduction -Review the summary at 25:45 [Power of Prolog meta_predicate declarations](https://www.youtube.com/watch?v=m3cbgebcKng). You will need some understanding of meta_predicate declarations. +reif predicates will have as an argument a predicate requiring at least one additional argument. That predicate's last argument will be the reified value of the predicate. -Typically, reif predicates provided as arguments typically require the last additional argument will be a reified binary value. +For example, the argument to tfilter is a predicate taking two additional arguments. Inspecting the source, you see the parameters for the first argument +is named C_2; the naming of parameters ending in _N is used to convey the paremeter will be a predicate taking N additional arguments (in reif, the last of which is expected +to be a reifed boolean value). - In this case, the first argument of tfilter is required to be a predicate taking two additional arguments. ``` :- meta_predicate(tfilter(2, ?, ?)). + +tfilter(_, [], []). +tfilter(C_2, [E|Es], Fs0) :- + … +``` + +C_2 can be a partial goal requring two more arguments, or a 2-ary predicate. In either case the last argument is the reified +truth value. +For example, using dif/3 from reif, and applying one argument 3, gives a partially applied goal X_2 requiring two more arguments. + ``` +?- X_2=dif(3), call(X_2,9,T). + X_2 = dif(3), T = true. +?- +``` +or more succinctly: -You will probably need to inspect reif.pl to fully understand how to use the module, and to learn -some prolog skills you may not already have. +``` +?- call(dif(3),9,T). + T = true. +?- +``` + +You will probably need to inspect reif.pl to fully understand how to use the module . By studying the implementation of predicates, you may also learn +some new prolog skills There is some code that you may find confusing, which shows a predicate expecting 2 arguments, but only one is provided: ``` if_(call(C_2, E), Fs0 = [E|Fs], Fs0 = Fs), ``` -Fortunatley, you don't need to understand that mechanism to use the reif module. Below there examples of how to use if_. +Fortunatley, you don't need to understand that mechanism to use the reif module. The C_2 parameter name gives you enough information about the predicate +you must supply as an argument. +You may be surprised when something like this works: -Also, don't be confused when something like this works: ``` ?- call( tfilter,=(3),[3],X). X = [3]. ?- ``` + but this breaks. ``` @@ -42,6 +66,7 @@ but this breaks. error(existence_error(procedure,(#>)/3),(#>)/3). ?- ``` + What is happening is that reif has provided =/3, and that is what is being used in tfilter (not the usual =/2). But (in my system at the time of writing) #>/2 exists, but there is no #>/3. @@ -69,7 +94,6 @@ so that P1(A1, A2, ...AN) holds when P2(A1, A2, ..., AN, T) holds and T is the r [Probably not](https://github.com/mthom/scryer-prolog/discussions/2557), but you can probably learn something about writing reified versions of predicates by reading that discussion. You can look at predicates like dif/3 or =/3 in [reif](/reif.html) to get ideas about writing predicates that include a reifed boolean value. -``` ## memberd_t/3 @@ -134,25 +158,12 @@ reif introdces =/3, which holds if and only if the first two arguments are equal You may find it useful to write predicates where the last argument is a boolean variable containing the result of whether some relation holds, for use with other reif predicates. - - ## if_/3 if_1 calls predicate If_1, adding on the last argument the predicate expects, which is a boolean value, T. if T then calls the predicate Then_0 otherwise, calls the predicate Else 0. -It is worth looking at the source. -``` -if_(If_1, Then_0, Else_0) :- - call(If_1, T), - ( T == true -> call(Then_0) - ; T == false -> call(Else_0) - ; nonvar(T) -> throw(error(type_error(boolean, T), _)) - ; throw(error(instantiation_error, _)) - ). -``` - ``` ?- if_( (X=3),(Y=4),(Z=5)), (X=4;X=3). X = 3, Y = 4 @@ -166,8 +177,8 @@ In the above example, only Y or Z are instantiated variables, but not both. Example with a differrent comparison predicate. ``` -greater_than_two(Y,T) :- Y #> 2 ,T=true. -greater_than_two(Y,T) :- Y #< 2 ,T=false. +greater_than_two(Y,true) :- Y #> 2. +greater_than_two(Y,false) :- Y #=< 2. ?- if_(greater_than_two(3),X="works", X="fails"). X = "works". ?- @@ -312,6 +323,8 @@ Find Z, for which #<(Z) partions [1,2,4,5] into [4,5], and [1,2]: [if_/3](https://www.metalevel.at/prolog/metapredicates) "correctly commits to one of two alternatives when admissable …". + + The first argument is a predicate requiring one more argument, which is the reified truth value of a comparsion . The prolog system provides some help here. The prolog system uses =/3 in this example, because =/3 wouldn't match. From a47937341eee9d7354bb7faf2bbd2c5fa9fa56b2 Mon Sep 17 00:00:00 2001 From: Doug Date: Sun, 5 Oct 2025 09:09:15 -0700 Subject: [PATCH 05/20] incorporation some feedback. --- learn/reif_examples.dj | 93 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 84 insertions(+), 9 deletions(-) diff --git a/learn/reif_examples.dj b/learn/reif_examples.dj index 677436c29..0c530b410 100644 --- a/learn/reif_examples.dj +++ b/learn/reif_examples.dj @@ -8,13 +8,86 @@ If you have an improvement (such as a better example or an explanation) to this ## Introduction -reif predicates will have as an argument a predicate requiring at least one additional argument. That predicate's last argument will be the reified value of the predicate. +Here are predicates exported by reif. +``` +:- module(reif, [if_/3, (=)/3, (',')/3, (;)/3, cond_t/3, dif/3, + memberd_t/3, tfilter/3, tmember/2, tmember_t/3, + tpartition/4]). +``` + +The last argument of (=)/3, (,)/3, (;)/3, dif/3, or the predicates with principal functor ending in "_t" is the reified boolean value of the predicate. The _t +suffix of a functor name for a predicate expecting a reified boolean value is a convention, as is naming the corresponding argument or parameter T. +You may wish to follow that convention when writing your own predictes. -For example, the argument to tfilter is a predicate taking two additional arguments. Inspecting the source, you see the parameters for the first argument -is named C_2; the naming of parameters ending in _N is used to convey the paremeter will be a predicate taking N additional arguments (in reif, the last of which is expected -to be a reifed boolean value). +### What is a reified boolean value? +In prolog it means representing the truth of some expression or relation as a boolean value. Unlike many languages, the +truthyness of a term like 'A < 10' can not be directly bound to a variable with an assignment. This is because terms have a different meaning in prolog +that you should already be familiar with. +``` +?- A is 112, C= (A<10). + A = 112, C = (112<10). +?- +``` +To make C bound to a boolean value with the built in (->): + +``` +?- A is 2, ( A#<10 -> C=true;C=false), C, format("~s",["C holds"]). +C holds A = 2, C = true. +?- A is 2000, ( A#<10 -> C=true;C=false), C, format("~s",["C holds"]). + false. +?- +``` + + _if from reif provides a better way to do the same. +``` +?- A #= 2,if_(A#<103,C=true,C=false),C,format("~s",["C Holds"]). +C Holds A = 2, C = true. +?- +``` + +true holds (and false doesn't), even when bound to variables: + +``` +?- X=true,X, format("foo",[]). +foo X = true. +?- X=false,X, format("foo",[]). + false. +?- +``` + +oddly, there is no build in < notation in prolog for boolean values. +``` +?- false < true, true. + error(type_error(evaluable,false/0),(is)/2). +?- +``` + + +Note that other Prolog modules may express a reified boolean value as 0 or 1 or choose some other representation instead of false and true. + + +### Naming Conventions in reif + +We will use tmember_t to demonstrate an example. T is bound to true when a condition holds, and false when it doesn't. + +``` +?- tmember_t(#<(1),[1,2,3,4,6],T). + T = true. +?- tmember_t(#<(100),[1,2,3,4,6],T). + T = false. +?- +``` + +The prefix of "t_" (tmember/2,tmember_t/3, tfilter, tpartition/4) in a predicate name indicates the first +argument will be a predicate that takes as as an argument a predicate (or partially applied predicate) taking additonal arguments, the last of which will +be a reified boolean value for whether the query holds. + +The naming convention for the parameters which are predicates is C_N, where C may convey some meaning about the arguments purpose, and the +N indicates how many additional arguments are required by the predicate. + +It is necessary useful to inspect the source of reif so you can see the information conveyed in the predicate and parameter names. ``` :- meta_predicate(tfilter(2, ?, ?)). @@ -22,9 +95,9 @@ tfilter(_, [], []). tfilter(C_2, [E|Es], Fs0) :- … ``` +C_2 can be a partial goal requring two more arguments, or a 2-ary predicate. In either case the parameter of C_2 is the reified +truth value. -C_2 can be a partial goal requring two more arguments, or a 2-ary predicate. In either case the last argument is the reified -truth value. For example, using dif/3 from reif, and applying one argument 3, gives a partially applied goal X_2 requiring two more arguments. ``` @@ -51,6 +124,8 @@ There is some code that you may find confusing, which shows a predicate expectin Fortunatley, you don't need to understand that mechanism to use the reif module. The C_2 parameter name gives you enough information about the predicate you must supply as an argument. +### Surprises + You may be surprised when something like this works: ``` @@ -70,7 +145,7 @@ but this breaks. What is happening is that reif has provided =/3, and that is what is being used in tfilter (not the usual =/2). But (in my system at the time of writing) #>/2 exists, but there is no #>/3. -A workaround is to define a three-arity predicate to do the comparison (it could be #>/3, but I choose pound_gt): +A workaround is to define a three-arity predicate to do the comparison (it could be #>/3, but I choose pound_gt_t): ``` pound_gt(X,Y,true):- X #>Y,!. @@ -100,13 +175,13 @@ You can look at predicates like dif/3 or =/3 in [reif](/reif.html) to get ideas Query holds if a single item 'c' is a member of a list. ``` -?- memberd_t('c',"branch free",true). +?- memberd_t(c,"branch free",true). true. ``` Query holds if single item is excluded from a list. Exclusion or inclusion is determined by the third argument. ``` -?- memberd_t('c',"branch free",false). +?- memberd_t(c,"branch free",false). true. ``` From 2bf4f5ea11ab2fa95e500dbc44f1bbf6e53614d4 Mon Sep 17 00:00:00 2001 From: Doug Date: Sun, 5 Oct 2025 15:18:53 -0700 Subject: [PATCH 06/20] incorporating feedback --- learn/reif_examples.dj | 165 +++++++++++++++++++++-------------------- 1 file changed, 83 insertions(+), 82 deletions(-) diff --git a/learn/reif_examples.dj b/learn/reif_examples.dj index 0c530b410..86800530d 100644 --- a/learn/reif_examples.dj +++ b/learn/reif_examples.dj @@ -1,4 +1,4 @@ -# reif Cookbook +# reif Cookbook This tutorial shows some examples of using the predicates in [reif](/reif.html). Load lambda, dif, clpz, and reif into your toplevel to evaluate the examples in your toplevel. @@ -8,31 +8,30 @@ If you have an improvement (such as a better example or an explanation) to this ## Introduction -Here are predicates exported by reif. -``` +### Exported Predicates + +Here are predicates exported by reif: + +``` prolog :- module(reif, [if_/3, (=)/3, (',')/3, (;)/3, cond_t/3, dif/3, memberd_t/3, tfilter/3, tmember/2, tmember_t/3, tpartition/4]). ``` -The last argument of (=)/3, (,)/3, (;)/3, dif/3, or the predicates with principal functor ending in "_t" is the reified boolean value of the predicate. The _t -suffix of a functor name for a predicate expecting a reified boolean value is a convention, as is naming the corresponding argument or parameter T. -You may wish to follow that convention when writing your own predictes. - ### What is a reified boolean value? In prolog it means representing the truth of some expression or relation as a boolean value. Unlike many languages, the truthyness of a term like 'A < 10' can not be directly bound to a variable with an assignment. This is because terms have a different meaning in prolog that you should already be familiar with. - -``` +1111 +``` prolog ?- A is 112, C= (A<10). A = 112, C = (112<10). ?- ``` To make C bound to a boolean value with the built in (->): -``` +``` prolog ?- A is 2, ( A#<10 -> C=true;C=false), C, format("~s",["C holds"]). C holds A = 2, C = true. ?- A is 2000, ( A#<10 -> C=true;C=false), C, format("~s",["C holds"]). @@ -40,8 +39,9 @@ C holds A = 2, C = true. ?- ``` - _if from reif provides a better way to do the same. -``` +if_ from reif provides a better way to do the same, and you should consider using if_ unless you ahve a compelling reason to use -> . + +``` prolog ?- A #= 2,if_(A#<103,C=true,C=false),C,format("~s",["C Holds"]). C Holds A = 2, C = true. ?- @@ -49,7 +49,7 @@ C Holds A = 2, C = true. true holds (and false doesn't), even when bound to variables: -``` +``` prolog ?- X=true,X, format("foo",[]). foo X = true. ?- X=false,X, format("foo",[]). @@ -58,13 +58,12 @@ foo X = true. ``` oddly, there is no build in < notation in prolog for boolean values. -``` +``` prolog ?- false < true, true. error(type_error(evaluable,false/0),(is)/2). ?- ``` - Note that other Prolog modules may express a reified boolean value as 0 or 1 or choose some other representation instead of false and true. @@ -72,7 +71,7 @@ Note that other Prolog modules may express a reified boolean value as 0 or 1 or We will use tmember_t to demonstrate an example. T is bound to true when a condition holds, and false when it doesn't. -``` +``` prolog ?- tmember_t(#<(1),[1,2,3,4,6],T). T = true. ?- tmember_t(#<(100),[1,2,3,4,6],T). @@ -80,15 +79,22 @@ We will use tmember_t to demonstrate an example. T is bound to true when a con ?- ``` -The prefix of "t_" (tmember/2,tmember_t/3, tfilter, tpartition/4) in a predicate name indicates the first -argument will be a predicate that takes as as an argument a predicate (or partially applied predicate) taking additonal arguments, the last of which will +The prefix of t_ (tmember/2,tmember_t/3, tfilter, tpartition/4) in a predicate name indicates the first +argument is a predicate that takes as as an argument a predicate (or partially applied predicate) taking additonal arguments, the last of which will be a reified boolean value for whether the query holds. The naming convention for the parameters which are predicates is C_N, where C may convey some meaning about the arguments purpose, and the N indicates how many additional arguments are required by the predicate. -It is necessary useful to inspect the source of reif so you can see the information conveyed in the predicate and parameter names. -``` +A suffix of _t indicates the last parameter of a predicate is a reified boolean value. The last argument of the following are also reifed boolean values: +(=)/3, (,)/3, (;)/3, dif/3. + + +You may wish to follow that convention when writing your own predicates. + +It is useful to inspect the source of reif so you can see the information conveyed in the predicate and predicate parameter names. + +``` prolog :- meta_predicate(tfilter(2, ?, ?)). tfilter(_, [], []). @@ -100,14 +106,14 @@ truth value. For example, using dif/3 from reif, and applying one argument 3, gives a partially applied goal X_2 requiring two more arguments. -``` +``` prolog ?- X_2=dif(3), call(X_2,9,T). X_2 = dif(3), T = true. ?- ``` or more succinctly: -``` +``` prolog ?- call(dif(3),9,T). T = true. ?- @@ -118,7 +124,7 @@ some new prolog skills There is some code that you may find confusing, which shows a predicate expecting 2 arguments, but only one is provided: -``` +``` prolog if_(call(C_2, E), Fs0 = [E|Fs], Fs0 = Fs), ``` Fortunatley, you don't need to understand that mechanism to use the reif module. The C_2 parameter name gives you enough information about the predicate @@ -128,7 +134,7 @@ you must supply as an argument. You may be surprised when something like this works: -``` +``` prolog ?- call( tfilter,=(3),[3],X). X = [3]. ?- @@ -136,31 +142,30 @@ You may be surprised when something like this works: but this breaks. -``` +``` prolog ?- call( tfilter,#>(3),[3],X). error(existence_error(procedure,(#>)/3),(#>)/3). ?- ``` -What is happening is that reif has provided =/3, and that is what is being used in tfilter (not the usual =/2). +What is happening is that reif has provided (=)/3, and that is what is being used in tfilter (not the usual (=)/2). -But (in my system at the time of writing) #>/2 exists, but there is no #>/3. -A workaround is to define a three-arity predicate to do the comparison (it could be #>/3, but I choose pound_gt_t): +But (in my system at the time of writing) (#>)/2 exists, but there is no (#>)/3. +A workaround is to define a three-arity predicate to do the comparison (it could be (#>)/3, but I choose pound_gt_t): -``` -pound_gt(X,Y,true):- X #>Y,!. -pound_gt(_,_,false). +``` prolog +pound_gt_t(X,Y,T) :- if_(Y #< X, T=true, T=false). -?- tfilter(pound_gt(3),[1,3],T). +?- tfilter(pound_gt_t(3),[1,3],T). T = [1]. ?- ``` -pound_gt is not very robust, using "!" for example. If you are writing predicates to use with reif, look at the implementation =/3 in reif. +If you are writing predicates to use with reif, look at the implementation (=)/3 in reif. You might wonder if you can write a predicate to relate a predicate with a reified value as the last parameter. -``` +``` prolog unreif_reif(P1, P2). ``` @@ -168,26 +173,26 @@ so that P1(A1, A2, ...AN) holds when P2(A1, A2, ..., AN, T) holds and T is the r [Probably not](https://github.com/mthom/scryer-prolog/discussions/2557), but you can probably learn something about writing reified versions of predicates by reading that discussion. -You can look at predicates like dif/3 or =/3 in [reif](/reif.html) to get ideas about writing predicates that include a reifed boolean value. +You can look at predicates like dif/3 or (=)/3 in [reif](/reif.html) to get ideas about writing predicates that include a reifed boolean value. ## memberd_t/3 Query holds if a single item 'c' is a member of a list. -``` +``` prolog ?- memberd_t(c,"branch free",true). true. ``` Query holds if single item is excluded from a list. Exclusion or inclusion is determined by the third argument. -``` +``` prolog ?- memberd_t(c,"branch free",false). true. ``` Query holds, and X is true if and only if a single item is included in the list. Note single quotes are normally not needed around atoms like 'c'. -``` +``` prolog ?- memberd_t(c,"branch free",X). X = true. ?- @@ -195,7 +200,7 @@ Query holds, and X is true if and only if a single item is included in the list. Query holds for Y in a member of the list and X is false or Y is not a member of the list and X is true. -``` +``` prolog ?- memberd_t(Y,[1,2,3],X). Y = 1, X = true ; Y = 2, X = true @@ -206,7 +211,7 @@ Query holds for Y in a member of the list and X is false or Y is not a member of Lists of length 2, where A is a member of exactly one of those lists. -``` +``` prolog length(L1,2),length(L2,2),memberd_t(A,L1,X), memberd_t(A,L2,Y), dif(X,Y). L1 = [A,_C], L2 = [_A,_B], X = true, Y = false, dif:dif(_A,A), dif:dif(_B,A) ; L1 = [_A,A], L2 = [_B,_C], X = true, Y = false, dif:dif(_A,A), dif:dif(_B,A), dif:dif(_C,A) @@ -239,7 +244,7 @@ if_1 calls predicate If_1, adding on the last argument the predicate expects, w if T then calls the predicate Then_0 otherwise, calls the predicate Else 0. -``` +``` prolog ?- if_( (X=3),(Y=4),(Z=5)), (X=4;X=3). X = 3, Y = 4 ; X = 4, Z = 5 @@ -251,7 +256,7 @@ In the above example, only Y or Z are instantiated variables, but not both. Example with a differrent comparison predicate. -``` +``` prolog greater_than_two(Y,true) :- Y #> 2. greater_than_two(Y,false) :- Y #=< 2. ?- if_(greater_than_two(3),X="works", X="fails"). @@ -266,7 +271,7 @@ tfilter/3 is a predicate for filtering lists. Again, worth reading the source, because it shows some prolog technique that may be new to you (unless you are a prolog expert already). -``` +``` prolog :- meta_predicate(tfilter(2, ?, ?)). tfilter(_, [], []). tfilter(C_2, [E|Es], Fs0) :- @@ -278,29 +283,29 @@ The meta_predicate directive is important. It specifies the first parameter to If your prolog system has -``` -#3 + ``` prolog + (#>)3 ```: -``` +``` prolog ?- tfilter(#>(10),[1,20,20], Xs). error(existence_error(procedure,(#>)/3),(#>)/3). ?- @@ -308,7 +313,7 @@ Oddly, scryer prolog 0.9.4-614 has ``` Relating a list, to a list without a certain element: -``` +``` prolog ?- tfilter(dif(10),[1,2,3,X],Es). X = 10, Es = [1,2,3] ; Es = [1,2,3,X], dif:dif(10,X). @@ -317,7 +322,7 @@ Relating a list, to a list without a certain element: See which predicates produce a non-empty list with tfilter. -``` +``` prolog ?- (X= =(3); X= #<(4); X= =(100) ), tfilter(X,[1,2,3,4,6],Y), Y\=[]. X = =(3), Y = [3] ; X = #<(4), Y = [6] @@ -325,41 +330,33 @@ See which predicates produce a non-empty list with tfilter. ?- ``` -## tmember/2 -Just like tfilter, tmember's first argument is a paritally applied 2-arity predicate: +## tmember/2 -``` -:- meta_predicate(tmember(2, ?)). -``` +Just like tfilter, tmember's first argument is a paritally applied 2-arity predicate. +It can be used to test for existance of an element for which the supplied predicate holds. -``` +``` prolog ?- tmember(=(3),[1,2,3,4,6]). true. -?- - -``` - - -It can be used to test for existance of an element for which the supplied predicate holds. - -``` ?- tmember(#<(1),[1,2,3,4,6]). true. ?- tmember(#<(12),[1,2,3,4,6]). false. ?- - ``` + + ## tmember_t + - tmembert can be used to test the existance or non-existance of an element of the list, - for which a predicate holds. + tmember_t can be used to test the existance or non-existance of an element of the list, + for which a predicate holds. -``` +``` prolog ?- tmember_t(#<(1),[1,2,3,4,6],T). T = true. ?- tmember_t(#<(100),[1,2,3,4,6],T). @@ -367,17 +364,17 @@ It can be used to test for existance of an element for which the supplied predic ?- ``` - - ## tpartition/4 + +## tparttition/4 Partition a list based on a predicate requiring two more arguments. - ``` + ``` prolog :- meta_predicate(tpartition(2, ?, ?, ?)). ``` Partition a list based on larger than 4: - ``` + ``` prolog ?- tpartition(#<(4),[1,2,3,4,5,6,3,4,5],A,B). A = [5,6,5], B = [1,2,3,4,3,4]. ?- @@ -385,7 +382,7 @@ Partition a list based on a predicate requiring two more arguments. Find Z, for which #<(Z) partions [1,2,4,5] into [4,5], and [1,2]: -``` +``` prolog ?- tpartition(#<(Z),[1,2,4,5],[4,5],[1,2]), indomain(Z). Z = 2 ; Z = 3 @@ -404,13 +401,13 @@ The first argument is a predicate requiring one more argument, which is the rei The prolog system provides some help here. The prolog system uses =/3 in this example, because =/3 wouldn't match. -``` +``` prolog ?- if_(X=8, Z=3, M=4). ``` or -``` +``` prolog ?- if_(X#<8, Z=3, M=4). M = 4, clpz:(X in 8..sup), clpz:(X+1#=_A), clpz:(_A in 9..sup) ; Z = 3, clpz:(X in inf..7), clpz:(X+1#=_A), clpz:(_A in inf..8). @@ -420,7 +417,7 @@ or Attemtping to use a 0-ary predicate fails: -``` +``` prolog ?- Y=true, if_(Y, Z=3, M=4). error(existence_error(procedure,true/1),true/1). ?- @@ -437,17 +434,21 @@ Which you can easily work around with: Seems to be an if construct without an else. -``` +``` prolog ?- cond_t(A=B,format("A is B",[]), true),A=west,B=west. A is B A = west, B = west ; false. ``` -``` +``` prolog ?- cond_t(A=B,format("A is not B",[]), false),A=wild,B=one. A is not B A = wild, B = one. ``` ## ,/3 and ;/3 Todo: Explain how ,/3 and ;/3 are to be used, and how they are used internally withing reif. - \ No newline at end of file + +# Motiviation for Using reif + +Todo: add some examples compared to using list or builtin predicates. + From 0d5dd009cd1ada6c6491035f80dfc3add4775cbf Mon Sep 17 00:00:00 2001 From: Doug Date: Sun, 5 Oct 2025 15:58:21 -0700 Subject: [PATCH 07/20] edits --- learn/reif_examples.dj | 59 ++++++++++++++++++++---------------------- 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/learn/reif_examples.dj b/learn/reif_examples.dj index 86800530d..d0f160e80 100644 --- a/learn/reif_examples.dj +++ b/learn/reif_examples.dj @@ -23,7 +23,7 @@ Here are predicates exported by reif: In prolog it means representing the truth of some expression or relation as a boolean value. Unlike many languages, the truthyness of a term like 'A < 10' can not be directly bound to a variable with an assignment. This is because terms have a different meaning in prolog that you should already be familiar with. -1111 + ``` prolog ?- A is 112, C= (A<10). A = 112, C = (112<10). @@ -57,14 +57,15 @@ foo X = true. ?- ``` -oddly, there is no build in < notation in prolog for boolean values. +Oddly, there is no build in < operator in prolog for boolean values: + ``` prolog ?- false < true, true. error(type_error(evaluable,false/0),(is)/2). ?- ``` -Note that other Prolog modules may express a reified boolean value as 0 or 1 or choose some other representation instead of false and true. +Note that other Prolog modules (like clp(z)) may express a reified boolean value as 0 or 1 or choose some other representation instead of false and true. ### Naming Conventions in reif @@ -151,7 +152,7 @@ but this breaks. What is happening is that reif has provided (=)/3, and that is what is being used in tfilter (not the usual (=)/2). But (in my system at the time of writing) (#>)/2 exists, but there is no (#>)/3. -A workaround is to define a three-arity predicate to do the comparison (it could be (#>)/3, but I choose pound_gt_t): +A workaround is to define a three-arity predicate to do the comparison (it could be (#>)/3, but I choose pound\_gt\_t): ``` prolog pound_gt_t(X,Y,T) :- if_(Y #< X, T=true, T=false). @@ -190,40 +191,42 @@ Query holds if single item is excluded from a list. Exclusion or inclusion is d true. ``` -Query holds, and X is true if and only if a single item is included in the list. Note single quotes are normally not needed around atoms like 'c'. +Query holds, and T is true if and only if a single item is included in the list. ``` prolog -?- memberd_t(c,"branch free",X). - X = true. +?- memberd_t(c,"branch free",T). + T = true. ?- ``` -Query holds for Y in a member of the list and X is false or Y is not a member of the list and X is true. +Query holds for Y in a member of the list and T is false or Y is not a member of the list and T is true. ``` prolog -?- memberd_t(Y,[1,2,3],X). - Y = 1, X = true -; Y = 2, X = true -; Y = 3, X = true -; X = false, dif:dif(1,Y), dif:dif(2,Y), dif:dif(3,Y). +?- memberd_t(Y,[1,2,3],T). + Y = 1, T = true +; Y = 2, T = true +; Y = 3, T = true +; T = false, dif:dif(1,Y), dif:dif(2,Y), dif:dif(3,Y). ?- + ``` Lists of length 2, where A is a member of exactly one of those lists. ``` prolog -length(L1,2),length(L2,2),memberd_t(A,L1,X), memberd_t(A,L2,Y), dif(X,Y). - L1 = [A,_C], L2 = [_A,_B], X = true, Y = false, dif:dif(_A,A), dif:dif(_B,A) -; L1 = [_A,A], L2 = [_B,_C], X = true, Y = false, dif:dif(_A,A), dif:dif(_B,A), dif:dif(_C,A) -; L1 = [_A,_B], L2 = [A,_C], X = false, Y = true, dif:dif(_A,A), dif:dif(_B,A) -; L1 = [_A,_B], L2 = [_C,A], X = false, Y = true, dif:dif(_A,A), dif:dif(_B,A), dif:dif(_C,A) +?- length(L1,2),length(L2,2),memberd_t(A,L1,TX), memberd_t(A,L2,TY), dif(TX,TY). + L1 = [A,_C], L2 = [_A,_B], TX = true, TY = false, dif:dif(_A,A), dif:dif(_B,A) +; L1 = [_A,A], L2 = [_B,_C], TX = true, TY = false, dif:dif(_A,A), dif:dif(_B,A), dif:dif(_C,A) +; L1 = [_A,_B], L2 = [A,_C], TX = false, TY = true, dif:dif(_A,A), dif:dif(_B,A) +; L1 = [_A,_B], L2 = [_C,A], TX = false, TY = true, dif:dif(_A,A), dif:dif(_B,A), dif:dif(_C,A) ; false. -?- +?- ``` ## (=)(A,B,T). -reif introdces =/3, which holds if and only if the first two arguments are equal, and the third argument T is true or the first two arguments are not equal the the third argument is false. T is the 'reified' truth value of A. +reif introdces (=)/3, which holds if and only if the first two arguments are equal, and the third argument T is true or the first two arguments are not equal the the third argument is false. +T is the reified truth value of =(A,B). ``` prolog ?- (reif:(=)(1,2,false). @@ -345,14 +348,8 @@ It can be used to test for existance of an element for which the supplied predic false. ?- ``` - - - - ## tmember_t - - tmember_t can be used to test the existance or non-existance of an element of the list, for which a predicate holds. @@ -365,7 +362,7 @@ It can be used to test for existance of an element for which the supplied predic ``` -## tparttition/4 +## tpartition/4 Partition a list based on a predicate requiring two more arguments. @@ -395,14 +392,14 @@ Find Z, for which #<(Z) partions [1,2,4,5] into [4,5], and [1,2]: [if_/3](https://www.metalevel.at/prolog/metapredicates) "correctly commits to one of two alternatives when admissable …". - - The first argument is a predicate requiring one more argument, which is the reified truth value of a comparsion . -The prolog system provides some help here. The prolog system uses =/3 in this example, because =/3 wouldn't match. +The prolog system provides some help here. The prolog system uses (=)/3 in this example, because (=)/3 wouldn't match. ``` prolog -?- if_(X=8, Z=3, M=4). +?- if_(X=8, Z=3,M=4). + X = 8, Z = 3 +; M = 4, dif:dif(X,8). ``` or From f5db942d96df1c0a189e8cedd9e095217ac015dd Mon Sep 17 00:00:00 2001 From: Doug Date: Sun, 5 Oct 2025 16:08:49 -0700 Subject: [PATCH 08/20] edits --- learn/reif_examples.dj | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/learn/reif_examples.dj b/learn/reif_examples.dj index d0f160e80..0221af8bb 100644 --- a/learn/reif_examples.dj +++ b/learn/reif_examples.dj @@ -306,7 +306,7 @@ Oddly, scryer prolog 0.9.4-614 has ``` prolog (#>)3 - ```: + ``` ``` prolog ?- tfilter(#>(10),[1,20,20], Xs). @@ -364,14 +364,16 @@ It can be used to test for existance of an element for which the supplied predic ## tpartition/4 -Partition a list based on a predicate requiring two more arguments. +Partition a list based on a predicate requiring two more arguments: + + +``` prolog +:- meta_predicate(tpartition(2, ?, ?, ?)). +``` - ``` prolog - :- meta_predicate(tpartition(2, ?, ?, ?)). - ``` Partition a list based on larger than 4: - ``` prolog +``` prolog ?- tpartition(#<(4),[1,2,3,4,5,6,3,4,5],A,B). A = [5,6,5], B = [1,2,3,4,3,4]. ?- From c23bbf0a50f2bba4568d2298cf87081d80c3ea97 Mon Sep 17 00:00:00 2001 From: Doug Date: Sun, 5 Oct 2025 16:23:58 -0700 Subject: [PATCH 09/20] edits --- learn/reif_examples.dj | 1 + 1 file changed, 1 insertion(+) diff --git a/learn/reif_examples.dj b/learn/reif_examples.dj index 0221af8bb..5021cf1f6 100644 --- a/learn/reif_examples.dj +++ b/learn/reif_examples.dj @@ -316,6 +316,7 @@ Oddly, scryer prolog 0.9.4-614 has ``` Relating a list, to a list without a certain element: + ``` prolog ?- tfilter(dif(10),[1,2,3,X],Es). X = 10, Es = [1,2,3] From 42bc69278df2e6df2ae5a0239c050c278d3650e6 Mon Sep 17 00:00:00 2001 From: Doug Date: Sun, 5 Oct 2025 16:57:16 -0700 Subject: [PATCH 10/20] edits --- learn/reif_examples.dj | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/learn/reif_examples.dj b/learn/reif_examples.dj index 5021cf1f6..5b7b78afe 100644 --- a/learn/reif_examples.dj +++ b/learn/reif_examples.dj @@ -223,10 +223,12 @@ Lists of length 2, where A is a member of exactly one of those lists. ?- ``` -## (=)(A,B,T). +## (=)/3 -reif introdces (=)/3, which holds if and only if the first two arguments are equal, and the third argument T is true or the first two arguments are not equal the the third argument is false. -T is the reified truth value of =(A,B). +reif introdces (=)/3, which holds if and only if the first two arguments + X and Y are equal, and the third argument T is true or X and Y not equal and T is false. + +T is the reified truth value of =(X,Y). ``` prolog ?- (reif:(=)(1,2,false). @@ -243,9 +245,8 @@ You may find it useful to write predicates where the last argument is a boolean ## if_/3 -if_1 calls predicate If_1, adding on the last argument the predicate expects, which is a boolean value, T. -if T then calls the predicate Then_0 -otherwise, calls the predicate Else 0. +The first argument to if_/3 is a predicate expecting one argument, which is a boolean value T. Read the definition in reif.pl for the meaning of the +remaining arguments. ``` prolog ?- if_( (X=3),(Y=4),(Z=5)), (X=4;X=3). @@ -260,11 +261,10 @@ In the above example, only Y or Z are instantiated variables, but not both. Example with a differrent comparison predicate. ``` prolog -greater_than_two(Y,true) :- Y #> 2. -greater_than_two(Y,false) :- Y #=< 2. +greater_than_two(Y, true) :- Y #> 2. + ?- if_(greater_than_two(3),X="works", X="fails"). X = "works". -?- ``` ## tfilter/3 @@ -282,7 +282,6 @@ tfilter(C_2, [E|Es], Fs0) :- tfilter(C_2, Es, Fs). ``` -The meta_predicate directive is important. It specifies the first parameter to tfilter is a predicate needing two more arguments. The first required argument is the value to be compared from the list, and the second is the reified result of the comparison (true, or false). If your prolog system has @@ -397,7 +396,7 @@ Find Z, for which #<(Z) partions [1,2,4,5] into [4,5], and [1,2]: The first argument is a predicate requiring one more argument, which is the reified truth value of a comparsion . -The prolog system provides some help here. The prolog system uses (=)/3 in this example, because (=)/3 wouldn't match. +The prolog system provides some help here. The prolog system uses (=)/3 in this example for the comparison X=8. ``` prolog ?- if_(X=8, Z=3,M=4). From 2e1c1804dd5574d18061796527ec71663d70258e Mon Sep 17 00:00:00 2001 From: Doug Date: Sun, 5 Oct 2025 17:00:19 -0700 Subject: [PATCH 11/20] edits --- learn/reif_examples.dj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/learn/reif_examples.dj b/learn/reif_examples.dj index 5b7b78afe..79ef0896a 100644 --- a/learn/reif_examples.dj +++ b/learn/reif_examples.dj @@ -394,7 +394,7 @@ Find Z, for which #<(Z) partions [1,2,4,5] into [4,5], and [1,2]: [if_/3](https://www.metalevel.at/prolog/metapredicates) "correctly commits to one of two alternatives when admissable …". -The first argument is a predicate requiring one more argument, which is the reified truth value of a comparsion . +The first argument is a predicate requiring one more argument, which is the reified truth value of a comparison. The prolog system provides some help here. The prolog system uses (=)/3 in this example for the comparison X=8. From f127d7a91c4e86f6656067a7e00e6e67754584bf Mon Sep 17 00:00:00 2001 From: Doug Date: Sat, 15 Nov 2025 05:03:35 -0800 Subject: [PATCH 12/20] removed -> --- doclog.config.pl | 1 - learn/reif_examples.dj | 13 +------------ 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/doclog.config.pl b/doclog.config.pl index b1d73df6d..d8e1ff977 100644 --- a/doclog.config.pl +++ b/doclog.config.pl @@ -5,7 +5,6 @@ omit(["ops_and_meta_predicates.pl", "tabling"]). learn_pages_source_folder("learn"). learn_pages_categories(["First steps", "Tutorials"]). -reif_learn_pages_categories( ["First steps", "Tutorials"]). learn_pages([ page("Let's play Brisca", "Tutorials", "lets-play-brisca.dj"), page("Test page", "First steps", "test-page.dj"), diff --git a/learn/reif_examples.dj b/learn/reif_examples.dj index 79ef0896a..153cbc56c 100644 --- a/learn/reif_examples.dj +++ b/learn/reif_examples.dj @@ -28,19 +28,8 @@ that you should already be familiar with. ?- A is 112, C= (A<10). A = 112, C = (112<10). ?- -``` -To make C bound to a boolean value with the built in (->): - -``` prolog -?- A is 2, ( A#<10 -> C=true;C=false), C, format("~s",["C holds"]). -C holds A = 2, C = true. -?- A is 2000, ( A#<10 -> C=true;C=false), C, format("~s",["C holds"]). - false. -?- -``` - -if_ from reif provides a better way to do the same, and you should consider using if_ unless you ahve a compelling reason to use -> . + if_ from reif provides a consise way to bind a variable that is true when a relatinship holds. ``` prolog ?- A #= 2,if_(A#<103,C=true,C=false),C,format("~s",["C Holds"]). C Holds A = 2, C = true. From d3b9aa6bafd3d50cb313bb0164df85cdb612753e Mon Sep 17 00:00:00 2001 From: Doug Date: Sat, 15 Nov 2025 06:38:11 -0800 Subject: [PATCH 13/20] added motivation --- learn/reif_examples.dj | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/learn/reif_examples.dj b/learn/reif_examples.dj index 153cbc56c..7910c4554 100644 --- a/learn/reif_examples.dj +++ b/learn/reif_examples.dj @@ -438,5 +438,13 @@ Todo: Explain how ,/3 and ;/3 are to be used, and how they are used internally # Motiviation for Using reif -Todo: add some examples compared to using list or builtin predicates. +The legacy built in predictes !/0, (->)/2, (\+), (\=) are not pure and +may lead to incomplete (buggy) programs. This is well covered in [Prolog Antipatterns: How not to do it]( +https://www.youtube.com/watch?v=6olf24BDtJc). Unfortunately, these legacy predicates are used in many prolog tutorials. + +The reason for most programmers learning or using Prolog is to enjoy the declarative nature of prolog. +The predicates in modules reif, dif, and clpz provide a better experience than the legacy predicates !/0, (->)/2, (\+), (\=). + +You should still be aware of the predicates in builtins, since you will need some of them, and may see all of them in existing prolog code. For example, +you will see (->) and (\+) in clpz, so you will need to understand (->) and (\+) to understand the implemention of clpz. From 6a73e115164d6ecf268df2b602fe2a9513231aee Mon Sep 17 00:00:00 2001 From: Doug Date: Sat, 15 Nov 2025 06:38:36 -0800 Subject: [PATCH 14/20] typo --- learn/reif_examples.dj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/learn/reif_examples.dj b/learn/reif_examples.dj index 7910c4554..d64cea9f1 100644 --- a/learn/reif_examples.dj +++ b/learn/reif_examples.dj @@ -434,7 +434,7 @@ A is not B A = wild, B = one. ``` ## ,/3 and ;/3 -Todo: Explain how ,/3 and ;/3 are to be used, and how they are used internally withing reif. +Todo: Explain how ,/3 and ;/3 are to be used, and how they are used internally within reif. # Motiviation for Using reif From f3556f709372f2f4e39ce46be4b11cb13890b22b Mon Sep 17 00:00:00 2001 From: Doug Date: Sat, 15 Nov 2025 12:02:08 -0800 Subject: [PATCH 15/20] Add triska's fizzbuzz from https://github.com/mthom/scryer-prolog/pull/3100#issuecomment-3466198617 --- learn/reif_examples.dj | 65 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) diff --git a/learn/reif_examples.dj b/learn/reif_examples.dj index d64cea9f1..c604f46fd 100644 --- a/learn/reif_examples.dj +++ b/learn/reif_examples.dj @@ -25,10 +25,11 @@ truthyness of a term like 'A < 10' can not be directly bound to a variable with that you should already be familiar with. ``` prolog -?- A is 112, C= (A<10). +?- A=112, C=(A<10). A = 112, C = (112<10). ?- +``` if_ from reif provides a consise way to bind a variable that is true when a relatinship holds. ``` prolog ?- A #= 2,if_(A#<103,C=true,C=false),C,format("~s",["C Holds"]). @@ -436,6 +437,68 @@ A is not B A = wild, B = one. Todo: Explain how ,/3 and ;/3 are to be used, and how they are used internally within reif. +# Sample Problems + +## FizzBuzz + +Copied from somewhere online: +Given an integer n, for every positive integer i <= n, the task is to print, + + "FizzBuzz" if i is divisible by 3 and 5, + "Fizz" if i is divisible by 3, + "Buzz" if i is divisible by 5 + "i" as a string, if none of the conditions are true. + +### FizzBuzz with if_ ladder. + +```prolog +i_output(I, Os) :- + if_((I mod 3 #= 0, I mod 5 #= 0), + Os = "FizzBuzz", + if_(I mod 3 #= 0, + Os = "Fizz", + if_(I mod 5 #= 0, + Os = "Buzz", + number_chars(I, Os)))). +``` +Yielding for example: + +``` +?- N = 15, between(1, N, I), i_output(I, Os). + N = 15, I = 1, Os = "1" +; N = 15, I = 2, Os = "2" +; N = 15, I = 3, Os = "Fizz" +; N = 15, I = 4, Os = "4" +; N = 15, I = 5, Os = "Buzz" +; N = 15, I = 6, Os = "Fizz" +; N = 15, I = 7, Os = "7" +; N = 15, I = 8, Os = "8" +; N = 15, I = 9, Os = "Fizz" +; N = 15, I = 10, Os = "Buzz" +; N = 15, I = 11, Os = "11" +; N = 15, I = 12, Os = "Fizz" +; N = 15, I = 13, Os = "13" +; N = 15, I = 14, Os = "14" +; N = 15, I = 15, Os = "FizzBuzz". +``` +We can test it also in other ways, for instance: Which integers yield "Fizz"? + +```prolog +?- length(_, I), catch(i_output(I, "Fizz"), error(syntax_error(_),_), false). + I = 3 +; I = 6 +; I = 9 +; I = 12 +; I = 18 +; I = 21 +; I = 24 +; I = 27 +; I = 33 +; I = 36 +; ... . +``` + + # Motiviation for Using reif The legacy built in predictes !/0, (->)/2, (\+), (\=) are not pure and From ed6d2810fa9b3604e28cc241cf39c966c0cb4793 Mon Sep 17 00:00:00 2001 From: Doug Ransom Date: Wed, 19 Nov 2025 09:26:38 -0800 Subject: [PATCH 16/20] Apply suggestions from code review Co-authored-by: Dan Rose --- learn/reif_examples.dj | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/learn/reif_examples.dj b/learn/reif_examples.dj index c604f46fd..94dc7c468 100644 --- a/learn/reif_examples.dj +++ b/learn/reif_examples.dj @@ -30,7 +30,7 @@ that you should already be familiar with. ?- ``` - if_ from reif provides a consise way to bind a variable that is true when a relatinship holds. + `if_` from reif provides a concise way to bind a variable that is true when a relationship holds. ``` prolog ?- A #= 2,if_(A#<103,C=true,C=false),C,format("~s",["C Holds"]). C Holds A = 2, C = true. @@ -71,13 +71,13 @@ We will use tmember_t to demonstrate an example. T is bound to true when a con ``` The prefix of t_ (tmember/2,tmember_t/3, tfilter, tpartition/4) in a predicate name indicates the first -argument is a predicate that takes as as an argument a predicate (or partially applied predicate) taking additonal arguments, the last of which will +argument is a predicate that takes as as an argument a (partial) goal taking additional arguments, the last of which will be a reified boolean value for whether the query holds. The naming convention for the parameters which are predicates is C_N, where C may convey some meaning about the arguments purpose, and the N indicates how many additional arguments are required by the predicate. -A suffix of _t indicates the last parameter of a predicate is a reified boolean value. The last argument of the following are also reifed boolean values: +A suffix of _t indicates the last parameter of a predicate is a reified boolean value. The last argument of the following are also reified boolean values: (=)/3, (,)/3, (;)/3, dif/3. @@ -92,7 +92,7 @@ tfilter(_, [], []). tfilter(C_2, [E|Es], Fs0) :- … ``` -C_2 can be a partial goal requring two more arguments, or a 2-ary predicate. In either case the parameter of C_2 is the reified +C_2 can be a partial goal requiring two more arguments, or a 2-ary predicate. In either case the parameter of C_2 is the reified truth value. For example, using dif/3 from reif, and applying one argument 3, gives a partially applied goal X_2 requiring two more arguments. @@ -118,7 +118,7 @@ There is some code that you may find confusing, which shows a predicate expectin ``` prolog if_(call(C_2, E), Fs0 = [E|Fs], Fs0 = Fs), ``` -Fortunatley, you don't need to understand that mechanism to use the reif module. The C_2 parameter name gives you enough information about the predicate +Fortunately, you don't need to understand that mechanism to use the reif module. The C_2 parameter name gives you enough information about the partial goal you must supply as an argument. ### Surprises @@ -326,8 +326,8 @@ See which predicates produce a non-empty list with tfilter. ## tmember/2 -Just like tfilter, tmember's first argument is a paritally applied 2-arity predicate. -It can be used to test for existance of an element for which the supplied predicate holds. +Just like tfilter, tmember's first argument is a partially applied 2-arity predicate. +It can be used to test for existence of an element for which the supplied predicate holds. ``` prolog ?- tmember(=(3),[1,2,3,4,6]). @@ -340,7 +340,7 @@ It can be used to test for existance of an element for which the supplied predic ``` ## tmember_t - tmember_t can be used to test the existance or non-existance of an element of the list, + tmember_t can be used to test the existence or non-existence of an element of the list, for which a predicate holds. ``` prolog @@ -361,7 +361,7 @@ Partition a list based on a predicate requiring two more arguments: :- meta_predicate(tpartition(2, ?, ?, ?)). ``` - Partition a list based on larger than 4: +Partition a list into two lists based on a condition. The first list contains the elements 'X' such that `4 #< X` and the second list are the elements that don't satisfy that condition. ``` prolog ?- tpartition(#<(4),[1,2,3,4,5,6,3,4,5],A,B). @@ -369,7 +369,7 @@ Partition a list based on a predicate requiring two more arguments: ?- ``` -Find Z, for which #<(Z) partions [1,2,4,5] into [4,5], and [1,2]: +Find Z, for which #<(Z) partitions [1,2,4,5] into [4,5], and [1,2]: ``` prolog ?- tpartition(#<(Z),[1,2,4,5],[4,5],[1,2]), indomain(Z). @@ -404,7 +404,7 @@ or ``` -Attemtping to use a 0-ary predicate fails: +Attempting to use a 0-ary predicate fails: ``` prolog ?- Y=true, if_(Y, Z=3, M=4). @@ -435,7 +435,9 @@ A is not B A = wild, B = one. ``` ## ,/3 and ;/3 -Todo: Explain how ,/3 and ;/3 are to be used, and how they are used internally within reif. +`,/3` is a reified *and* operator. If `G_1` and `H_1` are reified predicates, then `call((G_1,H_1),true)` succeeds if both `call(G_1,true)`, `call(H_1,true)` succeed. `((G_1,H_1),false)` succeeds if either `call(G_1,false)` or `call(H_1,false)` (or both) succeed. + +Similarly, `;/3` is a reified *or* operator. `(G_1,H_1,true)` succeeds if either `call(G_1,true)` succeeds *or* `call(H_1,true)` succeeds (or both). `(G_1,H_1,false)` succeeds if `call(G_1,false)` and `call(H_1,false)` both succeed. # Sample Problems @@ -499,9 +501,9 @@ We can test it also in other ways, for instance: Which integers yield "Fizz"? ``` -# Motiviation for Using reif +# Motivation for using reif -The legacy built in predictes !/0, (->)/2, (\+), (\=) are not pure and +The built in predicates !/0, (->)/2, (\+), (\=) are not pure and may lead to incomplete (buggy) programs. This is well covered in [Prolog Antipatterns: How not to do it]( https://www.youtube.com/watch?v=6olf24BDtJc). Unfortunately, these legacy predicates are used in many prolog tutorials. From d6027f38e18e2227de609ac7e271ee0edf2ee2a5 Mon Sep 17 00:00:00 2001 From: Doug Ransom Date: Wed, 19 Nov 2025 09:35:54 -0800 Subject: [PATCH 17/20] feedback from code review --- learn/reif_examples.dj | 9 +++++---- learn/reif_examples/play.pl | 11 +++++++++++ 2 files changed, 16 insertions(+), 4 deletions(-) create mode 100644 learn/reif_examples/play.pl diff --git a/learn/reif_examples.dj b/learn/reif_examples.dj index c604f46fd..5e7afb1ee 100644 --- a/learn/reif_examples.dj +++ b/learn/reif_examples.dj @@ -47,7 +47,7 @@ foo X = true. ?- ``` -Oddly, there is no build in < operator in prolog for boolean values: +There is no build in < operator in prolog for boolean values. true and false are just atoms. ``` prolog ?- false < true, true. @@ -55,6 +55,7 @@ Oddly, there is no build in < operator in prolog for boolean values: ?- ``` + Note that other Prolog modules (like clp(z)) may express a reified boolean value as 0 or 1 or choose some other representation instead of false and true. @@ -145,14 +146,14 @@ But (in my system at the time of writing) (#>)/2 exists, but there is no (#>)/3. A workaround is to define a three-arity predicate to do the comparison (it could be (#>)/3, but I choose pound\_gt\_t): ``` prolog -pound_gt_t(X,Y,T) :- if_(Y #< X, T=true, T=false). +gt_t(X,Y,T) :- if_(Y #< X, T=true, T=false). -?- tfilter(pound_gt_t(3),[1,3],T). +?- tfilter(gt_t(3),[1,3],T). T = [1]. ?- ``` -If you are writing predicates to use with reif, look at the implementation (=)/3 in reif. +If you are writing predicates to use with reif, look at the implementation (=)/3 in reif as an example. You might wonder if you can write a predicate to relate a predicate with a reified value as the last parameter. diff --git a/learn/reif_examples/play.pl b/learn/reif_examples/play.pl new file mode 100644 index 000000000..9a065d2f8 --- /dev/null +++ b/learn/reif_examples/play.pl @@ -0,0 +1,11 @@ +% greater_than_two(Y,true) :- Y #> 2. +% greater_than_two(Y,false) :- Y #=< 2. + +greater_than_two(Y, true) :- Y #> 2. + +pound_gt_t(X,Y,T) :- if_(Y #< X, T=true, T=false). + +weird:- if_(X #< 3, Y=X , Y=7), tmember(X, [1,9,3,-1, 0]). + + + \ No newline at end of file From f7370283982835ea71cf09c66ce4dddb9c8b4e0a Mon Sep 17 00:00:00 2001 From: Doug Ransom Date: Wed, 19 Nov 2025 11:05:08 -0800 Subject: [PATCH 18/20] tweaked some language, added a missing module load in fizzbuss. --- learn/reif_examples.dj | 47 +++++++++++++++++++++++-------------- learn/reif_examples/play.pl | 11 --------- 2 files changed, 30 insertions(+), 28 deletions(-) delete mode 100644 learn/reif_examples/play.pl diff --git a/learn/reif_examples.dj b/learn/reif_examples.dj index 789eeee20..e77e6bdcb 100644 --- a/learn/reif_examples.dj +++ b/learn/reif_examples.dj @@ -20,15 +20,15 @@ Here are predicates exported by reif: ### What is a reified boolean value? -In prolog it means representing the truth of some expression or relation as a boolean value. Unlike many languages, the -truthyness of a term like 'A < 10' can not be directly bound to a variable with an assignment. This is because terms have a different meaning in prolog -that you should already be familiar with. - +In many languages, A < 10 is an expression that evaluates to a Boolean value". In Prolog, it is merely a compound term. ``` prolog ?- A=112, C=(A<10). A = 112, C = (112<10). ?- +A reified boolean value is a value that contains true or false depending on whether some relationship holds. Binding a value to true or false +when a relationship holds is reifying the relationship as a boolean value. + ``` `if_` from reif provides a concise way to bind a variable that is true when a relationship holds. ``` prolog @@ -93,7 +93,7 @@ tfilter(_, [], []). tfilter(C_2, [E|Es], Fs0) :- … ``` -C_2 can be a partial goal requiring two more arguments, or a 2-ary predicate. In either case the parameter of C_2 is the reified +C_2 can be a partial goal requiring two more arguments, or a 2-ary predicate. In either case the last parameter of C_2 is the reified truth value. For example, using dif/3 from reif, and applying one argument 3, gives a partially applied goal X_2 requiring two more arguments. @@ -111,16 +111,20 @@ or more succinctly: ?- ``` -You will probably need to inspect reif.pl to fully understand how to use the module . By studying the implementation of predicates, you may also learn -some new prolog skills +It is useful inspect reif.pl to understand how to use the module. By studying the implementation of predicates, you may also learn +some new prolog skills. -There is some code that you may find confusing, which shows a predicate expecting 2 arguments, but only one is provided: +There is some code in reif, which shows a predicate expecting 2 arguments, but only one is provided: -``` prolog - if_(call(C_2, E), Fs0 = [E|Fs], Fs0 = Fs), +``` +tfilter(_, [], []). +tfilter(C_2, [E|Es], Fs0) :- + ... ``` -Fortunately, you don't need to understand that mechanism to use the reif module. The C_2 parameter name gives you enough information about the partial goal -you must supply as an argument. +The C_2 parameter name gives you enough information about the partial goal +you must supply as an argument. The _2 in the parameter name indicates C_2 requires two additional arguments. + + ### Surprises @@ -130,6 +134,9 @@ You may be surprised when something like this works: ?- call( tfilter,=(3),[3],X). X = [3]. ?- +?- tfilter(#<(3),[3],X). + X = []. + ``` but this breaks. @@ -141,9 +148,10 @@ but this breaks. ``` What is happening is that reif has provided (=)/3, and that is what is being used in tfilter (not the usual (=)/2). +clps provides (#<)/3, but it doesn't provide (#>)/3. -But (in my system at the time of writing) (#>)/2 exists, but there is no (#>)/3. -A workaround is to define a three-arity predicate to do the comparison (it could be (#>)/3, but I choose pound\_gt\_t): + +A workaround is to define a three-arity predicate to do the comparison (it could be (#>)/3, but I choose gt\_t): ``` prolog gt_t(X,Y,T) :- if_(Y #< X, T=true, T=false). @@ -155,7 +163,9 @@ gt_t(X,Y,T) :- if_(Y #< X, T=true, T=false). If you are writing predicates to use with reif, look at the implementation (=)/3 in reif as an example. -You might wonder if you can write a predicate to relate a predicate with a reified value as the last parameter. +You might wonder if you can write a predicate to relate a predicate with a reified value as the last parameter. + + ``` prolog unreif_reif(P1, P2). @@ -163,7 +173,8 @@ unreif_reif(P1, P2). so that P1(A1, A2, ...AN) holds when P2(A1, A2, ..., AN, T) holds and T is the reified value of P1(A1...AN). -[Probably not](https://github.com/mthom/scryer-prolog/discussions/2557), but you can probably learn something about writing reified versions of predicates by reading that discussion. +It seems like a good idea, but [probably not](https://github.com/mthom/scryer-prolog/discussions/2557). + You can look at predicates like dif/3 or (=)/3 in [reif](/reif.html) to get ideas about writing predicates that include a reifed boolean value. @@ -286,7 +297,8 @@ If your prolog system has Xs = [20]. ``` -Oddly, scryer prolog 0.9.4-614 has +clpz in scryer v0.10.0-17 + has ``` prolog (#<)/3 @@ -467,6 +479,7 @@ i_output(I, Os) :- Yielding for example: ``` +?- use_module(library(between)). ?- N = 15, between(1, N, I), i_output(I, Os). N = 15, I = 1, Os = "1" ; N = 15, I = 2, Os = "2" diff --git a/learn/reif_examples/play.pl b/learn/reif_examples/play.pl deleted file mode 100644 index 9a065d2f8..000000000 --- a/learn/reif_examples/play.pl +++ /dev/null @@ -1,11 +0,0 @@ -% greater_than_two(Y,true) :- Y #> 2. -% greater_than_two(Y,false) :- Y #=< 2. - -greater_than_two(Y, true) :- Y #> 2. - -pound_gt_t(X,Y,T) :- if_(Y #< X, T=true, T=false). - -weird:- if_(X #< 3, Y=X , Y=7), tmember(X, [1,9,3,-1, 0]). - - - \ No newline at end of file From 01c27aa871d8440ea020a96dce1486a4ae983897 Mon Sep 17 00:00:00 2001 From: Doug Ransom Date: Wed, 19 Nov 2025 11:19:39 -0800 Subject: [PATCH 19/20] called partial goals partial goals. --- learn/reif_examples.dj | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/learn/reif_examples.dj b/learn/reif_examples.dj index e77e6bdcb..72fe3a8de 100644 --- a/learn/reif_examples.dj +++ b/learn/reif_examples.dj @@ -75,8 +75,7 @@ The prefix of t_ (tmember/2,tmember_t/3, tfilter, tpartition/4) in a predicate argument is a predicate that takes as as an argument a (partial) goal taking additional arguments, the last of which will be a reified boolean value for whether the query holds. -The naming convention for the parameters which are predicates is C_N, where C may convey some meaning about the arguments purpose, and the -N indicates how many additional arguments are required by the predicate. +The naming convention for the parameters which are partial goals is C_N, which will be called with N more arguments. A suffix of _t indicates the last parameter of a predicate is a reified boolean value. The last argument of the following are also reified boolean values: (=)/3, (,)/3, (;)/3, dif/3. @@ -84,7 +83,7 @@ A suffix of _t indicates the last parameter of a predicate is a reified boolean You may wish to follow that convention when writing your own predicates. -It is useful to inspect the source of reif so you can see the information conveyed in the predicate and predicate parameter names. +It is useful to inspect the source of reif so you can see the information conveyed in the predicate and parameter names. ``` prolog :- meta_predicate(tfilter(2, ?, ?)). @@ -93,8 +92,6 @@ tfilter(_, [], []). tfilter(C_2, [E|Es], Fs0) :- … ``` -C_2 can be a partial goal requiring two more arguments, or a 2-ary predicate. In either case the last parameter of C_2 is the reified -truth value. For example, using dif/3 from reif, and applying one argument 3, gives a partially applied goal X_2 requiring two more arguments. @@ -111,18 +108,6 @@ or more succinctly: ?- ``` -It is useful inspect reif.pl to understand how to use the module. By studying the implementation of predicates, you may also learn -some new prolog skills. - -There is some code in reif, which shows a predicate expecting 2 arguments, but only one is provided: - -``` -tfilter(_, [], []). -tfilter(C_2, [E|Es], Fs0) :- - ... -``` -The C_2 parameter name gives you enough information about the partial goal -you must supply as an argument. The _2 in the parameter name indicates C_2 requires two additional arguments. From ecbc0b8893082b86c660241304b390f43c3aaafe Mon Sep 17 00:00:00 2001 From: Doug Ransom Date: Wed, 19 Nov 2025 11:30:29 -0800 Subject: [PATCH 20/20] Tweak of examples around partial goals and #> #< --- learn/reif_examples.dj | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/learn/reif_examples.dj b/learn/reif_examples.dj index 72fe3a8de..eb656e623 100644 --- a/learn/reif_examples.dj +++ b/learn/reif_examples.dj @@ -116,19 +116,22 @@ or more succinctly: You may be surprised when something like this works: ``` prolog -?- call( tfilter,=(3),[3],X). +?- tfilter(=(3),[3],X). X = [3]. ?- + ?- tfilter(#<(3),[3],X). X = []. ``` -but this breaks. +but this breaks: -``` prolog -?- call( tfilter,#>(3),[3],X). - error(existence_error(procedure,(#>)/3),(#>)/3). + +``` + +?- ?- tfilter(#>(3),[3],X). + error(syntax_error(unexpected_char),read_term/3:0). ?- ```