0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Call Later in Dogelog Notebooks

Last updated at Posted at 2025-06-10

Introduction

Dogelog Player is a Prolog system mostly written in Prolog itself. It is available for the JavaScript, Python and Java platform. In the browser environment it offers Notebooks that run without server roundtrip.

4832.gif

Compared to for example SWISH or Tinker from SWI-Prolog, the syntax coloring is in its early stage. Notebook cells were only colored during instrumentation. Things have a little improved with delayed re-coloring now.

Stackless Coroutines

Dogelog Player provides stackfull and stackless coroutines via the abstraction of tasks and timers. So far the APIs for these concepts had only experimental and internal status with a os_ prefix. We decided to make them first class citizens. Concerning stackless coroutines there was only one predicate:

  • call_later(G, D):
    The predicate succeeds. As a side effect it schedules the goal G to be executed after D milliseconds

We the upcoming release 1.3.3. the end-user will have access to:

  • call_later(G, D, T):
    The ternary predicate succceeds in T with the scheduled timer
  • timer_cancel(T):
    The predicate succeeds. As a side effect it cancels the timer T

It should be noted that timers are realized as stackless coroutines, they hook into the stack of the task that created the timer. In as far to simplify the realization of our cooperative multitasking we have restricted their functionality. Basically they run protected from yielding.

React Library

Although timers can extend their limited functionality via creating their own tasks, they have already found a couple of use cases in their limited form. One realization that we would like to mention is our take the predicate call_with_time_limit/2.

call_with_time_limit(Delay, Goal) :-
   current_task(Task),
   setup_once_cleanup(
      call_later(sys_timeout_signal(Task), Delay, Timer),
      Goal,
      timer_cancel(Timer)).

The predicate is usually realized in a multi-threaded Prolog system by having an alert thread that tears down the watched thread. The above shows how the same can be elegantly implemented with cooperative multitasking when the watched task yields enough.

Further applications of stackless coroutines are found in our library(react). The library allows to attach a stackless coroutine to a DOM element as an event handler. This corresponds to addEventListener() where the listener is not async:

  • tag_bind(T, P, G):
    The predicate succeeds. As a side effect it adds a type T stackless
    event handler with formal event parameter P and callback goal G to the cursor

A further extension is:

  • tag_bind(T, P, G, L):
    The quaternary predicate allows specifying event options

Among the event options we have block(true), which then realizes the waitForInput() pattern based on addEventListener() and removeEventListener(). In our variant the stackless coroutine is not called strickly once, since it acts as a filter via its Prolog failure and success result.

Debounced Recoloring

To implement recoloring of a Notebook cell, we found it useful to resort to another event handling pattern that didn't make it into our event options lists. It can be easily realized by storing a timer in a DOM element (sic!). The Prolog code reads as follows. The instrumentation is the moment we add the event handler:

instrument_elem(E) :-
   colorize_elem(E),
   ir_cell_bind(E, 'input', _, notify_elem(E), 0).

If the event is triggered before the delay we cancel the old timer:

notify_elem(E) :-
   ir_object_current(E, is_dirty, T), !,
   timer_cancel(T),
   call_later(mirror_elem(E), 1000, S),
   ir_object_set(E, is_dirty, S).
notify_elem(E) :-
   call_later(mirror_elem(E), 1000, S),
   ir_object_set(E, is_dirty, S).

Recoloring removes the current time which has now fired:

mirror_elem(E) :-
   ir_object_reset(E, is_dirty),
   colorize_elem(E).

The above event handling pattern is called debouncing, and the term is derived from the debouncing of electronic contacts such as in computer keyboards. In electronics this can be done by various methods such as wetted contacts, filters and Schmitt triggers. The term made it now into the Mozilla Glossary here.

User Experience

We opted for a 1000 ms delay since IntelliJ IDE seems to use this delay sometimes. It doesn't put a lot of computational load on the front end. We combined it with the attribute value plaintext-only for the attribute name contenteditable of a DOM element. Since we use a full recoloring not much goes wrong:

Aufzeichnung 2025-06-07 113817.gif

Here is an example from SWI Tinker where recoloring gives a hickup:

Aufzeichnung 2025-06-07 112633.gif

In general recoloring requires a more carefully crafted coloring library. The requirement changes from coloring valid tokens to coloring potentially invalid and incomplete tokens. We went through a couple of iterations of our library(fancy) to make it fit for the new requirement.

Conclusions

To implement recoloring of a Notebook cell, we found it useful to resort to the debouncing event handling pattern. It can be easily realized by storing a timer in a DOM element (sic!). Although recoloring requires coloring potentially invalid and incomplete tokens, since we use a full cell recoloring, not much goes wrong.

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?