Coroutining

Coroutining delays goals until variables become sufficiently instantiated.

30.1 dif/2 — sound disequality

?- dif(X, a), member(X, [a, b, c]).    % X = b ; X = c
?- dif(f(X, Y), f(1, 2)), X = 1.       % residual: dif(Y, 2)

30.2 freeze/2

?- freeze(X, (writeln(got(X)))), X = 5.     % prints got(5)
?- freeze(X, X > 3), X = 2.                 % false

% Lazy infinite lists
nat_stream(N, [N|T]) :- freeze(T, (N1 is N + 1, nat_stream(N1, T))).
?- nat_stream(0, S), S = [A, B, C|_].       % A = 0, B = 1, C = 2

30.3 when/2

Conditions: nonvar(X), ground(T), ?=(X, Y) (decidable equality), , and ; combinations.

?- when(ground(X-Y), Z is X + Y), X = 1, Y = 2.     % Z = 3
?- when((nonvar(A) ; nonvar(B)), writeln(woke)), B = x.

30.4 Attributed variables (building your own constraints)

put_attr/3, get_attr/3, del_attr/2, attr_unify_hook/2, attribute_goals//1, copy_term/3 (with residual goals). All CLP libraries are built on attributed variables.

:- module(domain, [domain/2]).
:- use_module(library(ordsets)).

domain(X, Dom) :- var(Dom), !, get_attr(X, domain, Dom).
domain(X, List) :-
    list_to_ord_set(List, Domain),
    put_attr(Y, domain, Domain),
    X = Y.

attr_unify_hook(Domain, Y) :-                % called when the variable is unified
    (   get_attr(Y, domain, Dom2)
    ->  ord_intersection(Domain, Dom2, NewDomain),
        (   NewDomain == [] -> fail
        ;   NewDomain = [Value] -> Y = Value
        ;   put_attr(Y, domain, NewDomain)
        )
    ;   var(Y) -> put_attr(Y, domain, Domain)
    ;   ord_memberchk(Y, Domain)
    ).

attribute_goals(X) --> { get_attr(X, domain, List) }, [domain(X, List)].

?- domain(X, [a, b]), domain(X, [b, c]).     % X = b