Search code examples
functiondictionaryf#immutabilitymutable

F# - Function not returning updated data


I have a function GetCars which does not return the updated state. If I add a parameter, it works fine, or if I execute the same code in the other functions it works fine.

open System
open System.Collections.Concurrent

type Car = {
    Make : string
    Model : string
    ID : int
}

type Cars = {
    Count : int
    Collection : Car[]
}

module AppState = begin
    let state = new ConcurrentDictionary<string, obj>()

    let added = state.TryAdd("cars", {
        Count = 1
        Collection = [| { Make = "Honda"; Model = "Civic"; ID = 0 } |]
    })

    let DownCastCars (o : obj) =
        match o with
        | :? Cars as cars -> Some(cars)
        | _ -> None

    let GetCars = 
        let mutable cars : obj = null; 
        let success = state.TryGetValue("cars", &cars)
        if success then
            DownCastCars cars
        else
            None

    let AddCar car =
        match GetCars with
        | Some cars -> 
            state.TryUpdate("cars", { cars with Collection = Array.append cars.Collection [| car |] }, cars)
        | None ->
            false
end

let AddCarMakeModel make model =
    let car : Car = {
        Make = make
        Model = model
        ID = match AppState.GetCars with
                | Some cars -> cars.Collection.Length
                | None -> 0
    }
    let success = AppState.AddCar car
    if success then
        printfn "Successfully added car :)"
    else
        printfn "Failed to add car :("

[<EntryPoint>]
let main argv = 
    AddCarMakeModel "Honda" "Civic"
    AddCarMakeModel "Honda" "Civic LX"
    printfn "Press any key to continue"
    let s = Console.ReadLine()
    0 // return an integer exit code

If I add a parameter to GetCars i.e

let GetCars notused = 
    let mutable cars : obj = null; 
    let success = state.TryGetValue("cars", &cars)
    if success then
        DownCastCars cars
    else
        None

Then the GetCars function will return the newest value every time. Or if I just place the same code inside the other methods I am using i.e

let AddCarMakeModel Make Model =
    let car : Car = {
        Make = make
        Model = model
        ID = match AppState.GetCars with
                | Some cars -> cars.Collection.Length
                | None -> 0
    }
    let success = AppState.AddCar car
    if success then
        printfn "Successfully added car :)"
    else
        printfn "Failed to add car :("

I am guessing this has to do with "Do Bindings" but I am not sure how to apply that successfully to GetCars. I did notice GetCars get called when the application starts, and if I set a break point inside the function it never gets that far again.


Solution

  • As already stated in the comments, the solution is to add a unit parameter.

    The problem is that you are declaring a constant value, not a function value.

    This is evident if you look at its signature: val GetCars : Cars option There is no arrow, just a value, so GetCars will be executed only once and bound to the value on the right side of the expression.

    By adding a unit parameter you get the signature: val GetCars : unit -> Cars option so now at the call site you can refer either to the function value by GetCars alone or to execute it and get its result by GetCars().

    Also note you don't need the mutable since F# treat functions returning a value and a ref as a tuple, you can write GetCars like this:

    let GetCars() = 
        match state.TryGetValue("cars") with
        | true, (:? Cars as cars) -> Some cars
        | _ -> None
    

    And then get rid of the function DownCastCars.