/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   Pure, monotonic list comprehension (Scryer CLPZ).

   Pattern:
     1) Enumerate an explicit finite domain list.
     2) Use a reified Test(E,T) with T in {true,false}.
     3) Filter via if_/3 without labeling or cuts.

   Example queries:

   ?- list_comp([1,2,3,4,5,6], gt3_t, Es).
   %@ Es = [4,5,6].

   ?- domain_1_to(9, Ds), list_comp(Ds, between_3_6_t, Es).
   %@ Es = [3,4,5,6].

      ^ ^
    =(o.o)=
      m m
   ~byakuren
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

:- use_module(library(clpz)).

:- op(760, yfx, #<==>).
:- op(740, yfx, #\/).
:- op(720, yfx, #/\).
:- op(700, xfx, #>).
:- op(700, xfx, #<).
:- op(700, xfx, #>=).
:- op(700, xfx, #=<).
:- op(700, xfx, #=).
:- op(700, xfx, #\=).
:- op(150, fx, #).

list_comp([], _, []).
list_comp([E|Es], Test_2, List) :-
        if_(call(Test_2, E), List = [E|Rest], List = Rest),
        list_comp(Es, Test_2, Rest).

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, _))
        ).

zo_t(0, false).
zo_t(1, true).

% Reified tests used in the examples.

gt3_t(E, T) :-
        #B #<==> (#E #> 3),
        zo_t(B, T).

between_3_6_t(E, T) :-
        #B1 #<==> (#E #>= 3),
        #B2 #<==> (#E #=< 6),
        #B #<==> (#B1 #/\ #B2),
        zo_t(B, T).

% Pure domain builder for an explicit list.

domain_1_to(N, Ds) :-
        #N #>= 0,
        domain_1_to_(1, N, Ds).

domain_1_to_(I, N, []) :-
        #I #> #N.

domain_1_to_(I, N, [I|Is]) :-
        #I #=< #N,
        #I1 #= #I + 1,
        domain_1_to_(I1, N, Is).
