Hello,
It took me a long time to realize / internalize the following trivial insight.
That choice points are an inherent part of the runtime data environment of predicates, including after predicate completed their processing. The trail of choice points left after completing a call is something the programmer needs to be very aware of.
Its clearly the case, yet, I kept struggling with cuts – where to put them – in particular when creating a multi-level architecture and when trying to “simulate” deterministic behaviors – clearly misunderstanding the utility provided.
In conventional programming languages every function call creates a call stack, while at the return site of functions the call stack, and all state data on it, is removed.
In Prolog, however, every return from a predicate, keeps choice point generated alive – providing the caller with an option to obtain additional solutions through backtracking.
So, when coding a predicate there are a number of options:
- the predicate represents an inherent deterministic mapping – a function.
For example, one argument is always a ground input argument, while another is an output variable, with a functional relationship between them.
Often calculations with the is operator are such predicates. Or predicates that interact with an outside system (e.g. sending a message).
- the predicate can generate choice points, but the caller only needs one result.
For example, to test if an object is of a particular specified type is deterministic – either the object is or is not – although, an object could be of more than one type for problem domains where multiple classification makes sense.
- the predicate can generate choice points, and its possible that the caller may need additional results.
For example, a predicate that returns an object of a given type, or a predicate that returns one of the types of an object is inherently non-deterministic since many objects are of a type and (say) an object can have many times.
I guess, often, there is a gray zone and I can either offer two variants of the predicate – one deterministic with a cut in the body, and usually at least one parameter limited to ground; and one non-deterministic;. or simply let caller choose if they want only one result, e.g. via a once/1 wrapper.
Dan
p.s. In Python the yield keyword, as far as i intuit, removes the call stack, but explicitly returns a generator object that packages the functions current call stack and program pointer, so that the caller can invoke the function again and let it resume where it left off, to obtain a next generated result.
In a way this is like a local choice point explicitly generated through the yield keyword and generator object language mechanism.