Adventures in F# - F# 101 Part 8 (Mutables and Reference Cells)

Time for another adventure in F#, covering some of the basics of functional programming and F# in particular.  Today we'll manage to look more at regular .NET integration and .NET programming.  With the previous efforts, we've looked more at functional programming and in turn F# specific things, but want to show that you can do anything normally in F# that you can in C#.  To me, F# is the perfect all-purpose language because it can do a lot of the things C# can do, but in turn, F# can do things much more elegantly than C# can, such as Pattern Matching.

Where We Are

Before we begin today, let's catch up to where we are today:
Today's topic will be covering imperative and object oriented programming in F#.  There is a lot to cover, so let's get started.  But there are a few administrative things to get out of the way first.

Learning F# ala Ted Neward?

Ted Neward recently announced on DotNetRocks Episode 332  that he's in the process of creating a class for F# for Pluralsight.  That should be interesting to those who are interested in this series, as well as F# in general.  Right now the community is rather small, so efforts like this should be rather rewarding I would hope.  Ted's a pretty brilliant guy, so I'd imagine only the best.  I'm hoping more details come out soon. 

Pattern Matching in C#

Part of this series is intended to bring such concepts as Pattern Matching, Currying and other Functional Programming concepts to the C# developer.  After all, the more C# language evolves, the more it seems to fall into the Functional Programming category.  In previous posts, I showed how to relate currying to C# and it was less elegant than F# to say the least.

But, let's look at Pattern Matching.  Bart De Smet has been posting recently on his blog about bringing the beauty of pattern matching to C#.  So far it's been a good six posts into it and I urge you to go ahead and take a look at this series. 
But when you read the series, it's all about getting into the low level and compiling expression trees to make the same simple beauty that is F#.  Sure, it can be done in C#, but nowhere near as elegant.  Performance is another issue that comes to mind with these.

Imperative Programming in F#

This section I'll lay out some of the basics of imperative style programming before I get into the full object oriented approach to programming.  So, we'll cover just a few topics and then I'll feel comfortable moving onto the real parts of creating classes and such.  We'll cover such things as void types and mutability in this section.

The unit Type

One of the first things I forgot to mention when describing F# functions and values in the unit type.  Think of this as the void type in C# that you are used to.  It's the type that doesn't accept or return a value.  First, let's look at the typical C# program with the void type for Hello World.

    class Program

    {

        static void Main(string[] args)

        {

            Console.WriteLine("Hello World");

        }

    }


Now, let's go ahead and look at it from the F# perspective.

#light

let main() =
  printfn "Hello World"

main()


As you will see when you hover over our code is that it is a unit type.  That in itself isn't all that interesting.  But, what we'll run into is problems when functions return a value, but we're not all that interested in them.  What happens?  Well, F# will complain that your return value isn't compatible with the unit type, which is essentially true.  So, how do you get around that?  Let's walk through a simple unit test of a Stack written in F# and unit testing with the xUnit.net framework.

#light

#R @"D:\Tools\xunit-build-1252\xunit.dll"

open System
open System.Collections.Generic
open Xunit

type Stack<'t> = class
  val elements : LinkedList<'t>
 
  new() = { elements = new LinkedList<'t>() }
 
  member x.IsEmpty
    with get() = x.elements.Count = 0
  
  member x.Push element =
    x.elements.AddFirst(element:'t)
  
  member x.Top
    with get() =
      if x.elements.Count = 0 then
        raise (InvalidOperationException("cannot top an empty stack"))
      x.elements.First.Value
    
  member x.Pop() =
    let top = x.Top
    x.elements.RemoveFirst()
    top
end

[<Fact>] 
let PopEmpty () =
  let stack = new Stack<string>()
  Assert.Throws<InvalidOperationException>(fun () -> stack.Pop() |> ignore )


The real interesting part you should pay attention to is the last line.  As you can see, I am using the forward operator to indicate that I really don't care what the function returns, just that I'm interested in that it executes.  This is most likely during such functions that have some sort of side effect to them.  I could also use the ignore function instead of the forward operator such as this:

[<Fact>] 
let PopEmpty () =
  let stack = new Stack<string>()
  Assert.Throws<InvalidOperationException>(fun () -> ignore(stack.Pop()) )

 


This is very helpful in these cases where we really don't care about the return value, instead want to mutate the state of our given object, such as removing a value from a collection and so on.

Mutables

As I said in many posts before, by default all "variables" by which I mean values in F# are immutable.  This is a standard in functional programming and all in the ML family.  You can easily redefine a value by using the let keyword, but not actually mutate its state.  But, since F# is a multi-purpose language on the .NET platform, mutable state can be had.  To take advantage of this, mark your value as mutable.  Then to change the value, just use the <- operator to reassign the value.  Below is a simple example of this:

#light

let mutable answer = 42
printfn "Answer is %i" answer
answer <- 8
printfn "Answer is %i" answer

 


A key difference from the reassignment is that you cannot change the value type.  Whereas I can redefine answer by keep using the let keyword, I can only keep my answer in this above example of the int type. 

This can also apply to record types as well where you can change the fields.  In the last installment, we talked about record types.  Well, by default there as well, the fields for the record type are immutable.  But, as with before, that can be changed.  I of course like to caution people that mutable state takes a lot of the value proposition away from the side effect free programming that you gain with F# by default.  But, nevertheless, you can still do it as noted below:

#light

type Person = { FirstName : string; mutable LastName : string; mutable IsMarried : bool }

let friend = { FirstName = "Courtney"; LastName = "Cox"; IsMarried = false }
friend.LastName <- "Cox-Arquette"
friend.IsMarried <- true 


What I was able to do was define a Person record and change a couple of fields while using the <- operator and defining the fields as mutable.  Yes, I could have used some scientific calculation or something, but this was easy.

Reference Cells

The last thing I want to touch on in this post is reference cells.  You can think of these much as pointers in other languages or reference types.  These of course can be of any type.  The idea behind using these is to make updating fields as easy as possible.  As with mutable fields, you cannot change the type once it has been assigned.  To use these, you need to remember three basic operators
  • ref - Allocates a reference cell
  • := - Mutates a reference cell
  • ! - Reads the reference cell
Below is a quick example of mutation through reference cells:

#light

let x = ref 2
x := !x + 25
printfn "%i" !x


What the code example above lets me do is define a reference to the number 2.  Then I can change that reference by reading the current x value and adding 25.  Then I mutate the existing x value with the result.

Conclusion

This is just meant to be a brief overview to some imperative programming constructs that you might see in .NET, Java, C, C++ and so on.  F# is a first class language all the way with constructs that support these things as well as your normal functional programming constructs.  I hope we get to cover some of this at ALT.NET Open Spaces, Seattle at some point because I'm sure a lot of people will be interested.  Until next time...

kick it on DotNetKicks.com

3 Comments

Comments have been disabled for this content.