As is, bind/4 creates a a JavaScript event listener that invokes prolog.query() using a textutal query created from the goal of the bind/4 call. This only supports passing the event. As references to JavaScript object are represented as Prolog blobs, there is no textual representation.
Some redesign of the bind primitive would be needed to allow for that. I’m do not know whether that it a good or bad idea. In this case, passing the event and asking the event for the target is probably the way to go. Is this a simplification of a more general problem.
Thank you for the explanation. Sometimes an event on one element should trigger some action on different element, so in those cases I can’t use event.target. But I still can assign an id to the desired target element and pass this ID to the goal through bind/4 and then acess the target with get_by_id/2.
I agree it would make sense. Roughly, I think there are three ways to find UI components:
Directly from a variable in the code (as you tried)
Using an id element
Navigate from the .target through the DOM using some property (the class, element type, some property, …)
I don’t like id as they live in a global name space. This means we must avoid collisions and creating two instances of some DOM sub-tree is not possible (well, there are some options in the current HTML standard). Navigating typically has my preference. Using variables it as least as attractive though. Need to give this some thought.
The after/1 is an attempt to have a non-blocking version of sleep/1. (Most probably it is not the right approach). The first call to after/1 in test_after/0 works OK, but the second call through bind/4 to on_click/0 gives (after clicking on button) a prompt dialog in the browser and bunch of errors in the console.
I tested on Ubuntu 22.04, Windows 10; Chrome, Firefox; and online swipl-wasm build from the code above as well as freshly compiled build (version 9.1.21-88-gaba626a7c). The results seems to be the same in all the tests.
So I dug a little deeper with this and I have two points:
Javascript bind function calls prolog.query, but to be able to use await/2 in “bound goal”, it have to be called with prolog.forEach instead. I’m not sure if it’s general issue in bind function, or if I just need to define custom bind function when I need to call await/2.
There seems to be some error with using library(strings) quasi quotations. If await/2 is called in bound goal through standard bind/4 and there is defined something like this:
:- js_script("null", _).
call await/2 in bound goal ends with error:
'$await'/2: No permission to execute vmi `'I_YIELD'' (not an engine)
which can be resolved with using prolog.forEach. Of course the error is always the same in case no js_script/2 is used.
But if quasi qotations are used like this:
Sorry for the non-response. It should be noted that SWI-Prolog’s await/2, or more general its notion of async procedures is still rather limited. Worse, the system cannot (yet) properly test that it is trying to do something illegal. Part of these problems are probably related to this.
Prolog’s VM is written in C and translated using EMSDK to WASM. That code generally is not async. I think I saw a remark that there is (experimental?) support to compile the code as async, but that probably won’t help us either due to performance reasons. The VM does implement an instruction that causes the VM to stop in a way that it can be restarted. The stop instruction is I_YIELD and that cannot be executed if the VM calls itself recursively, i.e., Prolog calls C, which calls Prolog again. That is (I think) your permission error: parsing a quasi quotation calls Prolog from the C based reader, so you cannot yield from there. await/2 uses I_YIELD.
The second problem is that the VM cannot be involved in two threads of execution. I.e., if some JavaScript function calls Prolog and Prolog uses await/2, no other JavaScript function can do the same before the first completes. There is, if I recall correctly, no guard against this. This makes async calls from a bind hazardous.
The latter can be fixed by detecting this and creating a new engine to deal with the “overlapping” async thread of execution. Originally, engines only worked in the multi-threaded version. There is now experimental code to use engines in the single threaded version.
The WASM version is an interesting start and already quite useful. It does need more work though. Help, either attacking these issues or getting funding to get this resolved is welcome.
I didn’t expect a response anytime soon, if at all. I really appreciate all your work and am always wondering how you can manage to work on such a complex system and still be able to address so many issues here on discourse.
Thank you for your explanation. There are still topics I need to learn a lot more about, but hopefully I will come across something with which be able to contribute somehow. It’s just a hobby project with no money involved, so I have no real funds for this.