ArgyleConcepts.FSharp.Actor 1.0.0

dotnet add package ArgyleConcepts.FSharp.Actor --version 1.0.0
                    
NuGet\Install-Package ArgyleConcepts.FSharp.Actor -Version 1.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="ArgyleConcepts.FSharp.Actor" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="ArgyleConcepts.FSharp.Actor" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="ArgyleConcepts.FSharp.Actor" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add ArgyleConcepts.FSharp.Actor --version 1.0.0
                    
#r "nuget: ArgyleConcepts.FSharp.Actor, 1.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package ArgyleConcepts.FSharp.Actor@1.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=ArgyleConcepts.FSharp.Actor&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=ArgyleConcepts.FSharp.Actor&version=1.0.0
                    
Install as a Cake Tool

ArgyleConcepts.FSharp.Actor

An Elm-style (Model–View–Update) actor for F# on .NET, built on System.Threading.Channels and task { }.

A single loop owns the model and processes one message at a time. Update is a pure function from a message and the current model to a new model, the commands to start, and the external messages to emit. Side effects run as commands or subscriptions, never inside Update, so the loop never blocks on I/O.

  • Commands start in order but are not awaited by the loop. Each returns the messages it produced, which are queued when it completes. Commands can therefore finish, and report back, out of order.
  • Subscriptions are long-running sources, keyed by name, that the model calls for. After every update the actor starts sources whose key is new and cancels sources whose key has gone.
  • External messages are emitted on an IAsyncEnumerable returned by Actor.start. The stream completes when the actor stops, or faults with the exception when the actor fails.
  • Status decides from the model whether the actor keeps running, stops cleanly, or fails.
  • Shutdown is graceful: Actor.stop closes the mailbox, already-queued messages are still processed, and in-flight work is given ShutdownTimeout to finish before the actor's token is cancelled. Actor.stopAndDrain also waits for work that outlives that timeout.

Install

dotnet add package ArgyleConcepts.FSharp.Actor

Targets .NET 8 and .NET 10.

Example

open System
open System.Threading
open System.Threading.Tasks
open ArgyleConcepts.FSharp.Actor

type Msg =
    | Increment
    | Saved of int
    | SaveFailed of exn
    | Tick

type Cmd = Save of int

type Event =
    | CountChanged of int
    | Persisted of int

type Model = { Count: int; Saved: int; Ticking: bool }

let update msg model =
    match msg with
    | Increment ->
        let count = model.Count + 1
        { model with Count = count }, [ Save count ], [ CountChanged count ]
    | Saved count -> { model with Saved = model.Saved + 1 }, [], [ Persisted count ]
    | SaveFailed _ -> model, [], []
    | Tick -> model, [], []

let execute (ct: CancellationToken) cmd : Task<Msg list> =
    task {
        match cmd with
        | Save count ->
            do! Task.Delay(10, ct)
            return [ Saved count ]
    }

let ticker: Subscription<Msg> =
    { Key = "ticker"
      Start =
        fun post ct ->
            task {
                while not ct.IsCancellationRequested && post Tick do
                    do! Task.Delay(TimeSpan.FromSeconds 1.0, ct)
            } }

let actor, events =
    Actor.start
        CancellationToken.None
        { Init = { Count = 0; Saved = 0; Ticking = true }, [], []
          Update = update
          Execute = execute
          OnCommandFailure = SaveFailed
          Subscribe = fun model -> if model.Ticking then [ ticker ] else []
          Status = fun model -> if model.Saved = 3 then ActorStatus.Stopped else ActorStatus.Running
          ShutdownTimeout = Actor.defaultShutdownTimeout }

for _ in 1..3 do
    Actor.post actor Increment |> ignore

task {
    let enumerator = events.GetAsyncEnumerator()
    let mutable more = true

    while more do
        let! next = enumerator.MoveNextAsync()

        if next then printfn "%A" enumerator.Current else more <- false

    do! Actor.completion actor
}
|> _.Wait()

API

Function Description
Actor.start ct config Starts an actor and returns its handle and stream of external messages. Cancelling ct aborts the actor.
Actor.post actor msg Queues a message. Returns false once the mailbox has closed.
Actor.model actor Reads a consistent snapshot of the current model from any thread.
Actor.stop actor Closes the mailbox and shuts down gracefully once queued messages are processed.
Actor.stopAndDrain actor Stops the actor and waits for every started command, even past the shutdown timeout.
Actor.completion actor A task that completes when the loop has exited and cleanup has finished.

Pull request validation

FSharp.Actor PR Validation uses azure-pipelines.yml to validate GitHub pull requests into main through Azure Pipelines in the ArgyleConceptsLLC Azure DevOps organization. It does not run on pushes or publish packages. To reproduce it locally, run from the repository root:

dotnet restore ArgyleConcepts.FSharp.Actor.slnx
dotnet tool restore
dotnet build ArgyleConcepts.FSharp.Actor.slnx --configuration Release --no-restore
dotnet fsi eng/fsharp-analysis/Run.fsx
dotnet test --solution ArgyleConcepts.FSharp.Actor.slnx --configuration Release --no-build --no-restore
dotnet fantomas check .
bash eng/verify-packages.sh

eng/verify-packages.sh packs the library, checks the package metadata, symbols and SourceLink mappings against the current commit, and runs a fresh consumer against the packed package.

Releasing

ArgylePackageVersion in Directory.Build.props is the package version. FSharp.Actor Release uses azure-release.yml and is run manually. By default it validates and retains the package and symbols as the packages artifact without publishing. A run on main with Publish verified packages to NuGet.org enabled pushes that artifact to NuGet.org through the shared fsharp-minimalapi-nuget environment and fsharp-minimalapi-nuget-release variable group. After publishing, tag the packaged commit with the version and create a GitHub release.

License

MIT

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0 323 10/6/2026