Re: Proposal to improve Promises (was Re: Re: The Inbox: Kernel-tonyg.1610.mcz)

Lauren P <[email protected]>
Newsgroups gmane.comp.lang.smalltalk.squeak.general
Message-ID <CANAQNuw724e9EjPJjpH4EWX-gCRC_KxAQK7CUfRCrEWGs=YDXw@mail.gmail.com>
Hi Jakob,

Is this far off the idea?

It sounds like you want continuations, but at specific places you ask the
user to proceed to the! next step or do something else (including
abandoning). There might also be context-sensitive branches you can choose
from based on which part of the continuation you're in when the computation
stops (due to error or interrupt).

If so, that is a Common Lisp restart. I was playing with implementing them,
but never needed one enough to warrant spending much time on it.


On Tue, Apr 28, 2026, 16:29 Jakob Reschke <[email protected]> wrote:

> Time flies and the code is years old by now, so I would have to dig
> deep into it or think hard to make any promises (pun intended) which
> way would work best, but I do not want to spend my free time with that
> right now. (I seem to rather spend it writing such emails at midnight,
> sigh.)
>
> What I can give you as context is why I used promises there. I sought
> for something that allows me to code multi-step workflows that are
> interrupted with user interactions (i. e. compute something, then ask
> something from the user via a dialog, then continue, maybe another
> interaction with waiting, continue, ...) with the following criteria:
>   - not obscuring the overall workflow too much by tearing it apart
> across many methods that don't seem to invoke each other directly,
>   - being able to use the Restart button in the debugger to easily
> roll back part of the workflow without having to restart from the very
> beginning and repeating lots of clicks,
>   - related to the above: not losing access to the contexts of what
> happened before the last user interaction ("How did I get here?!"),
>   - keeping the workflow implementation somewhat decoupled from the
> GUI implementation, especially the wiring what happens after you click
> Ok or Cancel,
>   - without modal dialogs because I strongly dislike how they break in
> Monticello (or used to, I don't know whether anything has changed
> there in the meantime).
>
> Promises did not really make me completely happy. I also tried other
> things, such as having the workflow in another Process. I was
> interested in continuations, too, but never fancied to roll yet
> another implementation of them or pull in another dependency for them.
>
> Which means: it is not mandatory that the Squot implementation stay
> with promises or with relying on BrokenPromise. Whoever takes time to
> look into the issue, and finds a simpler, stable alternative: feel
> free to change it. Maybe it is better without promises, or maybe it is
> simple to adapt the code to this breaking change and stick to
> promises, I don't know.
>
> As you can see in the code that you, Christoph, quoted, the purpose of
> most handlings of BrokenPromise is to react to the user cancelling the
> interaction, and to cancel the overall workflow, too. I vaguely recall
> that the promise (of completing the user interaction) gets rejected
> with #cancelled if the user cancels, but I am not sure. Thus this is
> not really an error/exception situation. So maybe one way would be to
> change the implementation to not reject the promises in such cases but
> resolve them with a special cancel value and check for that. That
> sounds like making it as ugly as null checks, but maybe that is not
> less pretty than the current code handling BrokenPromise.
>
> Also note this ifError:ifNotError: extension... I wrote that and it
> looks like at least one reason for it was a different incompatible
> change in promises around Squeak 5.3 already. (I vaguely remember
> being in favor of that change though.) How much patching will it take
> this time to make the adapted code work both in the latest Squeak and
> in slightly older Squeak versions? Thus the question in my last email
> was serious. If nobody finds a nice way to achieve that, well, then
> this is a very annoying breaking change!
>
> Kind regards,
> Jakob
>
> Am Mo., 27. Apr. 2026 um 23:04 Uhr schrieb Thiede, Christoph
> <[email protected]>:
> >
> > I mentioned you because you might understand your code base best. :D
> Quickly skimming through some representative references to BrokenPromise on
> the develop branch:
> >
> > [[[answer := aWaitable wait]
> > on: BrokenPromise do:
> > [:bp | bp ifError: [:error | exception := error. bp return] ifNotError:
> [bp pass]]
> > on: Error do:
> > [:e | exception := e. e return]]
> >
> > This is easy:
> >
> > [[[answer := aWaitable wait]
> > on: (Smalltalk at: #BrokenPromise) do:
> > [:bp | bp ifError: [:error | exception := error. bp return] ifNotError:
> [bp pass]]
> > on: Error do:
> > [:e | exception := e. e return]]
> >
> > (The second handler will now handle the failed promise.)
> >
> > self withUnitOfWork:
> > [[SquotGUI waitFor:
> > ((workingCopy newInteractiveSaveOperation
> > title: 'Select changes and message for the new version';
> > applyToWorkingCopy)
> > then: [:result | self refresh])]
> > on: BrokenPromise do: [:e | "cancelled" e return]].
> >
> > This is harder: You explicitly only search for asynchronous errors but
> not for synchronous errors. If the operation can be assumed not to raise
> synchronous errors as well, simply rewrite:
> >
> > self withUnitOfWork:
> > [[SquotGUI waitFor:
> > ((workingCopy newInteractiveSaveOperation
> > title: 'Select changes and message for the new version';
> > applyToWorkingCopy)
> > then: [:result | self refresh])]
> > on: Error do: [:e | "cancelled" e return]].
> >
> > If the operation might also raise synchronous errors, this is not
> something we could easily catch with the new refactoring. If necessary, you
> could test whether the signaler context of the error was
> Promise>>#signalErrorValue.
> >
> > [SquotGUI waitFor: promise] "will be resolved with a SquitAddRemote or
> rejected with #cancelled"
> > on: BrokenPromise do: [:e | e ifNotError: [^ self undo]].
> >
> > No idea when non-erroring broken promises are raised. Only in Squeak 5.3
> and older? Then simply:
> >
> > [SquotGUI waitFor: promise] "will be resolved with a SquitAddRemote or
> rejected with #cancelled"
> > on: (Smalltalk at: #BrokenPromise) do: [:e | e ifNotError: [^ self
> undo]].
> >
> > Overall, I think this should be doable. But I also mentioned you because
> I would be interested in your opinion on this refactoring - does it prevent
> possible use cases you would have in mind or does it only streamline
> working with promises and prevent bad ideas?
> >
> > Best,
> > Christoph
> > ________________________________
> > Von: Jakob Reschke <[email protected]>
> > Gesendet: Montag, 27. April 2026 21:31 Uhr
> > An: The general-purpose Squeak developers list <
> [email protected]>
> > Cc: Thiede, Christoph <[email protected]>
> > Betreff: Re: [squeak-dev] Re: Proposal to improve Promises (was Re: Re:
> The Inbox: Kernel-tonyg.1610.mcz)
> >
> > If somebody volunteers to refactor Squot to work without
> > BrokenPromise, will the result also work on older Squeak releases?
> >
> >
> > Am Mo., 27. Apr. 2026 um 19:54 Uhr schrieb Christoph Thiede via
> > Squeak-dev <[email protected]>:
> > >
> > > Hi Jakob, Tony, Chris, all,
> > >
> > > I only found out today that BrokenPromise has been deleted from the
> trunk. This is quite a breaking change.
> > >
> > > For example, one can no longer install Squot (which is recommended in
> the Preference Wizard) into a recent trunk image because it handles and
> extends BrokenPromises in multiple places.
> > >
> > > Regarding functionality, it is no longer possible to (i) disambiguate
> an exception raised asynchronously from a regular exception, (ii) access
> the original signaler context of the inner exception, or (iii) identify the
> causing promise. Squot seems to only do (i), while I think (ii) and maybe
> (iii) would be helpful debugging features (when I occassionally witnessed
> sporadic bugs in Squot in a few years ago, it was already hard enough to
> find out where they came from because the stack was not preserved, and not
> even preserving the signaler context or the resolvers will likely worsen
> such situations).
> > >
> > > That being said, I also understand that this extra exception wrappers
> felt opaque and cumbersome. Evolving APIs vs retaining
> backward-compatibility always is a tradeoff. Not sure what's more important.
> > >
> > > At the very least, why do we have to raise a generic Error rather than
> a specific one if errorValue is not an exception?
> > > For the other case, maybe we would need a way to attach multiple tags
> to exceptions to preserve information such as original signaler contexts or
> re-signaling promises to the original exception object without removing
> other information. Like, Exception>>tagDictionary instead of
> Exception>>tag? Maybe a transparent wrapper around the original exception
> class that forwards all unknown requests to the original exception (though
> this would not preserve identity, if that matters)? Maybe something else?
> > >
> > > For the upcoming release, I suggest we should do at least one of the
> following options:
> > >
> > > (i) Update Squot to no longer depend on BrokenPromise (it is not
> actively being maintained right now and a successor is in development, but
> still our recommendation in the preference wizard)
> > > (ii) Provide backward compatibility for BrokenPromise (though I don't
> know how we could achieve that)
> > >
> > > Also, we should document this breaking change in the release notes.
> > >
> > > Best,
> > > Christoph
> > >
> > > --
> > > Sent from Squeak Inbox Talk
> > >
> > > On 2025-08-12T10:15:25+02:00, [email protected] wrote:
> > >
> > > On 8/8/25 03:24, Chris Muller wrote:
> > > > It might be fine, but please let me know:  If the settlement
> > > > of one Promise utilizes a variable which refers to another, yet
> > > > unsettled Promise, and that inner promise is rejected, how should the
> > > > (outer) Promise respond?  I guess it would be Error Error Error up
> the
> > > > Promise chain; likely each signaled from different Processes, is that
> > > > right?  Or, do we only want one signal from the "root" Promise?
> > >
> > > Promises do chain together, yes. The implementation in Squeak already
> > > does something reasonably sensible (I'm sure there are more corner
> cases
> > > waiting to be discovered), drawing heavily on
> https://promisesaplus.com/.
> > >
> > > > But yes, for how *I* use Promises, this proposal is absolutely fine.
> > >
> > > Thanks. I'll go ahead and merge then, I think! I have a small queue of
> > > improvements to Promises that will follow.
> > >
> > > Best,
> > > Tony
> > >
> > > Squeak-dev mailing list -- [email protected]
> > > To unsubscribe send an email to
> [email protected]
> Squeak-dev mailing list -- [email protected]
> To unsubscribe send an email to
> [email protected]

Squeak-dev mailing list -- [email protected]
To unsubscribe send an email to [email protected]
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.