WASM wait for user input

I’m trying to build an interactive “expert system” in an html page with the wasm version of swi-prolog.

I have a textbox for the user input (that would normally come from the console), and a button to submit the text message.

I can write to the html page, and I can use bind/4 to add click events on the button, but is there a way I can suspend prolog until the value is read from the textbox?

Here is a snippet from my code.
add_message simply prints strings on screen, nothing more.
In the read_and_continue I can print the data read form the textbox, but ask_user does not wait for read_and_continue to end.

ask_user(Question)  :-
	get_by_id(submit, Button),
	add_message(expert_message, P),
	bind(Button, click, _, read_and_continue(Question)).

Good question. I think the answer is in creating a promise that waits for the (click) event and then use await/2. There are several code snippets on stack overflow for waiting for an event using a Promise. If you also want to process other stuff you should probably make sure that this “thread” runs in is own engine using Prolog.forEach() with the {engine: true} option as you find it (only) in the very latest (9.3.20) release.

So far, Prolog+WASM+JavaScript was mostly limited to using callbacks. With JavaScript Promise and Prolog engines we have something similar to JavaScript async functions. No example code and experience though. Please give it a try and share your experience!

I tried (copying some strange javascript code from stackoverflow for the js part).
The issue is if I run it with swipl-wasm I get the error in the picture. I’m not sure about what the input window is…

My understanding is that the prolog part should be something like this:

ask_user(P, Proof, Trace)  :-
	get_by_id(submit, Button),
	add_message(expert_message, P),
	FP := waitForClick(),
	await(FP, Text),
	_ := console.log(Text).

If I remove the last three lines I can print stuff to the html page (that is what add_message does).

waitForClick is the javascript code that adds the promise to the button click, and returns the value read from the input.

I’m sure I’m missing something obvious :upside_down_face:

The system error points at a GC error. Most likely caused by something fishy in JavaScript that wraps the WASM kernel.

Can you turn this into a reproducible thing we can run?

Sure. I made a few changes and included everything in the html file (prolog script included).
I’m not particularly familiar with Javascript, so that might very well be where the issue is.

This is basically the tweety code, with the addition of a user query tied to the html form.
The user is supposed to enter yes/no in the textfield.

Sorry for the delay. There are two issues.

  • There are two singleton variables. Apparently something goes wrong reporting these, causing the GC system error. If you fix these, you get a little further.
  • To run the query, you use Prolog.query().once(). But, this is synchronous. If you want async programming (which you need if you want to use Promises), you need Prolog.forEach(). That returns a promise, so you either need to add .then(...) or put it in an async function and add await.

With these two fixes it prints the answer to the question in the console, so that step works. I didn’t look at the rest.

Up to me is why printing compilation warnings leads to problems …

Fixed that one. Loading your original program in the latest version now gives this in the console, which looks pretty adequate to me.

swipl-web.js:9 Warning: /script/1:79:
swipl-web.js:9 Warning:    Singleton variables: [Button]
swipl-web.js:9 Warning: /script/1:109:
swipl-web.js:9 Warning:    Singleton variables: [C]
expert.html:182 Consulted
swipl-web.js:9 await/2 is only allowed in Prolog.forEach() queries
```

Thanks Jan!
Now it works. I hadn’t realized how the forEach worked.

There are still a few things to fix with more complex question/answering systems, but I can work with it now!

I’m basically re-implementing parts of the webconsole pack for an expert system.

(I’m sure there are better ways to go about doing this…)

Probably. We should probably have a closer look at other browser based Prolog systems such as Tau Prolog. Note there seem to be three ways for Prolog in the browser:

  • Build it all in JavaScript (e.g., Tau)
  • Compile to WASM. As is, WASM is synchronous unless you compile it as async code, but Emscripten says there is a high price both wrt size and performance. WASM engine support seems to be on the way, which may change things. If not, we have two options
    • Put Prolog in a web worker and communicate with it using messages. That is what e.g. Ciao does (AFAIK)
    • Support yield from the VM, so you can return to the browser event loop, optionally wait for something and resume. That is what SWI-Prolog does.

The first and last allows quite direct manipulation of the DOM, something which is a lot harder when using a web worker. These two result (I think) in a different approach for integrating Prolog into the browser.

At least, that is my current understanding. I’m not a browser/JavaScript/WASM expert though. If I missed some useful path, I’d be glad to hear.

I’m no browser/javascript expert either. Far from it in fact.

I’m mainly trying to use it for simple demonstrations of user interaction aimed at students.
I have to admit that I’d also like something more robust to come out of this, but that’s a long term plan…