Logo Mibo

AVal Module

Core operations for creating and transforming adaptive values. Adaptive values automatically track dependencies and recompute only when their inputs change.

Mibo.Adaptive provides incremental computation with a pull-evaluate model. A write bumps the version of its source and the write generation of its graph. A read version-checks its dependencies and recomputes only when one moved; the write-generation cache keeps repeated reads at the same generation O(1) per node.

Performance Guidance:

  • Use map, map2 for 1-2 dependencies (most common case)
  • Use mapN, reduce, sum for N dependencies (avoids O(N) node chains)

Example

Basic usage:

 let x = CVal.create 1
 let y = CVal.create 2
 let sum = AVal.map2 (+) (CVal.value x) (CVal.value y)
 printfn "%d" (AVal.getValue sum)  // 3
 x.Set(10)
 printfn "%d" (AVal.getValue sum)  // 12
val x: obj
val y: obj
val sum: obj
val printfn: format: Printf.TextWriterFormat<'T> -> 'T

Types

Type Description

ExternalValueNode<'T>

An adaptive value whose content is supplied by an external snapshot function, re-read only when invalidated via the handle returned by AVal.ofExternal (FDA AVal.ofExternal parity, MAPA-DESIGN §1.1). Not invalidated → reads are O(1): no re-read, no comparison, no allocation. The invalidate handle is O(1) to call and thread-safe (a foreign-thread call posts to the owner context, the cval.Post pattern); the re-read happens on the next read on the owner thread.

Functions and values

Function or value Description

bind f value

Full Usage: bind f value

Parameters:
Returns: aval<'U>
Modifiers: inline
Type parameters: 'T, 'U
f : 'T -> aval<'U>
value : aval<'T>
Returns: aval<'U>

bind2 f a b

Full Usage: bind2 f a b

Parameters:
Returns: aval<'V>
Modifiers: inline
Type parameters: 'T, 'U, 'V

Adaptively applies the mapping to the two values and adaptively depends on the adaptive value the mapping returns (FDA AVal.bind2 parity). When an input changes, the previously returned inner value is dropped and the mapping selects a new one.

f : 'T -> 'U -> aval<'V>
a : aval<'T>
b : aval<'U>
Returns: aval<'V>

bind3 f a b c

Full Usage: bind3 f a b c

Parameters:
Returns: aval<'W>
Modifiers: inline
Type parameters: 'T, 'U, 'V, 'W

Adaptively applies the mapping to the three values and adaptively depends on the adaptive value the mapping returns (FDA AVal.bind3 parity).

f : 'T -> 'U -> 'V -> aval<'W>
a : aval<'T>
b : aval<'U>
c : aval<'V>
Returns: aval<'W>

bindTask f value

Full Usage: bindTask f value

Parameters:
Returns: Task<'U>
Modifiers: inline
Type parameters: 'T, 'U
f : 'T -> Task<'U>
value : aval<'T>
Returns: Task<'U>

bindTaskResult f value

Full Usage: bindTaskResult f value

Parameters:
Returns: aval<Task<'U>>
Modifiers: inline
Type parameters: 'T, 'U
f : 'T -> Task<'U>
value : aval<Task<'T>>
Returns: aval<Task<'U>>

bindValueTask f value

Full Usage: bindValueTask f value

Parameters:
Returns: ValueTask<'U>
Modifiers: inline
Type parameters: 'T, 'U
f : 'T -> ValueTask<'U>
value : aval<'T>
Returns: ValueTask<'U>

bindValueTaskResult f value

Full Usage: bindValueTaskResult f value

Parameters:
Returns: aval<ValueTask<'U>>
Modifiers: inline
Type parameters: 'T, 'U
f : 'T -> ValueTask<'U>
value : aval<ValueTask<'T>>
Returns: aval<ValueTask<'U>>

constant value

Full Usage: constant value

Parameters:
    value : 'T - The constant value.

Returns: aval<'T> An adaptive value that always returns the given value.
Modifiers: inline
Type parameters: 'T

Creates a constant adaptive value that never changes.

Constant values have zero overhead - they never recompute and don't track dependencies. Use this for values that are known at creation time and will never change.

value : 'T

The constant value.

Returns: aval<'T>

An adaptive value that always returns the given value.

Example

 let pi = AVal.constant 3.14159
 let doubled = AVal.map (fun x -> x * 2.0) pi
val pi: obj
val doubled: obj

custom compute

Full Usage: custom compute

Parameters:
    compute : unit -> 'T

Returns: aval<'T>
Modifiers: inline
Type parameters: 'T

Creates a custom adaptive value using the given computation (FDA AVal.custom parity; deviation: FDA passes an AdaptiveToken, we have no token, so the computation takes unit). Callers are responsible for removing inputs that are no longer needed.

compute : unit -> 'T
Returns: aval<'T>

delay create

Full Usage: delay create

Parameters:
    create : unit -> 'T

Returns: aval<'T>
Modifiers: inline
Type parameters: 'T

Creates a constant adaptive value using the given create function (FDA AVal.delay parity). The function runs at most once, on the first read; later reads return the cached value.

create : unit -> 'T
Returns: aval<'T>
Example

 let v = AVal.delay (fun () -> expensiveComputation ())
val v: obj

force value

Full Usage: force value

Parameters:
Returns: 'T
Modifiers: inline
Type parameters: 'T

Evaluates the given adaptive value and returns its current value (FDA AVal.force parity; the same as getValue).

value : aval<'T>
Returns: 'T

getValue value

Full Usage: getValue value

Parameters:
Returns: 'T
Modifiers: inline
Type parameters: 'T
value : aval<'T>
Returns: 'T

getValueTask value

Full Usage: getValueTask value

Parameters:
Returns: Task<'T>
Modifiers: inline
Type parameters: 'T
value : aval<'T>
Returns: Task<'T>

getValueValueTask value

Full Usage: getValueValueTask value

Parameters:
Returns: ValueTask<'T>
Modifiers: inline
Type parameters: 'T
value : aval<'T>
Returns: ValueTask<'T>

init value

Full Usage: init value

Parameters:
    value : 'T

Returns: cval<'T>
Modifiers: inline
Type parameters: 'T

Creates a changeable value initially holding the given value (FDA AVal.init parity; the same as CVal.create).

value : 'T
Returns: cval<'T>

map f value

Full Usage: map f value

Parameters:
    f : 'T -> 'U - The function to apply to the value.
    value : aval<'T> - The source adaptive value.

Returns: aval<'U> A new adaptive value that applies the function to the source.
Modifiers: inline
Type parameters: 'T, 'U

Transforms an adaptive value using a mapping function.

The function is called lazily - only when the result is read and the source has changed. The result is cached until the source changes.

f : 'T -> 'U

The function to apply to the value.

value : aval<'T>

The source adaptive value.

Returns: aval<'U>

A new adaptive value that applies the function to the source.

Example

 let celsius = CVal.create 20.0
 let fahrenheit = AVal.map (fun c -> c * 9.0/5.0 + 32.0) (CVal.value celsius)
val celsius: obj
val fahrenheit: obj

map2 f left right

Full Usage: map2 f left right

Parameters:
    f : 'T -> 'U -> 'V - The function to combine the two values.
    left : aval<'T> - The first adaptive value.
    right : aval<'U> - The second adaptive value.

Returns: aval<'V> A new adaptive value that combines both inputs.
Modifiers: inline
Type parameters: 'T, 'U, 'V

Combines two adaptive values using a mapping function.

Recomputes only when either input changes. Both inputs are read in a single evaluation.

f : 'T -> 'U -> 'V

The function to combine the two values.

left : aval<'T>

The first adaptive value.

right : aval<'U>

The second adaptive value.

Returns: aval<'V>

A new adaptive value that combines both inputs.

Example

 let width = CVal.create 10.0
 let height = CVal.create 20.0
 let area = AVal.map2 (*) (CVal.value width) (CVal.value height)
val width: obj
val height: obj
val area: obj

map3 f a b c

Full Usage: map3 f a b c

Parameters:
    f : 'A -> 'B -> 'C -> 'T - The function to combine the three values.
    a : aval<'A> - The first adaptive value.
    b : aval<'B> - The second adaptive value.
    c : aval<'C> - The third adaptive value.

Returns: aval<'T> A new adaptive value that combines all three inputs.
Modifiers: inline
Type parameters: 'A, 'B, 'C, 'T

Combines three adaptive values using a mapping function. Recomputes only when one of the three inputs changes.

f : 'A -> 'B -> 'C -> 'T

The function to combine the three values.

a : aval<'A>

The first adaptive value.

b : aval<'B>

The second adaptive value.

c : aval<'C>

The third adaptive value.

Returns: aval<'T>

A new adaptive value that combines all three inputs.

Example

 let r = CVal.create 255
 let g = CVal.create 128
 let b = CVal.create 64
 let color = AVal.map3 (fun r g b -> sprintf "#%02X%02X%02X" r g b)
                       (CVal.value r) (CVal.value g) (CVal.value b)
val r: obj
val g: obj
val b: obj
val color: obj
val sprintf: format: Printf.StringFormat<'T> -> 'T

map4 f a b c d

Full Usage: map4 f a b c d

Parameters:
    f : 'A -> 'B -> 'C -> 'D -> 'T - The function to combine the four values.
    a : aval<'A> - The first adaptive value.
    b : aval<'B> - The second adaptive value.
    c : aval<'C> - The third adaptive value.
    d : aval<'D> - The fourth adaptive value.

Returns: aval<'T> A new adaptive value that combines all four inputs.
Modifiers: inline
Type parameters: 'A, 'B, 'C, 'D, 'T

Combines four adaptive values using a mapping function. Recomputes only when one of the four inputs changes.

f : 'A -> 'B -> 'C -> 'D -> 'T

The function to combine the four values.

a : aval<'A>

The first adaptive value.

b : aval<'B>

The second adaptive value.

c : aval<'C>

The third adaptive value.

d : aval<'D>

The fourth adaptive value.

Returns: aval<'T>

A new adaptive value that combines all four inputs.

Example

 let x = CVal.create 0.0
 let y = CVal.create 0.0
 let width = CVal.create 100.0
 let height = CVal.create 50.0
 let rect = AVal.map4 (fun x y w h -> { X = x; Y = y; Width = w; Height = h })
                      (CVal.value x) (CVal.value y) (CVal.value width) (CVal.value height)
val x: obj
val y: obj
val width: obj
val height: obj
val rect: obj

mapN compute deps

Full Usage: mapN compute deps

Parameters:
    compute : 'T[] -> 'U - A function that receives an array of all current values and produces the result.
    deps : aval<'T>[] - An array of adaptive values to combine.

Returns: aval<'U> A new adaptive value that combines all inputs.
Modifiers: inline
Type parameters: 'T, 'U

Combines N adaptive values of the same type using a function that receives all values as an array. Optimized for wide fan-in patterns where many inputs feed into a single computation.

When to use: Use mapN when you need to combine 5+ values of the same type, or when you need access to all values as an array (e.g., for aggregation, filtering).

Performance: Uses a single node instead of O(N) nodes from chained map2 calls. This provides 3-100× speedup for wide fan-in patterns (10-500 inputs). Memory usage is constant regardless of input count.

Note: The array passed to the compute function is reused by the node and is valid only during the call. Do not retain it. If you only need a reduction (sum, min, max, etc.), prefer reduce for better performance.

compute : 'T[] -> 'U

A function that receives an array of all current values and produces the result.

deps : aval<'T>[]

An array of adaptive values to combine.

Returns: aval<'U>

A new adaptive value that combines all inputs.

Example

 // Combine 10 sensor readings into their average
 let sensors = Array.init 10 (fun i -> CVal.create (float i))
 let deps = sensors |> Array.map (fun s -> CVal.value s :> IAdaptiveValue<float>)
 let average = AVal.mapN (fun values -> Array.average values) deps
val sensors: obj array
module Array from Microsoft.FSharp.Collections
val init: count: int -> initializer: (int -> 'T) -> 'T array
val i: int
Multiple items
val float: value: 'T -> float (requires member op_Explicit)

--------------------
type float = System.Double

--------------------
type float<'Measure> = float
val deps: obj array
val map: mapping: ('T -> 'U) -> array: 'T array -> 'U array
val s: obj
val average: obj
val average: array: 'T array -> 'T (requires member (+) and member DivideByInt and member Zero)

mapTask f value

Full Usage: mapTask f value

Parameters:
    f : 'T -> Task<'U> - The async function to apply.
    value : aval<'T> - The source adaptive value.

Returns: aval<Task<'U>> An adaptive value containing Tasks of the result type.
Modifiers: inline
Type parameters: 'T, 'U

Transforms an adaptive value using an async function that returns a Task.

f : 'T -> Task<'U>

The async function to apply.

value : aval<'T>

The source adaptive value.

Returns: aval<Task<'U>>

An adaptive value containing Tasks of the result type.

mapTaskResult f value

Full Usage: mapTaskResult f value

Parameters:
Returns: aval<Task<'U>>
Modifiers: inline
Type parameters: 'T, 'U
f : 'T -> 'U
value : aval<Task<'T>>
Returns: aval<Task<'U>>

mapValueTask f value

Full Usage: mapValueTask f value

Parameters:
Returns: aval<ValueTask<'U>>
Modifiers: inline
Type parameters: 'T, 'U
f : 'T -> ValueTask<'U>
value : aval<'T>
Returns: aval<ValueTask<'U>>

mapValueTaskResult f value

Full Usage: mapValueTaskResult f value

Parameters:
Returns: aval<ValueTask<'U>>
Modifiers: inline
Type parameters: 'T, 'U
f : 'T -> 'U
value : aval<ValueTask<'T>>
Returns: aval<ValueTask<'U>>

ofExternal read

Full Usage: ofExternal read

Parameters:
    read : unit -> 'T

Returns: aval<'T> * (unit -> unit)
Modifiers: inline
Type parameters: 'T (requires equality)

Creates an adaptive value from an external snapshot function and an invalidate handle (FDA AVal.ofExternal parity, MAPA-DESIGN §1.1). The read function runs at most once per invalidate, on the next read; when not invalidated, reads are O(1) and allocate nothing. The handle is O(1) to call and thread-safe (a foreign-thread call is posted to the owner context and applied at the next graph operation).

read : unit -> 'T
Returns: aval<'T> * (unit -> unit)
Example

 let mutable current = 0
 let value, invalidate = AVal.ofExternal (fun () -> current)
 current <- 42
 invalidate ()
 printfn "%d" (AVal.getValue value)  // 42
val mutable current: int
val value: obj
val invalidate: (unit -> obj)
val printfn: format: Printf.TextWriterFormat<'T> -> 'T

reduce init reduce deps

Full Usage: reduce init reduce deps

Parameters:
    init : 'T - The initial/identity value for the reduction (e.g., 0 for sum, 1 for product).
    reduce : 'T -> 'T -> 'T - A binary function to combine values (must be associative for correct results).
    deps : aval<'T>[] - An array of adaptive values to reduce.

Returns: aval<'T> A new adaptive value containing the reduction result.
Modifiers: inline
Type parameters: 'T

Reduces N adaptive values using a binary operation and initial value. Optimized for wide fan-in aggregation patterns (sum, product, min, max, etc.).

When to use: Use reduce for aggregations like sum, product, min, max, string concatenation, or any fold-like operation over adaptive values.

Performance: More efficient than mapN for reductions because it doesn't allocate an intermediate array. Uses a single node instead of O(N) nodes from chained map2. Provides 3-100× speedup for wide fan-in patterns.

Empty array behavior: Returns the init value when deps is empty.

init : 'T

The initial/identity value for the reduction (e.g., 0 for sum, 1 for product).

reduce : 'T -> 'T -> 'T

A binary function to combine values (must be associative for correct results).

deps : aval<'T>[]

An array of adaptive values to reduce.

Returns: aval<'T>

A new adaptive value containing the reduction result.

Example

 // Sum of prices
 let prices = [| CVal.create 10.0; CVal.create 20.0; CVal.create 15.0 |]
 let deps = prices |> Array.map (fun p -> CVal.value p :> IAdaptiveValue<float>)
 let total = AVal.reduce 0.0 (+) deps

 // Product
 let product = AVal.reduce 1.0 (*) deps

 // Maximum (using System.Double.MinValue as identity)
 let maxPrice = AVal.reduce System.Double.MinValue max deps
val prices: obj array
val deps: obj array
module Array from Microsoft.FSharp.Collections
val map: mapping: ('T -> 'U) -> array: 'T array -> 'U array
val p: obj
Multiple items
val float: value: 'T -> float (requires member op_Explicit)

--------------------
type float = System.Double

--------------------
type float<'Measure> = float
val total: obj
val product: obj
val maxPrice: obj
namespace System
type Double = member CompareTo: value: float -> int + 1 overload member Equals: obj: float -> bool + 1 overload member GetHashCode: unit -> int member GetTypeCode: unit -> TypeCode member ToString: unit -> string + 3 overloads member TryFormat: utf8Destination: Span<byte> * bytesWritten: byref<int> * ?format: ReadOnlySpan<char> * ?provider: IFormatProvider -> bool + 1 overload static member (<) : left: float * right: float -> bool static member (<=) : left: float * right: float -> bool static member (<>) : left: float * right: float -> bool static member (=) : left: float * right: float -> bool ...
<summary>Represents a double-precision floating-point number.</summary>
field float.MinValue: float = -1.79769313486e+308
val max: e1: 'T -> e2: 'T -> 'T (requires comparison)

sum deps

Full Usage: sum deps

Parameters:
    deps : aval<int>[] - An array of adaptive integer values to sum.

Returns: aval<int> A new adaptive value containing the sum.
Modifiers: inline

Sums N adaptive integer values. Convenience function equivalent to reduce 0 (+).

Performance: Uses a single node instead of O(N) nodes from chained additions. Provides 3-100× speedup for summing many values (10-500 inputs).

Empty array behavior: Returns 0 when deps is empty.

deps : aval<int>[]

An array of adaptive integer values to sum.

Returns: aval<int>

A new adaptive value containing the sum.

Example

 let scores = [| CVal.create 85; CVal.create 92; CVal.create 78 |]
 let deps = scores |> Array.map (fun s -> CVal.value s :> IAdaptiveValue<int>)
 let totalScore = AVal.sum deps

 scores.[0].Set(90)
 printfn "Total: %d" (AVal.getValue totalScore)  // Total: 260
val scores: obj array
val deps: obj array
module Array from Microsoft.FSharp.Collections
val map: mapping: ('T -> 'U) -> array: 'T array -> 'U array
val s: obj
Multiple items
val int: value: 'T -> int (requires member op_Explicit)

--------------------
type int = int32

--------------------
type int<'Measure> = int
val totalScore: obj
Multiple items
module Set from Microsoft.FSharp.Collections

--------------------
type Set<'T (requires comparison)> = interface IReadOnlyCollection<'T> interface IStructuralEquatable interface IComparable interface IEnumerable interface IEnumerable<'T> interface ICollection<'T> new: elements: 'T seq -> Set<'T> member Add: value: 'T -> Set<'T> member Contains: value: 'T -> bool member IsProperSubsetOf: otherSet: Set<'T> -> bool ...

--------------------
new: elements: 'T seq -> Set<'T>
val printfn: format: Printf.TextWriterFormat<'T> -> 'T

Type something to start searching.