Skip to content
Orleans.FSharp
Orleans.FSharp logo

Orleans.FSharp

Build Orleans grains in idiomatic F#. User-authored API records, immutable state transitions, and zero C# stubs — with transactions, event sourcing, and streaming replies as first-class operations.

Microsoft Orleans is one of the strongest virtual actor runtimes on .NET. But in raw C# style, F# teams often end up with mutable state classes, attribute noise, and repetitive infrastructure code.

Orleans.FSharp keeps Orleans underneath while giving you a functional-first authoring model: a grain’s public surface is a plain F# record of functions, its behavior is a set of explicit state transitions, and there is no application interface, attribute, or code-generation bridge to write.

type ICounterGrain =
inherit IGrainWithStringKey
abstract Increment: unit -> Task<int>
abstract Value: unit -> Task<int>
// ...plus a C# shim project in the solution, because Orleans'
// proxy source generator does not run on F# projects.
type CounterGrain() =
inherit Grain()
let mutable count = 0
interface ICounterGrain with
member _.Increment() =
count <- count + 1
Task.FromResult count
member _.Value() = Task.FromResult count

Orleans.FSharp — the functional grain runtime

Section titled “Orleans.FSharp — the functional grain runtime”
type CounterApi =
{ increment: unit -> Task<int>
value: unit -> Task<int> }
let counterContract =
contract<string, CounterApi> {
grainType "counter"
version 1
stringKey
readOnly (_.value)
}
let counter =
grainFor counterContract {
defaultState (fun () -> 0)
handle (_.increment) (fun _ctx n () -> task { return n + 1, n + 1 })
handleQuery (_.value) (fun _ctx n () -> task { return n })
}

And the call site is the record itself — no proxy interface, no cast:

let api = FunctionalGrain.ref counterContract factory "my-counter"
let! n = api.increment ()

Same two operations, same reply types — but state transitions are explicit values, the compiler checks every handler against the record’s field types, and sealing the definition verifies that each operation has exactly one handler. No interface, no shim project, no proxy generation.

Metric Value
Test suite Unit, integration, provider, rolling-update, and persisted-fixture coverage
Orleans support 10.1.0 – 10.3.1, CI-tested at both ends of the range
Feature tour 16 Orleans capabilities demonstrated live in examples/feature-tour
Production package set 6 — core, runtime, abstractions, testing, analyzers, templates
License MIT
Dispatch overhead under 5% versus calling the handler function directly, measured over 1,000,000 iterations
Terminal window
dotnet new install Orleans.FSharp.Templates@5.0.1
dotnet new orleans-fsharp -n MyApp
cd MyApp
dotnet build && dotnet test

A working Orleans.FSharp 5.0.1 solution with a counter grain, FsCheck property tests, and silo configuration. The packaged template references the matching 5.0.1 packages.

Orleans.FSharp

Core developer surface: grainContract, grainFor, journaledGrainFor, observers, streaming, logging, reminders, timers, and serialization.

Orleans.FSharp.Runtime

Hosting and client runtime: AddFunctionalGrain registration, siloConfig {} and clientConfig {} builders for production setup.

Orleans.FSharp.Abstractions

The fixed functional transport: request envelopes, protocol tokens, and precompiled Orleans proxies — reference it once, generate nothing.

Orleans.FSharp.Testing

TestHarness, WebTestHarness, MockGrainFactory, GrainArbitrary, and log capture utilities.

Orleans.FSharp.Analyzers

F# safety checks (for example OF0001 for async { }) run through the fsharp-analyzers CLI, with explicit opt-out where needed.

Orleans.FSharp.Templates

The current functional solution template, with exact references to the matching release packages.

The current wrapped and composed surface includes:

  • Grain lifecycle: activate, deactivate, per-stage onLifecycle hooks
  • State persistence: Memory, Redis, Azure Blob/Table, Cosmos, DynamoDB, ADO.NET
  • Streaming: Memory, Event Hubs, Azure Queue, broadcast channels — plus implicit subscriptions (onStream / onBroadcast) and Orleans 10.3+ stateless-worker competing consumers for onStream
  • Streaming replies: API record fields returning IAsyncEnumerable<'T>, riding Orleans’ own extension — await foreach-able from C#
  • Reentrancy & concurrency: reentrant and per-request mayInterleave as contract operations; statelessWorker as a definition operation
  • Placement strategies: placement (PreferLocal, ActivationCountBased, ResourceOptimized, Random) as a definition operation
  • Event sourcing: journaledGrainFor — pure apply fold over Orleans’ LogStorage/StateStorage log-consistency providers, with typed custom snapshots
  • Transactions: distributed ACID via transactional contract operations and transactionalStateFrom facets
  • Contract versioning: exact by default; opt-in tolerance with acceptsVersions and per-operation sinceVersion
  • Observers & filters: codegen-free functional observers, incoming/outgoing call filters, request context propagation
  • TLS/mTLS: Certificate-based silo and client encryption
  • Health checks & OpenTelemetry: Production observability out of the box
  • Kubernetes clustering: native K8s support with namespace and clustering configuration