Skip to content

Commit 1cc43d1

Browse files
committed
AVal updates
* implemented new builder methods as proposed in dotnet/fsharp#7756 * implemented `AVal.bind3`
1 parent 5c68c18 commit 1cc43d1

File tree

3 files changed

+81
-1
lines changed

3 files changed

+81
-1
lines changed

src/FSharp.Data.Adaptive/AdaptiveValue/AdaptiveValue.fs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,43 @@ module AVal =
252252
let res = mapping.Invoke (va, vb)
253253
inner <- ValueSome (struct (va, vb, res))
254254
res.GetValue token
255+
256+
/// Aval for binding three values in 'parallel'
257+
type Bind3Val<'T1, 'T2, 'T3, 'T4>(mapping: 'T1 -> 'T2 -> 'T3 -> aval<'T4>, value1: aval<'T1>, value2: aval<'T2>, value3: aval<'T3>) =
258+
inherit AbstractVal<'T4>()
259+
260+
let mapping = OptimizedClosures.FSharpFunc<'T1, 'T2, 'T3, aval<'T4>>.Adapt(mapping)
261+
let mutable inner: ValueOption< struct ('T1 * 'T2 * 'T3 * aval<'T4>) > = ValueNone
262+
let mutable inputDirty = 1
263+
264+
override x.InputChangedObject(_, o) =
265+
if Object.ReferenceEquals(o, value1) || Object.ReferenceEquals(o, value2) || Object.ReferenceEquals(o, value3) then
266+
inputDirty <- 1
267+
268+
override x.Compute(token: AdaptiveToken) =
269+
let va = value1.GetValue token
270+
let vb = value2.GetValue token
271+
let vc = value3.GetValue token
272+
#if FABLE_COMPILER
273+
let inputDirty = let v = inputDirty in inputDirty <- 0; v <> 0
274+
#else
275+
let inputDirty = System.Threading.Interlocked.Exchange(&inputDirty, 0) <> 0
276+
#endif
277+
278+
match inner with
279+
| ValueNone ->
280+
let res = mapping.Invoke (va, vb, vc)
281+
inner <- ValueSome (struct (va, vb, vc, res))
282+
res.GetValue token
283+
284+
| ValueSome(struct (oa, ob, oc, res)) when not inputDirty || (cheapEqual oa va && cheapEqual ob vb && cheapEqual oc vc) ->
285+
res.GetValue token
286+
287+
| ValueSome(struct (_, _, _, old)) ->
288+
old.Outputs.Remove x |> ignore
289+
let res = mapping.Invoke (va, vb, vc)
290+
inner <- ValueSome (struct (va, vb, vc, res))
291+
res.GetValue token
255292

256293
/// Aval for custom computations
257294
type CustomVal<'T>(compute: AdaptiveToken -> 'T) =
@@ -337,5 +374,23 @@ module AVal =
337374
else
338375
Bind2Val<'T1, 'T2, 'T3>(mapping, value1, value2) :> aval<_>
339376

377+
let bind3 (mapping: 'T1 -> 'T2 -> 'T3 -> aval<'T4>) (value1: aval<'T1>) (value2: aval<'T2>) (value3: aval<'T3>) =
378+
if value1.IsConstant && value2.IsConstant && value3.IsConstant then
379+
mapping (force value1) (force value2) (force value3)
380+
381+
elif value1.IsConstant then
382+
let a = force value1
383+
bind2 (fun b c -> mapping a b c) value2 value3
384+
385+
elif value2.IsConstant then
386+
let b = force value2
387+
bind2 (fun a c -> mapping a b c) value1 value3
388+
389+
elif value3.IsConstant then
390+
let c = force value3
391+
bind2 (fun a b -> mapping a b c) value1 value2
392+
else
393+
Bind3Val<'T1, 'T2, 'T3, 'T4>(mapping, value1, value2, value3) :> aval<_>
394+
340395
let custom (compute: AdaptiveToken -> 'T) =
341396
CustomVal compute :> aval<_>

src/FSharp.Data.Adaptive/AdaptiveValue/AdaptiveValue.fsi

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,11 @@ module AVal =
7676
/// adaptively depends on the adaptive value returned by mapping.
7777
/// The resulting aval<'T3> will hold the latest value of the aval<_> returned by mapping.
7878
val bind2 : mapping : ('T1 -> 'T2 -> aval<'T3>) -> value1 : aval<'T1> -> value2 : aval<'T2> -> aval<'T3>
79+
80+
/// Adaptively applies the mapping function to the given adaptive values and
81+
/// adaptively depends on the adaptive value returned by mapping.
82+
/// The resulting aval<'T4> will hold the latest value of the aval<_> returned by mapping.
83+
val bind3 : mapping : ('T1 -> 'T2 -> 'T3 -> aval<'T4>) -> value1 : aval<'T1> -> value2 : aval<'T2> -> value2 : aval<'T3> -> aval<'T4>
7984

8085
/// Creates a custom adaptive value using the given computation.
8186
/// Callers are responsible for removing inputs that are no longer needed.

src/FSharp.Data.Adaptive/ComputationExpressions.fs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,30 @@ module ComputationExpressions =
77

88
/// ComputationExpression builder for aval.
99
type AValBuilder() =
10-
static let delayed = AVal.custom ignore
10+
11+
member inline x.MergeSources(v1 : aval<'T1>, v2 : aval<'T2>) =
12+
AVal.map2 (fun a b -> a,b) v1 v2
13+
14+
member inline x.MergeSources3(v1 : aval<'T1>, v2 : aval<'T2>, v3 : aval<'T3>) =
15+
AVal.map3 (fun a b c -> a,b,c) v1 v2 v3
16+
17+
member inline x.BindReturn(value : aval<'T1>, mapping: 'T1 -> 'T2) =
18+
AVal.map mapping value
19+
20+
member inline x.Bind2Return(v1 : aval<'T1>, v2 : aval<'T2>, mapping: 'T1 -> 'T2 -> 'T3) =
21+
AVal.map2 mapping v1 v2
22+
23+
member inline x.Bind3Return(v1 : aval<'T1>, v2: aval<'T2>, v3: aval<'T3>, mapping: 'T1 -> 'T2 -> 'T3 -> 'T4) =
24+
AVal.map3 mapping v1 v2 v3
1125

1226
member inline x.Bind(value: aval<'T1>, mapping: 'T1 -> aval<'T2>) =
1327
AVal.bind mapping value
28+
29+
member inline x.Bind2(v1: aval<'T1>, v2: aval<'T2>, mapping: 'T1 -> 'T2 -> aval<'T3>) =
30+
AVal.bind2 mapping v1 v2
31+
32+
member inline x.Bind3(v1: aval<'T1>, v2: aval<'T2>, v3: aval<'T3>, mapping: 'T1 -> 'T2 -> 'T3 -> aval<'T4>) =
33+
AVal.bind3 mapping v1 v2 v3
1434

1535
member inline x.Return(value: 'T) =
1636
AVal.constant value

0 commit comments

Comments
 (0)