Exercise 4.78. Redesign the query language as a nondeterministic program to be implemented using the evaluator of section 4.3, rather than as a stream process. In this approach, each query will produce a single answer (rather than the stream of all answers) and the user can type try-again to see more answers. You should find that much of the mechanism we built in this section is subsumed by nondeterministic search and backtracking. You will probably also find, however, that your new query language has subtle differences in behavior from the one implemented here. Can you find examples that illustrate this difference? ———————————————————————————————————————————————————————————————————————— We will use `amb` to select frames from those available. In the case of the query: (job ?person ?position) The stream-based implementation will first query the database, and return a stream of frames which match this pattern, which the REPL will then display to the user. In the nondeterministic implementation, we use `amb` to select a single assertion from the database, and `require` it to pattern match against the query in the context of the existing frame. Each time we extend the frame with a new binding, we do so ambiguously, so that other bindings can also be tried. Rather than passing streams of frames, there is only one frame, but at each point where it is extended we create a branch point. Rather than returning a `failed` symbol when frames cannot be extended, we would use `require` to kill entire branches of the computation. At a glance, it seems this approach would take the same choices in the same order that a frame-stream implementation would take, and no obvious subtle differences are coming to mind...