-
Notifications
You must be signed in to change notification settings - Fork 157
Reif cookbook #3100
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Reif cookbook #3100
Changes from 7 commits
6b13523
a298614
a1897a5
0dedc6f
7195310
ec7eb43
9939763
a479373
6b550ba
2bf4f5e
0d5dd00
f5db942
c23bbf0
42bc692
2e1c180
f127d7a
d3b9aa6
6a73e11
f3556f7
ed6d281
d6027f3
25ad120
f737028
01c27aa
ecbc0b8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,378 @@ | ||
| # 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. | ||
|
|
||
| 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 | ||
|
|
||
| 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. | ||
dougransom marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| 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). | ||
|
|
||
|
|
||
| ``` | ||
| :- 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: | ||
|
|
||
| ``` | ||
| ?- 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. The C_2 parameter name gives you enough information about the predicate | ||
dougransom marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| you must supply as an argument. | ||
|
|
||
| You may be surprised when something like this works: | ||
|
|
||
| ``` | ||
| ?- call( tfilter,=(3),[3],X). | ||
|
||
| X = [3]. | ||
| ?- | ||
| ``` | ||
|
|
||
| but this breaks. | ||
|
|
||
| ``` | ||
| ?- 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). | ||
dougransom marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| 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): | ||
|
|
||
dougransom marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| ``` | ||
| pound_gt(X,Y,true):- X #>Y,!. | ||
dougransom marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| 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. | ||
|
|
||
| ``` | ||
| ?- memberd_t('c',"branch free",true). | ||
dougransom marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| 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). | ||
| 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'. | ||
|
|
||
| ``` | ||
| ?- 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. | ||
|
|
||
| ``` | ||
| ?- 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. | ||
|
|
||
| ``` | ||
| length(L1,2),length(L2,2),memberd_t(A,L1,X), memberd_t(A,L2,Y), dif(X,Y). | ||
dougransom marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| 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. | ||
| ?- | ||
| ``` | ||
dougransom marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| ## (=)(A,B,T). | ||
dougransom marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| 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). | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's a typo here. The parens don't match, and fixing that still gives me an incomplete reduction error. Maybe instead you mean: reif:'='(2,2,false)
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is no need to quote the functor name Single quotes are necessary for example if an atom contains whitespace characters.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's the best way to spell this? This does not work:
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Of your 3 examples, 2 and 3 are equivalent. You need spaces between the ?- A = reif: =(a,b,c), write_canonical(A), nl.
:(reif,=(a,b,c))
A = reif: =(a,b,c).
?- A = reif:(=(a,b,c)), write_canonical(A), nl.
:(reif,=(a,b,c))
A = reif: =(a,b,c).If you define ?- op(500,xfx,:=).
true.
?- A = reif:=(a,b,c), write_canonical(A), nl.
:=(reif,','(a,','(b,c)))
A = reif:=(a,b,c).
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, I don't think there's any need for module qualification here. |
||
| 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. | ||
|
||
|
|
||
| ``` | ||
| ?- 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. | ||
|
|
||
| ``` | ||
| 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". | ||
| ?- | ||
| ``` | ||
|
|
||
| ## 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). | ||
|
|
||
| ``` | ||
| :- 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). | ||
dougransom marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| If your prolog system has | ||
|
|
||
| ``` | ||
| #</3 | ||
| ``` | ||
|
|
||
|
|
||
| ``` | ||
| ?- tfilter(#<(10),[1,10,20],Xs). | ||
| Xs = [20]. | ||
| ``` | ||
|
|
||
| Oddly, scryer prolog 0.9.4-614 has | ||
|
|
||
| ``` | ||
| #</3 | ||
| ``` | ||
|
|
||
| but not | ||
|
|
||
| ``` | ||
| #>3 | ||
| ```: | ||
|
|
||
| ``` | ||
| ?- tfilter(#>(10),[1,20,20], Xs). | ||
| error(existence_error(procedure,(#>)/3),(#>)/3). | ||
| ?- | ||
|
|
||
| ``` | ||
|
|
||
| Relating a list, to a list without a certain element: | ||
| ``` | ||
| ?- 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. | ||
|
|
||
| ``` | ||
| ?- (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, 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. | ||
|
|
||
| ``` | ||
| ?- 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(#<(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 predicate requiring two more arguments. | ||
|
|
||
| ``` | ||
| :- meta_predicate(tpartition(2, ?, ?, ?)). | ||
| ``` | ||
| Partition a list based on larger than 4: | ||
dougransom marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| ``` | ||
| ?- 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]: | ||
|
||
|
|
||
| ``` | ||
| ?- 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. | ||
|
|
||
| ``` | ||
| ?- if_(X=8, Z=3, M=4). | ||
| ``` | ||
|
|
||
| or | ||
|
|
||
| ``` | ||
| ?- 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: | ||
dougransom marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| ``` | ||
| ?- Y=true, if_(Y, Z=3, M=4). | ||
| error(existence_error(procedure,true/1),true/1). | ||
| ?- | ||
| ``` | ||
| Which you can easily work around with: | ||
|
|
||
| ``` | ||
| ?- 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. | ||
|
|
||
| ``` | ||
| ?- cond_t(A=B,format("A is B",[]), true),A=west,B=west. | ||
| A is B A = west, B = west | ||
| ; false. | ||
| ``` | ||
|
|
||
| ``` | ||
| ?- 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. | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.