Skip to content
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion doclog.config.pl
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@
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")
page("Test page", "First steps", "test-page.dj"),
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").

378 changes: 378 additions & 0 deletions learn/reif_examples.dj
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.

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
you must supply as an argument.

You may be surprised when something like this works:

```
?- call( tfilter,=(3),[3],X).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The call is extraneous. More clear to write tfilter(=(3),[3],X)?

X = [3].
?-
```

but this breaks.

```
?- call( tfilter,#>(3),[3],X).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly here:

?- 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):

```
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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this statement (and the next few lines) are more confusing than helpful. If you feel like this needs to be considered, you might instead say something like "You may have trouble reifying a predicate that may error, instead of simply succeeding or failing."

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So can we write a predicate to relate a predicate to a reified value as last parameter if the predicate can't error?


```
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).
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).
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).
Copy link
Contributor

Choose a reason for hiding this comment

The 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)

Copy link
Contributor

Choose a reason for hiding this comment

The 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.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the best way to spell this?

This does not work: reif:=(1,2,false)
This works (but I don't know if it should): reif: =(1,2,false)
This works: reif:(=(1,2,false)).

Copy link
Contributor

Choose a reason for hiding this comment

The 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 : and the = for them to parse as different names instead of as :=:

?- 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 := as an infix operator 1 also works, but in a completely different way from what you would probably expect:

?- op(500,xfx,:=).
   true.
?- A = reif:=(a,b,c), write_canonical(A), nl.
:=(reif,','(a,','(b,c)))
   A = reif:=(a,b,c).

Copy link
Contributor

Choose a reason for hiding this comment

The 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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not an expert, but IMO the point of if_/3 is "indexing" it doesn't do any magic you can do everything it does while using dif/2, but it cleverly reduces amount of choice points – which is a real deal.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am less of an expert. I understand these predicates are also likely to be more monotonic or pure and therefore harder to write incorrect programs as well as some efficiency considerations. You want to add some language?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW I don’t think “indexing” is explained adequately either.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My goal at this point is to provide enough examples that make it easier to get going using reif for the basics.

I might (or any other contributor might) add more examples with an explanation of benefits such as efficiency or monotonicity.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My goal at this point is to provide enough examples that make it easier to get going using reif for the basics.

Totally. That's why I don't think it helps to explain in terms of jargon like "indexing". I think you're right that if_ should be understand in terms of what it gets YOU, the programmer, in terms of correctness.


```
?- 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).

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:

```
?- 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]:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a cool example. I like this!


```
?- 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:

```
?- 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.