Skip to content

Add "andthen" and "orelse" to Promise.rakudoc #4584

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

voldenet
Copy link
Contributor

This pull requests adds documentation for Promise.andthen and Promise.orelse methods, since they're really useful.

@voldenet
Copy link
Contributor Author

I'm not sure if these methods should be documented in the current form - they could get changed, so they get result/cause instead of the promise, which is not needed for anything.

@librasteve
Copy link
Contributor

maybe wise to also add two tests to ROAST - then the behaviour is official?

@FCO
Copy link

FCO commented May 29, 2025

Here is a suggestion on what it could do (not a suggestion on how to do it): https://glot.io/snippets/h7sotvz66u

@raiph
Copy link
Contributor

raiph commented May 29, 2025

Promise.andthen and Promise.orelse methods, since they're really useful.

.oO ( Did you forget a .new? )

(In other words, you must mean Promise.new rather than Promise, and I've decided to be explicit about that wrinkle in this comment in case anyone reading it is fairly new to Raku.)

I'm not sure if these methods should be documented in the current form - they could get changed

(Afaik) we are only supposed to document roasted behavior. Has that changed? (I'm asking about the general principle, regardless of whether any given current Rakudo behavior is or isn't roasted.)

I will assume that their current behavior is not roasted based on @librasteve's comment. (If that assumption isn't warranted then please adjust your understanding of the rest of this comment.)

they get result/cause instead of the promise, which is not needed for anything.

That sounded surprising, so I reflected for a moment.

One thing that immediately jumped to mind is that, in theory, and presumably potentially occasionally in practice in a future Rakudo run of a program, if not already, a new Promise instance might already have been kept or broken by the time the andthen/orelse method runs.

(I don't mean the code passed to the andthen/orelse method. I mean the andthen/orelse method itself.)

Presumably the foregoing does not matter -- it still all works as intended, right?

(I'm just asking y'all to check my home work/thunk after the mind jump I began this paragraph with. TIA.)


Another thing that jumped to mind is that the proposed change would presumably make andthen/orelse blocking (synchronous) methods unlike the current non-blocking / asynchronous behavior.

Presuming I'm right then that would surely be a fundamental change, right? Furthermore, I presume it would also be a breaking change for a reasonable definition of "breaking", right?


If we presume that the current behavior is not roasted (see above), and, furthermore, that that reflects the current behavior not being "spec'd" (in the last decade or so sense of "spec'd"), then that does have some weight on the side of making what I am presuming is a fundamental and/or breaking change.

However, I would still expect, or at least hope, that any such change would be discussed further before taking any action like adding roast tests for this or that behavior.

It would make sense to me for initial discussion to occur here. If we get consensus here, and perhaps on IRC or whatever, that, in retrospect, the current behavior makes sense, then we can decide whether to roast it as is, and document it as is, or instead just open a problem-solving issue.

A similar notion applies if we get consensus that the current behavior does not make sense, and that making it blocking makes sense. (I will say right now that making these methods block doesn't yet make sense to me as I write this sentence. Especially if that breaks code out there -- but even if it doesn't. But I'm getting older and am ever more aware that I could easily be making some mistake, basic or subtle! 😊)

If instead the current behavior is roasted, well, a similar notion to the above still applies. I'd expect discussion and development of consensus.


A final set of thoughts jumped to mind after the above and is covered in the rest of this comment.

(All of this jumping and reflecting on the jump described above, plus what follows, took about one second, literally. It's then taken me in the order of an hour to linearize it into words. I love and hate my brain/mind in equal measure.)

This final set of thoughts was about what andthen is doing, and what the words "and then" suggest.

(Actually, the initial thought was my first thought as I read the title of this issue before reading any of its content. The title -- Add "andthen" ... to Promise.rakudoc -- juxtaposes "andthen" and "Promise", which instantly sets things up for confusion.


The andthen infix operator has relatively obvious sequential / synchronous semantics. The LHS is evaluated, and then ("and then"), based on the result of the LHS, the RHS may be evaluated.

The exact same explanation of the overall semantics would work fine for orelse: the LHS is evaluated, and then ("and then"), based on the result of the LHS, the RHS may be evaluated.

Note how it's "and then" regardless of whether the operator is andthen or orelse. That's because the "and then" is just how code executes in most Raku code, and indeed most code in any programming language. One thing happens and then another and then another, sequentially.


To a degree the andthen Promise method is like the foregoing.

The LHS is evaluated, and then ("and then") the method is invoked. The "and then" I just wrote has nothing to do with the exact nature of the particular method. The method could be, for example, .orelse or .say or .my-method.

My point here is that .andthen means something like "queue some code that will await the Promise instance that's my invocant -- but do not await me, the method you just called". This is entirely different semantics from the andthen infix operator.


Thank you for your patience with my verbosity!

P.S. And maybe imagine what it's like to deal with me if I'm speaking. Consider the following:

Someone says a single sentence, like Add "andthen" and "orelse" to Promise.rakudoc.

In response all of the above -- plus a vast array of other thoughts I have not written about, something like a thousand times as much -- nearly simultaneously explodes in my brain/mind in less than a second.

This is all very off topic of course, but it's the kind of thing that has torpedoed my personal relationships with pretty much all friends, family, and even my wives.

Thank goodness for a community that can cope with my verbosity and off topic writing. I hope! Much love to any and all patient enough to read this far. 💚

@FCO
Copy link

FCO commented May 29, 2025

Another thing that jumped to mind is that the proposed change would presumably make andthen/orelse blocking (synchronous) methods unlike the current non-blocking / asynchronous behavior.

The block passed to andthen is already only called after the promise is kept, so it's always immediate. Right?

@voldenet
Copy link
Contributor Author

we are only supposed to document roasted behavior

That makes sense, these docs will have to wait until this is specified.

The rest of the comment is about the issue rakudo/rakudo#5892

.oO ( Did you forget a .new? )

I was trying to not write long blocks of code in the title, but I promise that the description has proper code.

Another thing that jumped to mind is that the proposed change would presumably make andthen/orelse blocking (synchronous) methods unlike the current non-blocking / asynchronous behavior.

The continuation is always executed after the Promise is resolved, so the behavior of those two methods would not significantly change. The biggest change is that the original Promise would not be accessible anymore, however already documented then method covers advanced use cases. My initial need was to have .then and .catch known from promises in javascript to allow building chain of callables in a simplified way, so upon finding undocumented (but working) methods, I thought it'd be good idea to describe them.

(Actually, the initial thought was my first thought as I read the title of this issue before reading any of its content. The title -- Add "andthen" ... to Promise.rakudoc -- juxtaposes "andthen" and "Promise", which instantly sets things up for confusion.

I have used the already existing methods, so I didn't think much about how they're named, but I agree - these names could be more unique.

@raiph
Copy link
Contributor

raiph commented May 29, 2025

@FCO

The block passed to andthen is already only called after the promise is kept, so it's always immediate. Right?

I was presuming that, currently, it's always immediately, if by "it" you mean return from .andthen. But @voldenet wrote:

I'm not sure if these methods should be documented in the current form - they could get changed, so they get result/cause

I was interpreting "they" as referring to these methods (not the block they're passed) and "get result/cause" requiring awaiting the promise before .andthen returns. So it'll no longer immediately return, right? (If not, I'm confused. Well, I'm confused anyway, but hopefully you see my point!)

@FCO
Copy link

FCO commented May 29, 2025

But the .result and .cause do not need to be called when the method runs, but, if it's implemented using .then, it could do something like:

method andthen(&block) {
    $.then: -> Promise $p {
        block $p.result if $p.status ~~ Kept; # it's only called inside the then's block. So, only after kept or broken (kept on this case, because of the if)
        # treat catch case...
        ...;
}

@raiph
Copy link
Contributor

raiph commented May 30, 2025

@voldenet

The continuation is always executed after the Promise is resolved

I can imagine that, by "continuation", you mean one of three entirely different things:

  • One is a "continuation" per its use in Rakudo corresponding to the computer science meaning. This is used in the context of Raku(do) to implement await, as explained on SO by jnthn . If you mean that, then yes, that's always executed after the promise is resolved -- but that does not alter the problem I see.

  • A second thing you might mean by "continuation" is the block that's passed to the .andthen. This is entirely different, and imo a confusing use of the term, but I can handle that usage if it's what you mean, and for this paragraph will do so. Again, yes, provided the promise is kept, then the block will be executed after the promise is resolved. (It won't be if the promise is broken.)

  • A third thing you might mean is something else that's neither of the two foregoing notions. In which case I don't know what you mean.

I will presume you mean one or both of the first two meanings of "continuation".

If so, the problem remains.

You commented about a possible change to .andthen whereby it returns the promise's result/cause -- i.e. its resolution data -- instead of the promise itself.

I presumed the current .andthen just returns the promise, and does not wait until the promise resolves before doing so. In contrast I presumed your mooted changed .andthen would have to wait until the promise resolves before returning the result/cause. (Because the result/cause isn't available until the promise resolves.)

If one or both my presumptions are wrong, what change were you suggesting might happen?

so the behavior of those two methods would not significantly change.

I view injecting (the equivalent of) an await into .andthen's logic before it returns as a (very) significant change.

If you didn't/don't mean that, then what change did/do you mean?

As a recap, here's the comment by you that has triggered my book length discussion:

I'm not sure if these methods should be documented in the current form - they could get changed, so they get result/cause instead of the promise, which is not needed for anything.

@voldenet
Copy link
Contributor Author

@raiph
To clarify - I didn't mean that andthen should block. The &code parameter inside it should get different parameters.
See the original issue about it: rakudo/rakudo#5892

@FCO
Copy link

FCO commented May 30, 2025

What I think was meant by:

I'm not sure if these methods should be documented in the current form - they could get changed, so they get result/cause instead of the promise, which is not needed for anything.

Is that the block passed to andthen ($promise.andthen: { this block }) would receive the .result instead of the promise object.

On .then, it's important to the block to receive the promise object, because it needs to be able to test its state.

On the other hand, if the block passed to andthen is running, the status is already known, it's Kept, otherwise it wouldn't run the block. So we don't need the whole promise object be received by the block, only the result is enough.

@raiph
Copy link
Contributor

raiph commented May 30, 2025

@voldenet

Ahhhh. A penny has dropped. So, in this sentence:

I'm not sure if these methods should be documented in the current form - they could get changed, so they get result/cause

the first "they" refers to "these methods", whereas the second "they" refers to the block (not a method, or at least probably not) that's passed to, er, them (the two methods andthen / orelse). Right?

(I had tried to carefully draw that distinction in my comments, precisely because I didn't want to presume I knew what you meant, but it's tough to be precise in natural language, especially English!)

I'm now confident I understand what you meant, and how I got confused, and that I can just apologize for the noise, and it's time for me (and I suggest you and @FCO) to hide our comments in this thread about the tangent I started.

But I think I'll wait for confirmation in the form of you (and @FCO) reading this, and responding by hiding some/all of your comments about this tangent as "Resolved". I plan to mark my comments likewise presuming I see that. Thank you for your patience!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants