Is record type updating optimized?

In clojure hashmap used Software transactional memory - Wikipedia.
If I will be to use this code for update record this is will be equivalent?

open Avalonia
open Avalonia.Controls
open Avalonia.Markup.Xaml
open FSharp.Control.Reactive
open Avalonia.Threading

type AppState =
    { Count: int
      EnableTimer: bool }

    static member Zero = { Count = 0; EnableTimer = true }

type MainWindow() as this =
    inherit Window()
    do this.InitializeComponent()

    member private this.InitializeComponent() =
#if DEBUG
        this.AttachDevTools()
#endif
        AvaloniaXamlLoader.Load(this)

        let count = Subject.behavior (AppState.Zero)

        let tb = this.FindControl<TextBlock>("info")
        let btn = this.FindControl<Button>("inc")
        let inc = fun (x: AppState) -> { x with Count = x.Count + 1 }

        btn.Click.Add(fun _ ->
            if count.Value.EnableTimer then
                count.OnNext(
                    { count.Value with
                        Count = count.Value.Count + 1
                        EnableTimer = false }
                )
            else
                count.OnNext(inc count.Value))

        count.Subscribe(fun x -> Dispatcher.UIThread.Invoke(fun _ -> tb.Text <- $"Count: {x.Count}"))
        |> ignore

        task {
            while count.Value.EnableTimer do
                do! Async.Sleep 1000
                count.OnNext(inc count.Value)
        }
        |> ignore

Short answer: no it will not use STM. It is thread-safe, but I believe that is achieved via locking.

Longer answer: While there’s some STM functionality available in the FSharpx.Extras nuget package, .NET largely doesn’t use STM at all, and relies primarily on locking and reset events for synchronization. In the code sample you provided, however, there are no real transactions that would benefit from STM. The update of count is a small enough operation that it’s doubtful that STM would provide any benefit over locking (any .NET STM implementation still requires a small bit of locking I believe). Note that it’s not actually mutating the fields in the record, it’s making a new copy of the record, and the update to count is just to have it point at the new copy instead of the old one, so it doesn’t matter how many fields you change, it’s still just a single operation. You can use stm from FSharpx if you define your own sequence of operations that you want to run atomically, but I don’t think any of the builtin operations here in FSharp.Control.Reactive would actually even benefit.

1 Like