-
Notifications
You must be signed in to change notification settings - Fork 121
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Toplevel reimplementation with leaf answer callbacks #2527
Conversation
You can throw, as exception, a term of the form |
Wow, thank you a lot for working on this! I added a few comments directly in the commit, and I hope you find them useful! |
What about a seamless integration of inference limits and/or timeouts? |
This is amazing work! Once this is done, would you have any interest in doing an informal mini-talk or discussion about your experience reimplementing the toplevel? It's a little beyond my current aptitude but I'm really fascinated by it because no other language I've worked with manipulates its own internals like this. It's like a puppet working its own ( |
I assume you mean this as options to
Definitely. I already worked a whole bunch on low level Prolog 1 with the intent to make high level monotonic Prolog easier and more general to use. It's fascinating and I would really like to share some of the insights I learned along the way. Footnotes
|
I renamed some things, removed the old toplevel, expanded the interface with an
This sounds interesting, but what I wanted here is to be able to trace variable identity for any exception1, to have things like this: ?- throw(a(A, B)).
% Current behavior
throw(a(_41,_42)).
?- throw(a(A, B)).
% Nice behavior I wanted to implement
throw(a(A, B)). Is there a fundamental reason why variables need to be copied and lose their identity in exceptions, or why you wouldn't want the "nice behavior" here? Related to this, and I already talked about this before, currently there is an inconsistency between printing of exceptions that are errors (in the sense of having principal functor ?- throw(a(A, B)).
throw(a(_41,_42)).
?- throw(error(A, B)).
% Strips away the throw/1
error(_41,_42). I guess this is for readability, but I think this is maybe the last detail that makes the toplevel not completely follow the philosophy of "the complete answer is logically equivalent to the query"2. If readability is the only argument here, I don't think it matters much. Let's just implement basic comment error pretty printing and readability is solved. In fact I think that I may work on that next, doesn't seem that difficult, although it won't be that useful yet because errors still don't have much information. The vision: ?- throw(a(A, B)).
throw(a(A,B)).
?- throw(error(instantiation_error,example/2).
% ERROR: instantiation_error
% - culprit: example/2
throw(error(instantiation_error,example/2). Footnotes
|
I added a mechanism to stop the query with another argument to the callback. This is needed for when you press enter in the toplevel and also in the |
You can use |
This is awesome, thank you a lot for all this work! In the description of the PR, you mention "a use for a soft cut"; which part of your code would benefit from this? Scryer currently does not provide a soft cut; I see in your code you are able to inspect the WAM to deduce interesting information about choice points, maybe this part can be separated as a construct, at least to improve readability of the current implementation. |
@bakaq I'll try this out soon and tell if I have any notes. thanks for your patience. |
@bakaq very nice! there's some trailing whitespace in toplevel.pl but it all looks good |
Is there anything else to be done here? I'm currently trying to use this to implement |
no, was just waiting for the correction in reply to @UWN's comment. merging now |
I went and reimplemented a good part of the toplevel on top of a predicate I called
run_query_goal/4
. It passes all the current tests, and works the same as the old implementation in my manual testing. The idea is to eventually make this same predicate drive the library interface (when I figure out how to call arbitrary Prolog from the Rust side1), other (pseudo) toplevels, and maybe even some other kinds of things, it's very general. It takes 4 arguments: the query term, a list of variable names (in the form given by thevariable_names(-VarNames)
option ofread_term/3
), a callback goal expecting 3 arguments and a list of options. This predicate then runs the query and calls the callback with the leaf answer and additional information, and the callback then signals if the query should continue or stop. The possibilities for the first argument (the leaf answer) are:final(false)
final(exception(Exception))
final(true)
final(leaf_answer(Bindings, ResidualGoals, VarNames))
pending(true)
pending(leaf_answer(Bindings, ResidualGoals, VarNames))
A principal functor of
final/1
means that this is the last leaf answer in the complete answer, and a principal functor ofpending/1
means that there are more leaf answers. Implementing this made me realize that this pending/final "attribute" of leaf answers is actually very important (it's not quite an iterator, in the Rust sense) and gives me ideas of how to improve the library interface further2. The second argument to the callback is a list with additional information that we can use to implement extensions to this in the future (like reporting inference counts). The third argument needs to be instantiated by the callback to eithercontinue
orstop
to signal if the query should stop.Notice that this can fully represent residual goals (!), with projections (!!) and also reifies exceptions (!!!). The only improvement on this I can think of is to map the query variable names onto the exception like in the
leaf_answer/3
case, but it seems that variables lose their identity when going through a throw-catch, so I don't know if that is even possible.I want to further break the toplevel into configurable/reusable pieces in the future, because that will allow a lot of really cool stuff, especially in the Scryer Playground!
Ok, now I gotta sleep, I've been coding this non-stop for 14 hours straight.
Footnotes
I have some ideas! If it works we could move even more of the engine into Prolog very easily. ↩
It also was the first time I've felt a use for a soft cut, which was interesting to stumble on,
and the first time I've actually used(this was actually not a place wheresubsumes_term/2
, very nicesubsumes_term/2
helped much). ↩