He-Pin opened a new issue, #3683: URL: https://github.com/apache/pekko/issues/3683
### Motivation Adding `thenStop` directly to `ReplyEffect` also exposes `Effect.stash().thenStop()` in both persistence models and both DSLs. This stashes the current command and immediately stops the actor, discarding that command without a reply. Enforced replies do not reject it because stashing already returns a `ReplyEffect`. This was identified in the [review of #3682](https://github.com/apache/pekko/pull/3682#discussion_r4366555016). Instead of accepting the combination or throwing when the effect is constructed, we can express the permitted operations through stages in the public API. ### Proposed staged API Use a session-type-inspired staged API (also commonly described as typestate): each returned type exposes only the operations supported at that stage. This is a restriction on effect construction, not a full session type system for actor communication. For `thenStop`, introduce `ReplyEffectWithStop` as a subtype of `ReplyEffect`. The name is provisional: ```scala trait ReplyEffect[+Event, State] extends Effect[Event, State] { def thenUnstashAll(): ReplyEffect[Event, State] } trait ReplyEffectWithStop[+Event, State] extends ReplyEffect[Event, State] { def thenStop(): ReplyEffectWithStop[Event, State] override def thenUnstashAll(): ReplyEffectWithStop[Event, State] } ``` | Operation | Returned stage | | --- | --- | | `reply`, `thenReply` | `ReplyEffectWithStop` | | `noReply`, `thenNoReply` | `ReplyEffectWithStop` | | `stash` | `ReplyEffect` | The base `ReplyEffect` must not declare `thenStop`: a subtrait cannot hide an inherited operation. Scala and Java, and event sourcing and durable state, should follow the same rules. Public traits should contain declarations; concrete effect implementations implement the methods. ### Expected behavior and boundaries - `reply(...).thenStop()` and `persist(...).thenReply(...).thenStop()` compile. - `reply(...).thenUnstashAll().thenStop()` retains the stop capability. - `stash().thenStop()` and `stash().thenUnstashAll().thenStop()` do not compile. - A helper that may stash returns `ReplyEffect`, so its caller cannot append `thenStop`. A helper whose callers need that capability must explicitly return `ReplyEffectWithStop`. - `noReply` deliberately opts out of replying and can still be followed by stopping. - This only constrains the current effect chain. Stopping can still discard commands stashed by earlier commands; it cannot prove that the actor's stash is empty or that a recipient has processed a reply. ### Compatibility and validation An isolated prototype using the real effect sources compiled on Scala 2.13 and Java, accepted legal chains and rejected illegal ones. Directly narrowing existing factory return types changes JVM descriptors: a Java client compiled against the old `EffectFactories.noReply(): ReplyEffect` failed with `NoSuchMethodError` on that prototype. A separate one-method bridge experiment preserved that call, but introduced compatibility machinery. For the proposed 2.0.0 change, prefer the direct staged API and explicitly document the intentional binary incompatibility, rather than adding bridge machinery. Any MiMa exceptions must identify the exact changed signatures and the 2.0.0 rationale. Full repository validation remains required; the isolated prototype is not a substitute for CI. #3682 is being revised as the first concrete application. Once it is merged, a separate `thenRun` proposal should follow the same stage design, including whether callbacks are supported after stashing and how capability is preserved through callback chaining. Avoid introducing `thenRun` into #3682. Acceptance includes positive and negative compilation coverage, runtime reply/stop ordering, Scala/Java parity, documentation, `@since 2.0.0`, and remote CI including binary compatibility checks with only the agreed intentional changes excluded. ### Prior art [jOOQ's DSL API](https://www.jooq.org/doc/latest/manual/sql-building/dsl-api/) uses an interface hierarchy to express legal construction sequences. Its [guidance on step types](https://www.jooq.org/doc/latest/manual/reference/dont-do-this/dont-do-this-jooq-step-types/) also highlights the composition and compatibility costs of exposing those types in helper signatures. Those tradeoffs should be explicit in Pekko's design. Refs #3682. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected] --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
