Search code examples
f#mutablelet-binding

How do I properly implement a property in F#?


Consider my first attempt, a simple type in F# like the following:

type Test() =
    inherit BaseImplementingNotifyPropertyChangedViaOnPropertyChanged()
    let mutable prop: string = null
    member this.Prop
        with public get() = prop
        and public set value =
            match value with
                | _ when value = prop -> ()
                | _ -> 
                    let prop = value
                    this.OnPropertyChanged("Prop")

Now I test this via C# (this object is being exposed to a C# project, so apparent C# semantics are desirable):

[TestMethod]
public void TaskMaster_Test()
{
    var target = new FTest();
    string propName = null;
    target.PropertyChanged += (s, a) => propName = a.PropertyName;
    target.Prop = "newString";

    Assert.AreEqual("Prop", propName);
    Assert.AreEqual("newString", target.Prop);

    return;
}

propName is properly assigned, my F# Setter is running, but the second assert is failing because the underlying value of prop isn't changed. This sort of makes sense to me, because if I remove mutable from the prop field, no error is generated (and one should be because I'm trying to mutate the value). I think I must be missing a fundamental concept.

What's the correct way to rebind/mutate prop in the Test class so that I can pass my unit test?


Solution

  • Try this:

    type Test() =
        inherit BaseImplementingNotifyPropertyChangedViaOnPropertyChanged()
        let mutable prop: string = null
        member this.Prop
            with public get() = prop
            and public set value =
                match value with
                    | _ when value = prop -> ()
                    | _ -> 
                        prop <- value
                        this.OnPropertyChanged("Prop")
    

    You need to make the binding mutable and then alter its value in your setter. In your initial code, you were just creating a new binding (also called prop) within your setter, so no change was visible.