scryer-prolog icon indicating copy to clipboard operation
scryer-prolog copied to clipboard

How to use `run_query` in streaming mode/limiting number of matches?

Open lambdaofgod opened this issue 1 year ago • 3 comments

I've written basic bindings for Python in my project using lib_machine::Machine's run_query.

For queries that return small number of matches it works fine, but when I run a query that returns large or infinite numer of matches the program seems to enter infinite loop.

Is there a way to limit number of matches, or even better, is there a way to explicitly return a stream of matches? A stream of matches or something that allows for suspended computation seems perfect as it naturally fits returning a generator in Python.

Looking at code I've also seen a server, but I don't know how to run it - maybe this would be a better approach than calling Rust code, what do you think?

lambdaofgod avatar Apr 26 '24 17:04 lambdaofgod

I don't think Scryer currently has an interface for getting this (what in Rust you would call an iterator). It was postponed from the initial pull request because it wasn't high priority for a lot of use cases. Related: https://github.com/mthom/scryer-prolog/pull/1880#issuecomment-1664883911 https://github.com/mthom/scryer-prolog/pull/1880#issuecomment-1765078753.

One thing you can maybe do is to limit or stream the queries from inside Prolog, although this is not as performant or ergonomic as simply having a run_query() that returns an iterator.

:- use_module(library(iso_ext)).
:- use_module(library(lists)).
:- use_module(library(clpz)).

all_lists_infinite(L) :-
    length(L, _).

all_lists_limited_clpz(L, N) :-
    #Len #>= 0,
    #Len #=< #N,
    label([Len]),
    length(L, Len).

% Very low level (and ugly in my opinion), but more general because
% the predicate that you are limiting doesn't need to be parametrized.
all_lists_limited_bb(L, N) :-
    bb_put(all_lists_count, 0),
    length(L, _),
    bb_get(all_lists_count, Count),
    (   Count > N ->
        % A cut to stop looking for answers
        !, false
    ;   Count1 is Count + 1,
        bb_put(all_lists_count, Count1)
    ).

You could use similar techniques to skip the first N answers, so that you could make an iterator with this, although it would get much slower to get the next answer the further along you go because in the general case you would still need to compute all of the previous answers.

bakaq avatar Apr 26 '24 18:04 bakaq

@lucksus: This could be an interesting candidate for a generalization of the existing interface, and I would greatly appreciate if could take it into account! Thank you a lot!

triska avatar Apr 27 '24 07:04 triska

Using these bb_ primitives is the entry to non-reentrant code. Use call_nth/2 of library iso_ext in its stead.

UWN avatar Apr 30 '24 06:04 UWN