Hi Christoph,

It takes me quite a while to get back into this, and also to validate my message from three years ago. Here are my current conclusions:

I must distinguish better between the use of #future and promise chaining with #then:ifRejected:. While my #halt-s in then:ifRejected: might be nice at development time, they do not make sense in general, where ifRejected blocks are actually the error handlers, much like on:do: handlers. When I complained about the fact that promises swallow errors, it is inconsistent on my behalf to not complain about the fact that on:do: handlers can swallow errors, too... On the other hand, we could and probably should adapt future sends—everything that comes between #future and a #then: or #wait.

So I must rectify something from three years ago:

Am So., 21. Juni 2020 um 22:57 Uhr schrieb Jakob Reschke <forums.jakob@resfarm.de>:

Promises with whenRejected:/ifRejected: callbacks would no longer
swallow errors, and would only be rejected when the user aborts in the
debuggers, or if the future execution catches errors by itself and
converts them to rejected promises, so the future promise will also be
rejected. This could pose a compatibility problem for existing code.

promise := (1 future / 0) then: [:result | result + 3] ifRejected:
[:reason | #cancelled]. "<= inspect it => Actual: resolved with
#cancelled immediately. Expected with my proposed changes: it would
first show the ZeroDivide debugger, which you can abandon to resolve
with #cancelled, or proceed to a MessageNotUnderstood +. If you
abandon the MNU, the promise would be rejected with the MNU, not
#cancelled, in accordance with the Promises/A+ spec."

Promises with when/ifRejected blocks must "swallow" errors that happened in other whenResolved or whenRejected blocks because that is how this model works.

But nevertheless we should show the ZeroDivide in a debugger. The continuation to evaluate "1 / 0" is not inside of a promise callback block, hence the rules to turn the error into a rejection do not apply. Appropriately, the evaluation happens in #fulfillWith:passErrors: rather than in the blocks of #then:ifRejected: in the Promise implementation, so different on:do: handlers apply.

The example is mostly still alright, except for a mistake at the end:

    promise := (1 future / 0) then: [:result | result + 3]
       ifRejected: [:reason | #cancelled]. "<= inspect it
    => Actual: resolved with #cancelled immediately.
    Preferable: show debugger on ZeroDivide.
    Expected on abandon: resolve with #cancelled.
    Expected on proceed: new debugger "ZeroDivide does not understand +".
    Expected on abandon: resolve with #cancelled.
    Expected on proceed: the usual MNU loop..."

The inner promise that is the result of `(1 future / 0)` is actually rejected on abandon, but the `ifRejected: [:reason | #cancelled]` always gets the outer promise resolved.

Kind regards,

Am Sa., 19. Aug. 2023 um 14:40 Uhr schrieb <christoph.thiede@student.hpi.uni-potsdam.de>:
Hi Jakob,

alright, so would you like to upload a patch to the inbox? Should anyone else review it? :-)


Sent from Squeak Inbox Talk