feat: add Actor.callAsync — request-reply returning Async<'Reply>#32
Merged
Conversation
Reverse companion to the rc.10 Async→ActorOp bridge: rc.10 let you do!
an Async inside actor { }; callAsync lets you let! an actor call inside
async { } on all targets.
On BEAM, call is CPS (ActorOp = { Run: ('T -> unit) -> unit }) and cannot
be let!-bound in async { }. callAsync wraps the CPS call in an async and
captures the reply — the Run continuation fires synchronously once the
reply arrives, since BEAM processes block natively. On non-BEAM targets
ActorOp = Async, so callAsync is a direct alias for call.
Purely additive; existing call/callWithTimeout and all tests unchanged.
Downstream, Fable.Reactive's mapActor operator needs this for BEAM
support (it does let! result = call inside an async { } observer).
Adds actor_call_async_test covering let! callAsync from inside async { }.
Green on .NET, Python, and BEAM (28/28 each).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds
Actor.callAsync— a request-reply that returnsAsync<'Reply>so it can belet!-bound from inside anasync { }context on all targets. This is the reverse companion to the rc.10 Async→ActorOp bridge:do!anAsyncinsideactor { }.callAsynclets youlet!an actor call insideasync { }.Downstream, Fable.Reactive's
mapActoroperator doeslet! result = Actor.call actor xinside anasync { }observer, which fails to compile on BEAM becausecallreturns the CPSActorOp, notAsync.callAsyncfixes that.Implementation (
src/Fable.Actor/Actor.fs)Added immediately after
callin both platform branches, mirroring thecall/callWithTimeoutordering so the branches stay structurally in sync:#if FABLE_COMPILER_BEAM): wraps the CPScallin anasync, runs itsRuncontinuation, and returns the captured reply. BEAM processes block natively, so the continuation fires synchronously once the reply arrives.#else):ActorOp = Async, socallAsyncis a direct alias forcall.Purely additive and backward-compatible —
call/callWithTimeoutand all existing tests are untouched.Tests
Adds
actor_call_async_test(test/ActorTest.fs, registered inProgram.fs): from inside anasync { }block it doeslet! reply = Actor.callAsync server GetCountagainst a counter server started withActor.start, and asserts the reply. The server is a distinct process from the caller, socallAsyncdoes not deadlock on BEAM.just test— all three targets green:Formatted with
dotnet fantomas src test(max line length 120). Changelog bump intentionally omitted (handled automatically at release).🤖 Generated with Claude Code