doAfterSuccessOrError and doOnSuccess not called in order if there is intermediate operators - project-reactor

I'm using reactor-core 3.2.10.RELEASE. By default the doAfterSuccessOrError should be called after doOnSuccess called. But if I add then or publishOn operators it seems that it creates an inner Mono and the order of the doXXX changes.
Is this an intended behavior?
Mono.just(1)
.doAfterTerminate(() -> System.out.println("Terminated"))
.doAfterSuccessOrError((i, e) -> System.out.println("AfterSuccessOrError: " + i))
// Uncommenting any of these will change the order to
// .then(Mono.empty())
// .then()
// .publishOn(Schedulers.elastic())
.doFinally(s -> System.out.println("Finally called"))
.doOnSuccess(s -> System.out.println("Success"))
.subscribe(i -> System.out.println("Result: " + i));
Expected output:
Success
Result: 1
AfterSuccessOrError: 1
Terminated
Finally called
After uncommenting then or publishOn the order changes.
AfterSuccessOrError: 1
Terminated
Success
Result: 1
Finally called

Is this an intended behavior?
Yes. As per the docs for `then():
Let this Mono complete then play another Mono.
...so in this case, it's because the Mono up until that point completes (hence the first two operators print), and then the output from your next Mono prints (the final 3 operators).
For publishOn(), it's slightly different:
This operator influences the threading context where the rest of the operators in the chain below it will execute, up to a new occurrence of publishOn.
This means that you have your first two operators executing on one thread, and then the rest of your operators executing on a separate thread (defined by the elastic scheduler) - which is why you see the output. The mechanism is different here, but the end result happens to be exactly the same.

Related

Flux.switchOnNext variant that switches when the next publisher _emits_ rather than when it _is emitted_

Reactor has the switchOnNext operator which mirrors a sequence of publishers, cancelling the previous subscription whenever a new publisher becomes available:
For my use case I need a variation on this theme, where instead of cancelling the first publisher before subscribing to the next, I continue to mirror the sequence of publisher 1 until the point when publisher 2 emits its first item, and only then make the switch, as in this marble diagram (for anyone who finds this question later this is not a diagram of an existing operator from the Reactor docs, it's one I've just sketched myself):
I appreciate that in the general case this could potentially involve the operator maintaining an unbounded number of subscriptions waiting for any one of them to emit before cancelling the others, but for my use case I know that the initial flux-of-fluxes is finite (so I don't necessarily need the fully general publisher-of-publishers solution, something that works for a finite list of N publishers would be sufficient).
Can anyone see a clever combination of the existing operators that would implement this behaviour or do I need to write it from first principles?
Interesting problem! I think something like this might work:
#Test
void switchOnNextEmit() {
Duration gracePeriod = Duration.ofSeconds(2);
Flux.concat(
Mono.just(sequence("a", 1)),
Mono.just(sequence("b", 3)).delaySubscription(Duration.ofSeconds(5)),
Mono.just(sequence("c", 10)).delaySubscription(Duration.ofSeconds(10)))
.map(seq -> seq.publish().refCount(1, gracePeriod))
.scan(
Tuples.of(Flux.<String>never(), Flux.<String>never()),
(acc, next) -> Tuples.of(acc.getT2().takeUntilOther(next), next))
.switchMap(t -> Flux.merge(t.getT1(), t.getT2()))
.doOnNext(it -> System.out.println("Result: " + it))
.then()
.block();
}
private static Flux<String> sequence(String name, int interval) {
return Flux.interval(Duration.ofSeconds(interval))
.map(i -> name + i)
.doOnSubscribe(__ -> System.out.println("Subscribe: " + name))
.doOnCancel(() -> System.out.println("Cancel: " + name));
}
The important part is that we convert the sequences into hot publishers (meaning that resubscribing them doesn't cause another subscription, rather than we share the initial subscription). Then we use scan to emit a tuple containing the previous and the next, and finally we just use a regular switchMap to observe both (note how the first will stop when the second emits, due to takeUntilOther).
Note that the grace period is important because switchMap will first cancel and then subscribe the next, so if there isn't any grace period it would cause the current hot publisher to fully stop and start from scratch, which is not what we want.

What exactly does `return` do in AutoHotKey?

In a comment chain, I ask:
Hmm, so the return doesn't play like a closing bracket like in other languages?
To which a user answers:
It's maybe easy to think of return as just something that stops the code execution from going further. #IfDirectives don't care about returns, or anything else related to code execution, because they have nothing to do with code execution. They are kind of just markers that enclose different parts of code within them
I have two questions from this:
If return is just something that stops code execution from going further, then how is it different to exit? They are both flow controls playing a role to determine The Top of the Script (the Auto-execute Section). I see that many people having this problem.
If return doesn't play like a closing bracket, then why does it appear in many places one expects a closing bracket? Especially when there is no subroutine involving in, like what is described in the formal definition. Take this example in Hotkeys:
#n::
Run Notepad
return
In AHK return is what you'd learn return to be from other programming languages. It's just that control flow, and in general the legacy syntax, in AHK is different from what you might be used to from other languages.
How is return different from exit?
If we're not trying to return a value from a function, and are just interested in the control flow, there is not much difference. But the difference is there.
You could indeed end an hotkey label with exit, and it would be the same as ending it with return.
From the documentation:
"If there is no caller to which to return, Return will do an Exit instead.".
I guess it's just convention to end hotkey labels with return.
So basically said, there is difference between the two, if return isn't about to do an exit.
Well when would this be?
For example:
MsgBox, 1
function()
MsgBox, 3
return ;this does an exit
function()
{
MsgBox, 2
return ;this does a return
}
The first return has no caller to which to return to, so it will do an exit.
The second return however does have a caller to return to do, so it will return there, and then execute the 3rd message box.
If we replaced the second return with an exit, the current thread would just be exited and the 3rd message box would never have been shown.
Here's the same exact example with legacy labels:
MsgBox, 1
gosub, label
MsgBox, 3
return ;this does an exit
label:
MsgBox, 2
return ;this does a return
I'm showing this, because they're very close to hotkey labels, and hotkey labels were something you were wondering a lot about.
In this specific example, if the line MsgBox, 3 didn't exist, replacing the second return with an exit would produce the same end result. But only because code execution is about to end anyway on the first return.
If return doesn't play like a closing bracket, then why does it appear in many places one expects a closing bracket?
In AHK v1, hotkeys and hotstrings work like labels, and labels are legacy. Labels don't care about { }s like modern functions do.
So we need something to stop the code execution. Return does that.
Consider the following example:
c::
MsgBox, % "Hotkey triggered!"
gosub, run_chrome
;code execution not ended
n::
MsgBox, % "Hotkey triggered!`nRunning notepad"
Run, notepad
;code execution not ended
run_chrome:
MsgBox, % "Running chrome"
run, chrome
WinWait, ahk_exe chrome.exe
WinMove, ahk_exe chrome.exe, , 500, 500
;code execution not ended
show_system_uptime:
MsgBox, % "System uptime: " A_TickCount " ms"
;code execution not ended
We're not ending code execution at any of the labels. Because of this, code execution will bleed into other parts of the code, in this case, into the other labels.
Try running the hotkeys and see how code execution bleeds into the other labels.
Because of this, the code execution needs to be ended somehow.
If using { } was supported by labels, it would indeed be the solution to keep the code execution where it needs to be.
And in fact AHK v2 hotkeys and hotstrings are no longer labels (they're only lookalikes). In v2 using { } is actually the correct way (the script wont even run if you don't use them).
See hotkeys from the AHK v2 documentation.
Another answer above already gave a good explanation.
So, to me:_
(return vs Exit is like gosub vs goto -- return (literal meaning) vs terminate. )
return completes (ends) the current subroutine, and returns from the current subroutine back to the calling subroutine;
Exit completes (ends) the current subroutine, and terminates (/exits /ends) the current thread (which discards all the previous calling subroutine);
where subroutines are inside a thread.
(subroutines are just like function calls or label calls (or hotkey label calls);
you can visualize this as if method calls (stack frame) in Java (, you can see this when you debug in an IDE))
Quotes:
[]
Return
Returns from a subroutine to which execution had previously jumped via function-call, Gosub, Hotkey activation, GroupActivate, or other means.
https://www.autohotkey.com/docs/commands/Return.htm
[]
Exit
Exits the current thread or (if the script is not persistent) the entire script.
https://www.autohotkey.com/docs/commands/Exit.htm
[]
Gosub
Jumps to the specified label and continues execution until Return is encountered.
https://www.autohotkey.com/docs/commands/Gosub.htm
[]
Goto
Jumps to the specified label and continues execution.
https://www.autohotkey.com/docs/commands/Goto.htm
[]
main difference is gosub comes back, and goto does not.
https://www.autohotkey.com/board/topic/46160-goto-vs-gosub/
[]
A subroutine and a function are essentially the same thing
Difference between subroutine , co-routine , function and thread?
[]
A subroutine is a portion of code which can be called to perform a specific task.
Execution of a subroutine begins at the target of a label and continues until a Return or Exit is encountered.
Since the end of a subroutine depends on flow of control,
any label can act as both a Goto target and the beginning of a subroutine.
https://www.autohotkey.com/docs/misc/Labels.htm#subroutines
[]
The current thread is defined as the flow of execution invoked by the most recent event;
examples include hotkeys, SetTimer subroutines, custom menu items, and GUI events.
The current thread can be executing commands within its own subroutine or within other subroutines called by that subroutine.
https://www.autohotkey.com/docs/misc/Threads.htm
(you may try out the following code to see the difference)
^r::
MsgBox, aaa
gosub, TestLabel
MsgBox, bbb
goto, TestLabel
MsgBox, ccc
return
TestLabel:
MsgBox, inside
return
; Exit
Return returns from a function (called with funcname(...) syntax) or subroutine (called with Gosub label). Subroutines/functions can call other subroutines/functions and so it's convenient to end them with Return rather than Exit. Exit ends the current thread of execution and won't transfer flow of control back to the caller.
For functions, Return also can return a value.
Return is not analogous to a closing bracket. Closing brackets can occur only at the end of a function and imply a Return but you can also Return anywhere in a function. It is common to return in an If statement to "bail out" of a function early, perhaps if some parameter had an invalid value.

Zero and Combine in Computation Expression

I have the following computation expression builder:
type ExprBuilder() =
member this.Return(x) =
Some x
let expr = new ExprBuilder()
I understand the purpose of methods Return, Zero and Combine, but I don't understand what is the difference between expressions shown below:
let a = expr{
printfn "Hello"
return 1
} // result is Some 1
let c = expr{
return 1
printfn "Hello"
} // do not compile. Combine method required
I also don't understand why in the first case Zero method in not required for printfn statement?
In the first expression, you perform some computation that results in value 1, and that's it. You don't need a Zero in it, because Zero is only needed for return-less expressions (that's why it's called "zero" - it's what is there when nothing is there), and your expression does have a return.
To specifically answer your question, Zero is not required "for the printfn statement", because not every line within the expression gets transformed. When compiling computation expressions, the compiler breaks them up at "special" points, such as let!, do!, return, etc., leaving all the rest of the code between those points intact. In this case, your printfn call just becomes part of the code that executes before the return.
In the second expression, you perform two computations: the first one results in value 1, and second one results in Zero (which is implicitly assumed when expression lacks a return). But the whole computation expression can't have two return values, it must have one. So in order to bring results of the two computations together (one might say, combine them), you need the Combine method.
Besides Combine and Zero, you'd also need to implement Delay for this to work. Multipart (i.e. "combined") computations are also wrapped in Delay in order to allow the builder to defer evaluation and possibly drop some parts within the Combine implementation.
I recommend reading through this introduction by Scott Wlaschin, specifically part 3 about Delay and Run.

Erlang Dialyzer: only accept certain integers?

Say I have a function,foo/1, whose spec is -spec foo(atom()) -> #r{}., where #r{} is a record defined as -record(r, {a :: 1..789})., however, I have foo(a) -> 800. in my code, when I run dialyzer against it, it didn't warn me about this, (800 is not a "valid" return value for function foo/1), can I make dialyzer warn me about this?
Edit
Learn You Some Erlang says:
Dialyzer reserves the right to expand this range into a bigger one.
But I couldn't find how to disable this.
As of Erlang 18, the handling of integer ranges is done by erl_types:t_from_range/2. As you can see, there are a lot of generalizations happening to get a "safe" overapproximation of a range.
If you tried to ?USE_UNSAFE_RANGES (see the code) it is likely that your particular error would be caught, but at a terrible cost: native compilation and dialyzing of recursive integer functions would not ever finish!
The reason is that the type analysis for recursive functions uses a simple fixpoint approach, where the initial types accept the base cases and are repeatedly expanded using the recursive cases to include more values. At some point overapproximations must happen if the process is to terminate. Here is a concrete example:
fact(1) -> 1;
fact(N) -> N * fact(N - 1).
Initially fact/1 is assumed to have type fun(none()) -> none(). Using that to analyse the code, the second clause is 'failing' and only the first one is ok. Therefore after the first iteration the new type is fun(1) -> 1. Using the new type the second clause can succeed, expanding the type to fun(1|2) -> 1|2. Then fun(1|2|3) -> 1|2|6 this continues until the ?SET_LIMIT is reached in which case t_from_range stops using the individual values and type becomes fun(1..255) -> pos_integer(). The next iteration expands 1..255 to pos_integer() and then fun(pos_integer()) -> pos_integer() is a fixpoint!
Incorrect answer follows (explains the first comment below):
You should get a warning for this code if you use the -Woverspecs option. This option is not enabled by default, since Dialyzer operates under the assumption that it is 'ok' to over-approximate the return values of a function. In your particular case, however, you actually want any extra values to produce warnings.

Why does return/redo evaluate result functions in the calling context, but block results are not evaluated?

Last night I learned about the /redo option for when you return from a function. It lets you return another function, which is then invoked at the calling site and reinvokes the evaluator from the same position
>> foo: func [a] [(print a) (return/redo (func [b] [print b + 10]))]
>> foo "Hello" 10
Hello
20
Even though foo is a function that only takes one argument, it now acts like a function that took two arguments. Something like that would otherwise require the caller to know you were returning a function, and that caller would have to manually use the do evaluator on it.
Thus without return/redo, you'd get:
>> foo: func [a] [(print a) (return (func [b] [print b + 10]))]
>> foo "Hello" 10
Hello
== 10
foo consumed its one parameter and returned a function by value (which was not invoked, thus the interpreter moved on). Then the expression evaluated to 10. If return/redo did not exist you'd have had to write:
>> do foo "Hello" 10
Hello
20
This keeps the caller from having to know (or care) if you've chosen to return a function to execute. And is cool because you can do things like tail call optimization, or writing a wrapper for the return functionality itself. Here's a variant of return that prints a message but still exits the function and provides the result:
>> myreturn: func [] [(print "Leaving...") (return/redo :return)]
>> foo: func [num] [myreturn num + 10]
>> foo 10
Leaving...
== 20
But functions aren't the only thing that have behavior in do. So if this is a general pattern for "removing the need for a DO at the callsite", then why doesn't this print anything?
>> test: func [] [return/redo [print "test"]]
>> test
== [print "test"]
It just returned the block by value, like a normal return would have. Shouldn't it have printed out "test"? That's what do would...uh, do with it:
>> do [print "test"]
test
The short answer is because it is generally unnecessary to evaluate a block at the call point, because blocks in Rebol don't take parameters so it mostly doesn't matter where they are evaluated. However, that "mostly" may need some explanation...
It comes down to two interesting features of Rebol: static binding, and how do of a function works.
Static Binding and Scopes
Rebol doesn't have scoped word bindings, it has static direct word bindings. Sometimes it seems like we have lexical scope, but we really fake that by updating the static bindings each time we're building a new "scoped" code block. We can also rebind words manually whenever we want.
What that means for us in this case though, is that once a block exists, its bindings and values are static - they're not affected by where the block is physically located, or where it is being evaluated.
However, and this is where it gets tricky, function contexts are weird. While the bindings of words bound to a function context are static, the set of values assigned to those words are dynamically scoped. It's a side effect of how code is evaluated in Rebol: What are language statements in other languages are functions in Rebol, so a call to if, for instance, actually passes a block of data to the if function which if then passes to do. That means that while a function is running, do has to look up the values of its words from the call frame of the most recent call to the function that hasn't returned yet.
This does mean that if you call a function and return a block of code with words bound to its context, evaluating that block will fail after the function returns. However, if your function calls itself and that call returns a block of code with its words bound to it, evaluating that block before your function returns will make it look up those words in the call frame of the current call of your function.
This is the same for whether you do or return/redo, and affects inner functions as well. Let me demonstrate:
Function returning code that is evaluated after the function returns, referencing a function word:
>> a: 10 do do has [a] [a: 20 [a]]
** Script error: a word is not bound to a context
** Where: do
** Near: do do has [a] [a: 20 [a]]
Same, but with return/redo and the code in a function:
>> a: 10 do has [a] [a: 20 return/redo does [a]]
** Script error: a word is not bound to a context
** Where: function!
** Near: [a: 20 return/redo does [a]]
Code do version, but inside an outer call to the same function:
>> do f: function [x] [a: 10 either zero? x [do f 1] [a: 20 [a]]] 0
== 10
Same, but with return/redo and the code in a function:
>> do f: function [x] [a: 10 either zero? x [f 1] [a: 20 return/redo does [a]]] 0
== 10
So in short, with blocks there is usually no advantage to doing the block elsewhere than where it is defined, and if you want to it is easier to use another call to do instead. Self-calling recursive functions that need to return code to be executed in outer calls of the same function are an exceedingly rare code pattern that I have never seen used in Rebol code at all.
It could be possible to change return/redo so it would handle blocks as well, but it probably isn't worth the increased overhead to return/redo to add a feature that is only useful in rare circumstances and already has a better way to do it.
However, that brings up an interesting point: If you don't need return/redo for blocks because do does the same job, doesn't the same apply to functions? Why do we need return/redo at all?
How DO of a Function Works
Basically, we have return/redo because it uses exactly the same code that we use to implement do of a function. You might not realize it, but do of a function is really unusual.
In most programming languages that can call a function value, you have to pass the parameters to the function as a complete set, sort of how R3's apply function works. Regular Rebol function calling causes some unknown-ahead-of-time number of additional evaluations to happen for its arguments using unknown-ahead-of-time evaluation rules. The evaluator figures out these evaluation rules at runtime and just passes the results of the evaluation to the function. The function itself doesn't handle the evaluation of its parameters, or even necessarily know how those parameters were evaluated.
However, when you do a function value explicitly, that means passing the function value to a call to another function, a regular function named do, and then that magically causes the evaluation of additional parameters that weren't even passed to the do function at all.
Well it's not magic, it's return/redo. The way do of a function works is that it returns a reference to the function in a regular shortcut-return value, with a flag in the shortcut-return value that tells the interpreter that called do to evaluate the returned function as if it were called right there in the code. This is basically what is called a trampoline.
Here's where we get to another interesting feature of Rebol: The ability to shortcut-return values from a function is built into the evaluator, but it doesn't actually use the return function to do it. All of the functions you see from Rebol code are wrappers around the internal stuff, even return and do. The return function we call just generates one of those shortcut-return values and returns it; the evaluator does the rest.
So in this case, what really happened is that all along we had code that did what return/redo does internally, but Carl decided to add an option to our return function to set that flag, even though the internal code doesn't need return to do so because the internal code calls the internal function. And then he didn't tell anyone that he was making the option externally available, or why, or what it did (I guess you can't mention everything; who has the time?). I have the suspicion, based on conversations with Carl and some bugs we've been fixing, that R2 handled do of a function differently, in a way that would have made return/redo impossible.
That does mean that the handling of return/redo is pretty thoroughly oriented towards function evaluation, since that is its entire reason for existing at all. Adding any overhead to it would add overhead to do of a function, and we use that a lot. Probably not worth extending it to blocks, given how little we'd gain and how rarely we'd get any benefit at all.
For return/redo of a function though, it seems to be getting more and more useful the more we think about it. In the last day we've come up with all sorts of tricks that this enables. Trampolines are useful.
While the question originally asked why return/redo did not evaluate blocks, there were also formulations like: "is cool because you can do things like tail call optimization", "[can write] a wrapper for the return functionality", "it seems to be getting more and more useful the more we think about it".
I do not think these are true. My first example demonstrates a case where return/redo can really be used, an example being in the "area of expertise" of return/redo, so to speak. It is a variadic sum function called sumn:
use [result collect process] [
collect: func [:value [any-type!]] [
unless value? 'value [return process result]
append/only result :value
return/redo :collect
]
process: func [block [block!] /local result] [
result: 0
foreach value reduce block [result: result + value]
result
]
sumn: func [] [
result: copy []
return/redo :collect
]
]
This is the usage example:
>> sumn 1 * 2 2 * 3 4
== 12
Variadic functions taking "unlimited number" of arguments are not as useful in Rebol as it may look at the first sight. For example, if we wanted to use the sumn function in a small script, we would have to wrap it into a paren to indicate where it should stop collecting arguments:
result: (sumn 1 * 2 2 * 3 4)
print result
This is not any better than using a more standard (non-variadic) alternative called e.g. block-sum and taking just one argument, a block. The usage would be like
result: block-sum [1 * 2 2 * 3 4]
print result
Of course, if the function can somehow detect what is its last argument without needing enclosing paren, we really gain something. In this case we could use the #[unset!] value as the sumn stopping argument, but that does not spare typing either:
result: sumn 1 * 2 2 * 3 4 #[unset!]
print result
Seeing the example of a return wrapper I would say that return/redo is not well suited for return wrappers, return wrappers being outside of its area of expertise. To demonstrate that, here is a return wrapper written in Rebol 2 that actually is outside of return/redo's area of expertise:
myreturn: func [
{my RETURN wrapper returning the string "indefinite" instead of #[unset!]}
; the [throw] attribute makes this function a RETURN wrapper in R2:
[throw]
value [any-type!] {the value to return}
] [
either value? 'value [return :value] [return "indefinite"]
]
Testing in R2:
>> do does [return #[unset!]]
>> do does [myreturn #[unset!]]
== "indefinite"
>> do does [return 1]
== 1
>> do does [myreturn 1]
== 1
>> do does [return 2 3]
== 2
>> do does [myreturn 2 3]
== 2
Also, I do not think it is true that return/redo helps with tail call optimizations. There are examples how tail calls can be implemented without using return/redo at the www.rebol.org site. As said, return/redo was tailor-made to support implementation of variadic functions and it is not flexible enough for other purposes as far as argument passing is concerned.

Resources