The View model is the centerpiece to MVVM, with no external dependencies. As a result, the view model is an excellent class that requires no libraries while also exercising many language features related to classes. These requirements make view models a great example for learning how to build classes in a new language.
If learning F# (FSharp) is on your bucket list, getting started with classes is the easiest. Everything you need to know about F# classes is covered here. This post shows 23 (mostly) useless view models to get you started with F#. At the end, you will know how to make any useful view model in F#, so let’s get started!
View Model: #1
As already stated, in the purest sense, a view model is just a class. Creating classes in F# is really easy. Here is our first view model
1: 2: |
|
There is no need to create a new file. The only requirement is that it is declared before it is used. type
is synonymous with the class
keyword. ViewModel
is also public by default, which is generally what the developer intended. Parenthesis follow the class name.
Foo
is a public method. They keyword member
is used to declare anything that belongs to the class. Additionally don’t forget the instance for the method this.
required before the name of the method.
A few striking differences that immediately stand out to most developers is the lack of curly braces and types. The braces are omitted in favour of indentation. Should any problems arise with indentation, the compiler will highlight the line. Secondly, the lack of types can seam daunting. The F# compiler utilizes advanced mathematics so this generally not a problem. All items are given a type and checked for consistent usage. This is known as type inference.
Variables: More than one way
Variables are possible in F#, and the language even has two ways of doing it. An example of each is listed below:
1: 2: 3: 4: 5: |
|
A key aspect to highlight in the examples above is that mutation/variables require ‘opt in’ with keywords. By default, all items are immutable, which is what most developers prefer. Predictable code is the result of immutability. Next up, our view model needs a getter to expose the variables.
1: 2: 3: 4: 5: 6: 7: |
|
As with the methods, the member
keyword is used, but the parentheses are left off. For the ref
variable, note the !
that is used to return the value contained. If this is missed, the wrong type will be returned. If a setter is also present (shown later), then a compiler error will result. With the getters defined, a setter is is the next step. On a side note, in F# <-
is used for assignment. For the ref
there is a shorthand :=
to update the value.
Adding a setter to the property is simple and straight forward. Append the keyword and
with set
and then name the parameter to function, value
is commonly used.
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: |
|
When to use mutable
or ref
You should prefer mutable
over ref
as it is easier to with. You can use ref
when using a framework that updates the value of properties in a subclass. To illustrate this, here is our next view model:
1: 2: 3: 4: 5: 6: 7: 8: 9: |
|
They key part to the code above is the call to SetProperty
a method on the base class. For a view model, there is no need to set a value if it has not changed. SetProperty
performs this check, and then updates the value if value has changed. A bool, ie true, will be returned if the value is updated. A ref
makes this possible.
Auto Properties: A new keyword
Auto properties have a backing variable. In F# the syntax is a little different to methods. Here is an example with a getter only
1: 2: 3: 4: 5: 6: |
|
The key difference here is the use of the keyword val
. Additionally the instance, this
no longer needs to declared before the name. Adding a setter is also super easy, just add a comma and the keyword set
:
1: 2: |
|
Scoping rules: There’s a twist
Scoping rules are also available in F#. Methods can have their usage restricted to only inside the class with private
.
1: 2: |
|
There is no scoping for protected
as it creates potential problems with lamda functions accessing the base class. Instead use interfaces/higher-order functions.
Prefer function over methods
It turns out there is another way to achieve the same as a private method in F#. A function declared in a class in most circumstances will behave the same as a private method. Because of this, it is best to prefer functions over methods, as functions have better type inference and can be chained together easier using F#’s iconic piper operator |>
.
1: 2: 3: 4: |
|
Closely related to access modifiers, is overriding methods. It is also possible in F#, and is just a keyword change to use override
instead of member
1: 2: 3: |
|
Adding constructor arguments
Constructor arguments, are passed within the parentheses of the declaring line of the class. See below:
1: 2: |
|
Types in F# are declared after the name with a colon. The type can also be omitted in many cases and F# will infer the type.
1: 2: |
|
For this example, the method Foo
has been declared to return an int. F# can figure out that the type of foo
must also be an int. Alternatively, the type could be declared the other way around. The types and meaning are identical in both cases:
1: 2: |
|
Another important point to highlight with constructor arguments in F# is that they are immutable:
1: 2: 3: 4: |
|
If immutability is a problem and mutation is required, there is a simple solution. A copy of the constructor argument can be taken and declared with the mutable
keyword highlighted above (though many prefer to avoid mutation, as functional competence is gained).
1: 2: 3: 4: |
|
A Constructor without a Constructor
The syntax for constructors is a quite different in F#; though much cleaner. To declare a constructor, after the local functions/values and before any member items, do
signals the constructor followed by the required statements.
1: 2: 3: 4: 5: 6: |
|
In the example above, the class is initially constructed with foo
set to null
. The constructor (the statements after do
), are evaluated and foo
is updated to be “Hello, World”.
The example above only aims to highlight the usage of do
, however usage of null
and mutable
are not encouraged. As a developer learns techniques in functional programming, usage of null
or mutable
is required less. The reason for the reduced usage is that concepts in functional programming, model computation differently resulting in code that is much clear in intent and fewer runtime errors. For a brief highlight of this see my post on How to turn runtime exceptions into compiler errors.
Dependencies with interfaces
If you have read this far, we’re almost at the point where all the knowledge has been laid out to build any standard view model. To complete this, you need to know how to pass in dependencies, notably interfaces.
An interface could be declared in C# or F# (The C# declaration would need to be in a different project). Here is one written in F#. There are no parenthesis after the name. Methods are declared with the abstract
keyword and a type declaration.
1: 2: |
|
‘Foo’ is method that, when invoked, returns a single integer. With an interface declared, it can now be used. Here is a view model that is using the interface as a dependency. Simply declare the type and use it as you would any constructor argument.
1: 2: |
|
If you were to compare this to the C#/Java equivalent, you will see that a copy of the dependency does not need to be taken. No strange attributes, no local constructor parameters. No duplicate names.
Another key feature with these interfaces and view models is that they are fully compliant with any IOC framework. Register the interface (F# or C#), make sure the view model is appropriately named and enjoy everything working. These F# interfaces/classes generate very similar IL that the C# equivalent would output, you benefit from clearer code.
Interfaces are optional
The last example showed building a view model with a dependency through an interface. In large apps, both interfaces themselves and the number of interfaces a view model requires can get large. Many developers have found this can make things difficult to work with, and hard to refactor as the abstractions are no longer clear. There is an alternative that many developers have already turned to. Replace the interface with just a function. For a full read up on this Scott Wlaschin provides the details: Functional approaches to dependency injection. To follow the approach that Scott talks about, all we need to do is pass in functions.
1: 2: 3: 4: 5: |
|
As stated, no interface needed, just a function. Type inference checks the type for us so everything must be wired up correctly. Additionally, it must be noted that construction of the class must be done explicitly (Leave a comment if you know of an IOC container that can resolve functions). The result is that the code is now easier to read, especially for juniors since an IOC container is an advanced topic.
Using this functional approach will result in many functions passed into the view model. Incase it gets hard to keep track of those names and types, an alias for the type can be created to keep dependencies readable. Here’s an example with a couple of dependencies:
1: 2: 3: 4: 5: 6: |
|
The type of foo
and bar
are declared explicitly, rather than being inferred by the compiler. As arguments to the class they now hve readable types as opposed to function types. The alias only affects readability, and will not help the compiler find errors.
So there you go, 23 absolutely useless view models. F# makes it easier and clear to create view models. The code is shorter and with a powerful compiler, both typing and errors can be reduced!
Your challenge: How many ways can you combine the 22 view models to make useful view models?