IGJoshua / farolero

Thread-safe Common Lisp style conditions and restarts for Clojure(Script) and Babashka.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Tests or examples exercising Farolero with Clojure async and js/Promise

enspritz opened this issue · comments

Hi!

It would be highly instructive to me if the project supplied some tests or documentation that exercises Farolero with Clojure(Script) async as well as ClojureScript code that interops with js/Promise.
Are there any established patterns that can be relied upon as a kind of known-good practice?

Hey, thanks for opening the issue. I can definitely work to do that, but I also just need to caution you: farolero and the mechanisms of conditions and restarts in general rely heavily on the stack (although this could probably be at least partially avoided in a language with reified continuations, but that would cause its own issues).

The entire purpose of core.async and promises is that they remove the stack context from execution.

This leads to a couple rules you can follow depending on if you're using a promise-like API, and a second set of rules you can follow with an async/await-like API (like core.async).

For Promises: do not invoke restarts that were bound outside of the lambda passed to the Promise level that the signal came from, and if you want to use bound handlers then construct promises using bound-fn rather than fn.

For async/await: treat puts and takes as if they split the code in two, the code that was evaluated before, and the code that's evaluated after. Never bind a restart around a split, only bind restarts fully within one section of the code. Then treat these sections the same way as you treat a promise lambda, and be aware it automatically acts like bound-fn.

For both: handlers bound with handler-case always unwind, and so do not compose with async code run inside their scope. Any async code within them must ignore external handlers, which you can do with either fn rather than bound-fn (and remember core.async go blocks always act like bound-fn), or with the farolero macro without-handlers.

An additional note here is that in ClojureScript invoking a bad restart or signaling something under a handler-case is simply undefined behavior. But in JVM Clojure it will detect that you are doing this and signal a :farolero.core/control-error condition, which if not handled itself will then invoke the debugger, by default throwing an exception and unwinding whatever is going on.

Thanks @IGJoshua . The information you shared might serve as enough documentation for me to go with for now. I spent a few hours considering ClojureScript support for the base art library of ash-ra-template.

One focus that arose is automated testing those parts of ART that uses Farolero to signal conditions. And given that this is JavaScript, the land of asynchronous calls, I seek to thoroughly exercise and prove correctness of ART when used in CSP-style scenarios. Covering js/Promise, core.async, and even (overkill?) promesa and manifold would drive the point that both Farolero and ART are reliable in such scenarios + serve as examples on how to interface with them in each programming style.

Thanks for Farolero BTW, I'm really enjoying it!