Repository F# setup
open System
open System.IO
open System.Threading
open System.Threading.Tasks
open Axial
open Axial.Layers
open Axial.Console
open Axial.FileSystem
open Axial.Hosting
open Axial.Hosting.Browser
open Axial.Hosting.Node
open Axial.HttpClient
open Axial.PlatformService
open Axial.Process
open Axial.State
open Axial.Telemetry
open Axial.Telemetry.JavaScriptRef (Atomic References)
Ref<'T> is a handle to a mutable reference that can be updated atomically. It is the simplest way to manage shared state between multiple concurrent parts of your application.
open Axial
open Axial.State
AxialStateIn a functional program, state is usually passed as parameters or through the environment. However, some scenarios—like a shared counter, a cache, or a status flag—require multiple concurrent workflows to observe and update the same value.
Ref provides a high-level abstraction for thread-safe state management. It encapsulates the synchronization logic (currently implemented using lightweight per-instance locking), ensuring that you can perform complex updates without manual lock management or risking data races.
Creating a Ref
Use Ref.make to create a new reference with an initial value. Like all things in Axial, creating a Ref is an effectful operation that returns a Flow.
let counterWorkflow =
flow {
let! counter = Ref.make 0
return counter
}Use Ref.get to read the current value and Ref.set to overwrite it.
let workWithRef (counter: Ref<int>) =
flow {
let! value = Ref.get counter
do! Ref.set (value + 1) counter
return value
}
workWithRef: Ref<int> -> Flow<'a,'b,int>counter: Ref<int>Axial.State.Ref`1Represents a handle to a mutable reference that can be updated atomically. The type of the value stored in the reference.
intAn abbreviation for the CLI type . Basic Types
flow: FlowBuilderThe universal flow { } computation expression.
value: intAxial.State.RefModuleget: Ref<'T> -> Flow<'env,'none,'T>Reads the current value of the reference. The to read from. A flow that returns the current value. let readInitialValue () = flow { let! reference = Ref.make 10 return! Ref.get reference }
set: 'T -> Ref<'T> -> Flow<'env,'none,unit>Sets the value of the reference to the specified value. The new value to set. The to update. A flow that sets the value and returns unit. let replaceValue () = flow { let! reference = Ref.make 10 do! Ref.set 20 reference }
(+): ^T1 -> ^T2 -> ^T3Overloaded addition operator The first parameter. The second parameter. The result of the operation. 2 + 2 // Evaluates to 4 "Hello " + "World" // Evaluates to "Hello World"
For concurrent safety, you should use Ref.update or Ref.modify. These operations ensure that the update is atomic, preventing race conditions when multiple fibers are updating the same reference simultaneously.
Ref.update
Updates the value using a transformation function.
let increment (counter: Ref<int>) =
Ref.update (fun x -> x + 1) counter
increment: Ref<int> -> Flow<'a,'b,unit>counter: Ref<int>Axial.State.Ref`1Represents a handle to a mutable reference that can be updated atomically. The type of the value stored in the reference.
intAn abbreviation for the CLI type . Basic Types
Axial.State.RefModuleupdate: ('T -> 'T) -> Ref<'T> -> Flow<'env,'none,unit>Updates the value of the reference using the supplied function. The update function of type 'T -> 'T. The to update. A flow that updates the value and returns unit. Ref.update (fun x -> x + 1) myRef
x: int(+): ^T1 -> ^T2 -> ^T3Overloaded addition operator The first parameter. The second parameter. The result of the operation. 2 + 2 // Evaluates to 4 "Hello " + "World" // Evaluates to "Hello World"
let incrementAndGet (counter: Ref<int>) =
Ref.modify (fun x ->
let next = x + 1
next, next // (return value, new state)
) counter
incrementAndGet: Ref<int> -> Flow<'a,'b,int>counter: Ref<int>Axial.State.Ref`1Represents a handle to a mutable reference that can be updated atomically. The type of the value stored in the reference.
intAn abbreviation for the CLI type . Basic Types
Axial.State.RefModulemodify: ('T -> 'v * 'T) -> Ref<'T> -> Flow<'env,'none,'v>Updates the value of the reference using the supplied function and returns a derived value. The update function of type 'T -> 'v * 'T, returning the result before the next state. The to update. A flow that updates the value and returns the first part of the tuple returned by . Ref.modify (fun x -> "increased", x + 1) myRef
x: intnext: int(+): ^T1 -> ^T2 -> ^T3Overloaded addition operator The first parameter. The second parameter. The result of the operation. 2 + 2 // Evaluates to 4 "Hello " + "World" // Evaluates to "Hello World"
Ref.getAndSet, Ref.getAndUpdate, and Ref.updateAndGet cover the common "get the previous/updated value while
changing state" cases without writing a custom modify function:
let incrementAndGetWithHelper (counter: Ref<int>) =
Ref.updateAndGet (fun x -> x + 1) counter
let snapshotAndReset (counter: Ref<int>) =
Ref.getAndSet 0 counter
incrementAndGetWithHelper: Ref<int> -> Flow<'a,'b,int>counter: Ref<int>Axial.State.Ref`1Represents a handle to a mutable reference that can be updated atomically. The type of the value stored in the reference.
intAn abbreviation for the CLI type . Basic Types
Axial.State.RefModuleupdateAndGet: ('T -> 'T) -> Ref<'T> -> Flow<'env,'none,'T>Updates the value using the supplied function and returns the value after the update. The update function of type 'T -> 'T. The to update. A flow that returns the updated value. Ref.updateAndGet (fun x -> x + 1) myRef
x: int(+): ^T1 -> ^T2 -> ^T3Overloaded addition operator The first parameter. The second parameter. The result of the operation. 2 + 2 // Evaluates to 4 "Hello " + "World" // Evaluates to "Hello World"
snapshotAndReset: Ref<int> -> Flow<'a,'b,int>getAndSet: 'T -> Ref<'T> -> Flow<'env,'none,'T>Sets the value of the reference and returns the value it held before the update. The new value to set. The to update. A flow that returns the previous value. let replaceAndReturnPrevious () = flow { let! reference = Ref.make 10 return! Ref.getAndSet 20 reference }
let trackProgress (total: int) =
flow {
let! progress = Ref.make 0
let doStep = Ref.updateAndGet (fun p -> p + 1) progress
// Run several steps
for _ in 1 .. total do
do! doStep |> Flow.ignore
return "Complete"
}
trackProgress: int -> Flow<'a,'b,string>total: intintAn abbreviation for the CLI type . Basic Types
flow: FlowBuilderThe universal flow { } computation expression.
progress: Ref<int>Axial.State.RefModulemake: 'T -> Flow<'env,'none,Ref<'T>>Creates a new with the initial value. The initial value of the reference. A flow that creates and returns the reference. (Ref.make 10).RunSynchronously(())
doStep: Flow<'a,'b,int>updateAndGet: ('T -> 'T) -> Ref<'T> -> Flow<'env,'none,'T>Updates the value using the supplied function and returns the value after the update. The update function of type 'T -> 'T. The to update. A flow that returns the updated value. Ref.updateAndGet (fun x -> x + 1) myRef
p: int(+): ^T1 -> ^T2 -> ^T3Overloaded addition operator The first parameter. The second parameter. The result of the operation. 2 + 2 // Evaluates to 4 "Hello " + "World" // Evaluates to "Hello World"
(..): ^T -> ^T -> ^T seqThe standard overloaded range operator, e.g. [n..m] for lists, seq {n..m} for sequences The start value of the range. The end value of the range. The sequence spanning the range. [1..4] // Evaluates to [1; 2; 3; 4] [1.5..4.4] // Evaluates to [1.5; 2.5; 3.5] ['a'..'d'] // Evaluates to ['a'; 'b'; 'c'; 'd'] [|1..4|] // Evaluates to an array [|1; 2; 3; 4|] { 1..4 } // Evaluates to a sequence [1; 2; 3; 4])
(|>): 'T1 -> ('T1 -> 'U) -> 'UApply a function to a value, the value being on the left, the function on the right The argument. The function. The function result. let doubleIt x = x * 2 3 |> doubleIt // Evaluates to 6
Axial.Flowignore: Flow<'env,'error,'value> -> Flow<'env,'error,unit>Maps the successful value of a synchronous flow to unit. The source flow. A flow that succeeds with unit instead of the original value. let flow = Flow.succeed 42 |> Flow.ignore
| Function | Signature | Description |
|---|---|---|
make |
'T -> Flow<'env, 'none, Ref<'T>> |
Creates a new reference with an initial value. |
get |
Ref<'T> -> Flow<'env, 'none, 'T> |
Reads the current value of the reference. |
set |
'T -> Ref<'T> -> Flow<'env, 'none, unit> |
Sets the value of the reference to a new value. |
update |
('T -> 'T) -> Ref<'T> -> Flow<'env, 'none, unit> |
Atomically updates the value using the supplied function. |
modify |
('T -> 'v * 'T) -> Ref<'T> -> Flow<'env, 'none, 'v> |
Atomically updates the value and returns a derived result. |
getAndSet |
'T -> Ref<'T> -> Flow<'env, 'none, 'T> |
Sets the value and returns the value it held before the update. |
getAndUpdate |
('T -> 'T) -> Ref<'T> -> Flow<'env, 'none, 'T> |
Updates the value and returns the value it held before the update. |
updateAndGet |
('T -> 'T) -> Ref<'T> -> Flow<'env, 'none, 'T> |
Updates the value and returns the value after the update. |

