Skip to content

Commit

Permalink
basic AArr implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
krauthaufen committed Oct 14, 2024
1 parent 36b8092 commit 4adffd2
Show file tree
Hide file tree
Showing 16 changed files with 1,144 additions and 339 deletions.
634 changes: 317 additions & 317 deletions src/FSharp.Data.Adaptive.Experimental/AdaptiveArray/Arr.fs

Large diffs are not rendered by default.

133 changes: 133 additions & 0 deletions src/FSharp.Data.Adaptive/AdaptiveArray/AdaptiveArray.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
namespace FSharp.Data.Adaptive

open System
open FSharp.Data.Traceable
open FSharp.Data.Adaptive

/// An adaptive reader for aarr that allows to pull operations and exposes its current state.
type IArrayReader<'T> =
IOpReader<arr<'T>, arrdelta<'T>>

/// Adaptive array datastructure.
[<Interface>]
type IAdaptiveArray<'T> =
/// Is the array constant?
abstract member IsConstant : bool

/// The current content of the array as aval.
abstract member Content : aval<arr<'T>>

/// Gets a new reader to the array.
abstract member GetReader : unit -> IArrayReader<'T>

/// Gets the underlying History instance for the alist (if any)
abstract member History : option<History<arr<'T>, arrdelta<'T>>>

/// Adaptive list datastructure.
type aarr<'T> = IAdaptiveArray<'T>


/// Functional operators for the alist<_> type.
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module AArr =
/// Efficient implementation for a constant adaptive array.
[<Sealed>]
type ConstantArray<'T>(content : Lazy<arr<'T>>) =
let value = AVal.delay (fun () -> content.Value)

member x.Content = value

member x.GetReader() =
History.Readers.ConstantReader<_,_>(
Arr.trace,
lazy (Arr.computeDelta DefaultEqualityComparer.Instance Arr.empty content.Value),
content
) :> IArrayReader<_>

interface IAdaptiveArray<'T> with
member x.IsConstant = true
member x.GetReader() = x.GetReader()
member x.Content = x.Content
member x.History = None

/// Core implementation for a dependent array.
[<Sealed>]
type AdaptiveArray<'T>(createReader : unit -> IOpReader<arrdelta<'T>>) =
let history = History(createReader, Arr.trace)

/// Gets a new reader to the set.
member x.GetReader() : IArrayReader<'T> =
history.NewReader()

/// Current content of the set as aval.
member x.Content =
history :> aval<_>

interface IAdaptiveArray<'T> with
member x.IsConstant = false
member x.GetReader() = x.GetReader()
member x.Content = x.Content
member x.History = Some history

/// Efficient implementation for an empty adaptive array.
[<Sealed>]
type EmptyArray<'T> private() =
static let instance = EmptyArray<'T>() :> aarr<_>
let content = AVal.constant Arr.empty
let reader = History.Readers.EmptyReader<arr<'T>, arrdelta<'T>>(Arr.trace) :> IArrayReader<'T>
static member Instance = instance

member x.Content = content
member x.GetReader() = reader

interface IAdaptiveArray<'T> with
member x.IsConstant = true
member x.GetReader() = x.GetReader()
member x.Content = x.Content
member x.History = None

module Readers =
type MapReader<'T1, 'T2>(input : aarr<'T1>, mapping : 'T1 -> 'T2) =
inherit AbstractReader<arrdelta<'T2>>(ArrDelta.empty)

let reader = input.GetReader()

override x.Compute(token : AdaptiveToken) =
let delta = reader.GetChanges(token)
ArrDelta.map mapping delta

/// The empty aarr.
[<GeneralizableValue>]
let empty<'T> : aarr<'T> =
EmptyArray<'T>.Instance

/// A constant aarr holding a single value.
let single (value : 'T) : aarr<'T> =
ConstantArray(Lazy<_>.CreateFromValue(Arr.single value)) :> aarr<_>

/// Creates an aarr holding the given values.
let ofSeq (elements: seq<'T>) : aarr<'T> =
ConstantArray(Lazy<_>.CreateFromValue(Arr.ofSeq elements)) :> aarr<_>


/// Creates an aarr holding the given values.
let ofList (elements: list<'T>) : aarr<'T> =
ConstantArray(Lazy<_>.CreateFromValue(Arr.ofList elements)) :> aarr<_>

/// Creates an aarr holding the given values.
let ofArray (elements: 'T[]) : aarr<'T> =
ConstantArray(Lazy<_>.CreateFromValue(Arr.ofArray elements)) :> aarr<_>

/// Creates an aarr holding the given values.
let ofArr (elements: arr<'T>) : aarr<'T> =
ConstantArray(Lazy<_>.CreateFromValue(elements)) :> aarr<_>

/// Creates an aarr using the given reader-creator.
let ofReader (create : (unit -> #IOpReader<arrdelta<'T>>)) : aarr<'T> =
AdaptiveArray<'T>(fun () -> create() :> IOpReader<arrdelta<'T>>) :> aarr<'T>

/// Adaptively applies the given mapping function to all elements and returns a new aarr containing the results.
let map (mapping : 'T1 -> 'T2) (input : aarr<'T1>) : aarr<'T2> =
ofReader <| fun () ->
Readers.MapReader(input, mapping)

57 changes: 57 additions & 0 deletions src/FSharp.Data.Adaptive/AdaptiveArray/AdaptiveArray.fsi
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
namespace FSharp.Data.Adaptive

open System
open FSharp.Data.Traceable
open FSharp.Data.Adaptive

/// An adaptive reader for aarr that allows to pull operations and exposes its current state.
type IArrayReader<'T> =
IOpReader<arr<'T>, arrdelta<'T>>

/// Adaptive array datastructure.
[<Interface>]
type IAdaptiveArray<'T> =
/// Is the array constant?
abstract member IsConstant : bool

/// The current content of the array as aval.
abstract member Content : aval<arr<'T>>

/// Gets a new reader to the array.
abstract member GetReader : unit -> IArrayReader<'T>

/// Gets the underlying History instance for the alist (if any)
abstract member History : option<History<arr<'T>, arrdelta<'T>>>

/// Adaptive list datastructure.
type aarr<'T> = IAdaptiveArray<'T>


/// Functional operators for the alist<_> type.
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module AArr =

/// The empty aarr.
[<GeneralizableValue>]
val empty<'T> : aarr<'T>

/// A constant aarr holding a single value.
val single : value: 'T -> aarr<'T>

/// Creates an aarr holding the given values.
val ofSeq : elements: seq<'T> -> aarr<'T>

/// Creates an aarr holding the given values.
val ofList : elements: list<'T> -> aarr<'T>

/// Creates an aarr holding the given values.
val ofArray : elements: 'T[] -> aarr<'T>

/// Creates an aarr holding the given values.
val ofArr : elements: arr<'T> -> aarr<'T>

/// Creates an aarr using the given reader-creator.
val ofReader : create: (unit -> #IOpReader<arrdelta<'T>>) -> aarr<'T>

/// Adaptively applies the given mapping function to all elements and returns a new aarr containing the results.
val map : mapping: ('T1 -> 'T2) -> input: aarr<'T1> -> aarr<'T2>
96 changes: 96 additions & 0 deletions src/FSharp.Data.Adaptive/AdaptiveArray/ChangeableArray.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
namespace FSharp.Data.Adaptive

open System.Collections.Generic
open FSharp.Data.Traceable

/// Changeable adaptive array that allows mutation by user-code and implements aarr.
[<Sealed>]
type ChangeableArray<'T>(state : arr<'T>) =
let history = History<arr<'T>, arrdelta<'T>>(Arr.trace)

do history.Perform (ArrDelta.single { Index = 0; Count = 0; Elements = state }) |> ignore

/// is the array currently empty?
member x.IsEmpty = history.State.IsEmpty

/// the number of elements currently in the array.
member x.Length = history.State.Length

/// Gets or sets the value for the array.
member x.Value
with get() =
history.State
and set (v : arr<'T>) =
let delta = Arr.computeDelta DefaultEqualityComparer.Instance history.State v
history.Perform delta |> ignore

member x.Clear() =
let delta = ArrDelta.single { Index = 0; Count = history.State.Length; Elements = Arr.empty }
history.Perform delta |> ignore

member x.Add (item : 'T) =
let delta = ArrDelta.single { Index = history.State.Length; Count = 0; Elements = Arr.single item }
history.Perform delta |> ignore

member x.Insert (index : int, value : 'T) =
if index < 0 || index > history.State.Length then raise <| System.IndexOutOfRangeException()
let delta = ArrDelta.single { Index = index; Count = 0; Elements = Arr.single value }
history.Perform delta |> ignore

member x.RemoveAt(index : int) =
if index < 0 || index >= history.State.Length then raise <| System.IndexOutOfRangeException()
let delta = ArrDelta.single { Index = index; Count = 1; Elements = Arr.empty }
history.Perform delta |> ignore

member x.CopyTo(array, arrayIndex) = history.State.CopyTo(array, arrayIndex)

member x.Item
with get (index : int) =
history.State.[index]
and set (index : int) (value : 'T) =
let delta = ArrDelta.single { Index = index; Count = 1; Elements = Arr.single value }
history.Perform delta |> ignore

new(elements : seq<'T>) = ChangeableArray<'T>(Arr.ofSeq elements)
new() = ChangeableArray<'T>(Arr.empty)

member x.GetEnumerator() = history.State.GetEnumerator()

interface System.Collections.IEnumerable with
member x.GetEnumerator() = history.State.GetEnumerator()

interface System.Collections.Generic.IEnumerable<'T> with
member x.GetEnumerator() = history.State.GetEnumerator()

interface System.Collections.Generic.ICollection<'T> with
member x.Add(item) = x.Add item
member x.Clear() = x.Clear()
member x.Contains(item) = history.State |> Arr.exists (fun v -> DefaultEquality.equals v item)
member x.CopyTo(array, arrayIndex) = x.CopyTo(array, arrayIndex)
member x.Remove(item) =
match Arr.tryFindIndex (fun v -> DefaultEquality.equals v item) history.State with
| Some index -> x.RemoveAt index; true
| None -> false
member x.Count = x.Length
member x.IsReadOnly = false

interface System.Collections.Generic.IList<'T> with
member x.IndexOf(item) =
match Arr.tryFindIndex (fun v -> DefaultEquality.equals v item) history.State with
| Some index -> index
| None -> -1
member x.Insert(index,item) = x.Insert(index, item)
member x.RemoveAt(index) = x.RemoveAt(index)
member x.Item
with get(i : int) = x.[i]
and set (i : int) (value : 'T) = x.[i] <- value

interface IAdaptiveArray<'T> with
member x.IsConstant = false
member x.GetReader() = history.NewReader()
member x.Content = history :> aval<_>
member x.History = Some history


/// Changeable adaptive array that allows mutation by user-code and implements aarr.
type carr<'T> = ChangeableArray<'T>
33 changes: 33 additions & 0 deletions src/FSharp.Data.Adaptive/AdaptiveArray/ChangeableArray.fsi
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
namespace FSharp.Data.Adaptive

open System.Collections.Generic
open FSharp.Data.Traceable

/// Changeable adaptive array that allows mutation by user-code and implements aarr.
[<Sealed>]
type ChangeableArray<'T> =
interface IAdaptiveArray<'T>
interface System.Collections.Generic.IEnumerable<'T>
interface System.Collections.Generic.ICollection<'T>
interface System.Collections.Generic.IList<'T>

/// is the array currently empty?
member IsEmpty : bool

/// the number of elements currently in the array.
member Length : int

/// Gets or sets the value for the array.
member Value : arr<'T> with get, set

member Clear : unit -> unit
member Add : 'T -> unit
member Insert : index: int * value: 'T -> unit
member Item : int -> 'T with get, set
new : unit -> ChangeableArray<'T>
new : arr<'T> -> ChangeableArray<'T>
new : seq<'T> -> ChangeableArray<'T>
/// Changeable adaptive array that allows mutation by user-code and implements aarr.
type carr<'T> = ChangeableArray<'T>
11 changes: 6 additions & 5 deletions src/FSharp.Data.Adaptive/Datastructures/ArrDelta.fs
Original file line number Diff line number Diff line change
Expand Up @@ -407,11 +407,13 @@ module ArrDelta =
module ``ArrDelta Extensions`` =

module Arr =
let computeDelta (equal : 'a -> 'a -> bool) (src : arr<'a>) (dst : arr<'a>) : arrdelta<'a> =
let computeDelta (cmp : System.Collections.Generic.IEqualityComparer<'a>) (src : arr<'a>) (dst : arr<'a>) : arrdelta<'a> =
let srcArr = Arr.toArray src
let dstArr = Arr.toArray dst

let mutable steps = DeltaOperationList.ofArrayMyers equal srcArr dstArr
let mutable steps =
if srcArr.Length = 0 && dstArr.Length = 0 then DeltaOperationList.DeltaOperationList.Empty
else DeltaOperationList.ofArrayMyersComparer cmp srcArr dstArr

let mutable si = 0
let mutable di = 0
Expand Down Expand Up @@ -457,15 +459,14 @@ module ``ArrDelta Extensions`` =

arrdelta delta


let applyDeltaAndGetEffective (equal : 'a -> 'a -> bool) (state : arr<'a>) (delta : arrdelta<'a>) =
let applyDeltaAndGetEffective (cmp : System.Collections.Generic.IEqualityComparer<'a>) (state : arr<'a>) (delta : arrdelta<'a>) =
let mutable effective = ArrDelta.empty
let mutable res = state
for op in delta do
res <-
res.UpdateRange(op.Index, op.Count, fun old ->
let real =
computeDelta equal old op.Elements
computeDelta cmp old op.Elements
|> ArrDelta.mapOp (fun oo -> { oo with Index = oo.Index + op.Index })
effective <- ArrDelta.combine effective real
op.Elements
Expand Down
Loading

0 comments on commit 4adffd2

Please sign in to comment.