View Models in F#

A View Model is a projections of one or more models onto the view. As projections, they should have no mutable state of their own. In C# and other object-oriented languages, mutable state is the default. In F# and other functional languages, it is discouraged. So F# is actually a better language for expressing View Models than is C#.

Constructors

An F# View Model has less ceremony than its C# counterpart. Here is the constructor of a C# view model that represents a Product from a Customer’s point-of-view:

public class ProductViewModel
{
    private Product _product;
    private Customer _customer;

    public ProductViewModel(Product product, Customer customer)
    {
        _product = product;
        _customer = customer;
    }
}

The equivalent code in F# is:

type ProductViewModel(product: Product, customer: Customer)

F# automatically pulls the parameters into context, so we don’t have to store them.

Properties

A View Model exposes properties for data binding. The values of these properties are calculated based on the models. In C#, a calculated property looks like this:

public decimal Price
{
    get
    {
        return Decimal.Round((_product.BasePrice * (_customer.IsPreferred ? 0.9m : 1.0m)), 2);
    }
}

We have to declare the property type and use two layers of punctuation. In F#, it looks like this:

member this.Price =
    System.Decimal.Round((product.BasePrice * if customer.IsPreferred then 0.9m else 1.0m), 2)

F# infers the type from the expression. Indentation removes the need for much of the punctuation.

Some properties are bidirectional. When the user sets them, they modify the model. In C#:

public string CustomerName
{
    get
    {
        return _customer.Name;
    }
    set
    {
        _customer.Name = value;
    }
}

And in F#:

member this.CustomerName
    with get() = customer.Name
    and set(value) = customer.Name <- value

Here the fact that properties are atypical in F# is starting to show. The “value” parameter is explicitly declared, as opposed to being a built-in keyword in C#.

Collections

A View Model exposes other View Models. So when we need a list, we should project a collection of Model objects into a collection of View Model objects. In C#, we do this with the Linq “select”.

public IEnumerable<ProductViewModel> Products
{
    get
    {
        return from p in _catalog.Products select new ProductViewModel(p, _customer);
    }
}

Since Linq is functional in nature, this projection is succinct. In F#, the equivalent is:

member this.Products = Seq.map (fun p -> ProductViewModel(p, customer)) catalog.Products

Seq.map is equivalent to “select”. It takes a mapping function and applies it to all elements in the source collection. The type inference of IEnumerable<ProductViewModel> is nice, but otherwise it’s even.

Equivalence

Whenever you bind both the ItemsSource and the SelectedItem of an items control, you need to ensure that the selected item is equal to one of the items in the collection. When projecting into new View Models, we create new identities. We therefore need to replace identity comparison with value comparison. Here’s how we do that in C#:

public override bool Equals(object obj)
{
    ProductViewModel that = obj as ProductViewModel;
    if (that == null)
        return false;
    return _product == that._product;
}

public override int GetHashCode()
{
    return _product.GetHashCode();
}

We only compare the product because we know that all items in the list are for the same customer. In F#, we have to write this:

member this.Product = product
override this.Equals(obj) =
    match obj with
        | :? ProductViewModel as that -> that.Product = product
        | _ -> false
override this.GetHashCode() = product.GetHashCode()

Since “product” is in the scope of the closure, it is not an actual member of the View Model. That’s why we can’t access “that.product” directly. Instead, we define a member “Product” as an alias to the parameter. We need to do this anyway, since the code that uses ProductViewModel need to know the actual Product of the selected item, but it might surprise you at first.

I actually expected F# to do much better at this part. Since the class has no mutable values, I expected that it would assume that two objects with the same parameters are equal. Other functional languages have a feature called “memoization” that treats all closures having the same parameters as equal, even identical.

Commands

Update Controls gives you the MakeCommand class, which generates an ICommand from a couple of functions. To use this, you have to add the following references to your ViewModels project:

  • UpdateControls
  • UpdateControls.XAML
  • PresentationCore

Like Linq, MakeCommand is functional in nature. Here is how it is used in C#:

public ICommand PlaceOrder
{
    get
    {
        return MakeCommand
            .When(() => _navigation.SelectedProduct != null)
            .Do(() => _customer.PlaceOrder(_navigation.SelectedProduct));
    }
}

The F# syntax is nearly identical:

member this.PlaceOrder =
    MakeCommand
        .When(fun () -> navigation.SelectedProduct <> null)
        .Do(fun () -> customer.PlaceOrder(navigation.SelectedProduct))

The main thing to recognize here is that C# is more forgiving; lambda expressions that return values (i.e. Func<T>) can be used as actions. F# does not allow this. So be sure that your method returns void. Alternatively, you could do something with that value, like assigning it to a navigation model property.

F# as View Model layer

Even though there are some places where F# is simply on par with C#, I believe the strengths of F# are compelling enough to recommend using it in the View Model layer. All of the modern XAML-based user interface technologies (WPF, Silverlight 4, Windows Phone 7) support F#. The syntax may be a little unfamiliar, but perhaps these examples will help get you started. Download the code and try it for yourself.