Tuesday, January 29, 2008
As promised, today I'm demonstrating a compelling way in which F# uses tuples to make .NET programming more elegant.

A question that comes up early in F# demonstrations is, "Can I use F# to access code written in my favorite .NET language, <BLANK>?" The answer is an emphatic yes. F# is a first-class .NET citizen that compiles to the same IL as any other .NET language. Consider the following code:

> #light
- open System.Collections.Generic
-
- let d = new Dictionary<int, string>()
- d.Add(1, "My")
- d.Add(2, "F#")
- d.Add(3, "Dictionary");;

val d : Dictionary<int,string>

> d;;
val it : Dictionary<int,string> = dict [(1, "My"); (2, "F#"); (3, "Dictionary")]

The above code1 instantiates a new System.Collections.Generic.Dictionary<TKey, TValue> for int and string, and adds three key/value pairs to it. Note that Dictionary is not written in F#. It is part of the .NET base class library, written in C#.

Retrieving values from d is easy. We simply pass the value's key to the dictionary's indexer like so:

> d.[1];;

val it : string = "My"

> d.[3];;

val it : string = "Dictionary"

However, if we pass a key that isn't found in the dictionary, an exception is thrown.2

> d.[4];;

System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.
   at System.ThrowHelper.ThrowKeyNotFoundException()
   at System.Collections.Generic.Dictionary`2.get_Item(TKey key)
   at <StartupCode$FSI_0013>.FSI_0013._main()
stopped due to error

Fortunately, Dictionary provides a function that allows us to query using an invalid key without throwing an exception. This function, TryGetValue, has the following signature (shown in C#):

bool TryGetValue(TKey key, out TValue value)

The purpose of TryGetValue is obvious. If key is found, the function returns true and the value is returned in the output parameter3. If key is not found, the function returns false and value contains some throwaway data. The C# code below demonstrates how this function might be used.

using System;
using System.Collections.Generic;

class Program
{
  static void Main()
  {
    var d = new Dictionary<int, string>();
    d.Add(1, "My");
    d.Add(2, "C#");
    d.Add(3, "Dictionary");

    string v;
    if (d.TryGetValue(4, out v))
      Console.WriteLine(v);
  }
}

So, how can we use this function in F#? Well, there're a few ways.

The first approach is almost exactly the same as the C# version above. First, we declare a variable to pass as the output parameter. Note that this variable must be declared as mutable so TryGetValue can modify it.

> let mutable v = "";;

val mutable v : string

Now, we can call TryGetValue, passing v by reference.

> d.TryGetValue(1, &v);;

  d.TryGetValue(1, &v);;
  -----------------^^^

stdin(19,17): warning: FS0051: The address-of operator may result in non-verifiable code.
Use only when passing byrefs to functions that require them.

val it : bool = true

> v;;

val it : string = "My"

OK. That worked but displayed an ugly warning about non-verifiable code. Yikes! Fortunately, F# provides another way to declare variables which support mutation: reference cells.4

Declaring a variable as a reference cell is trivial:

> let v = ref "";;

val v : string ref

We can pass the reference cell into TryGetValue without receiving that nasty warning.

> d.TryGetValue(2, v);;

val it : bool = true

> !v;;

val it : string = "F#"

That's much better.

At this point, many of you are probably thinking, "Wait a minute! Wasn't this article supposed to be about tuples? What's all this mutable-variable-output-parameter stuff?" Don't worry. There's a method to my madness. Are you ready?

Consider what happens if we call TryGetValue without specifying a variable for the output parameter:

> let res = d.TryGetValue(3);;

val res : bool * string

> res;;

val it : bool * string = (true, "Dictionary")

Did you catch that? When calling a function containing output parameters in F#, you don't have to specify variables for them. The F# compiler will automatically consolidate the function's result and output parameters into a tuple (in this case, a pair). Awesome! If you were paying attention last time, you've probably already realized that we can bind the TryGetValue call to a pattern that extracts the values from the resulting pair.

> let res, v = d.TryGetValue(2);;

val res : bool
val v : string

> res;;

val it : bool = true

> v;;

val it : string = "F#"

Now, we can easily query our dictionary using an invalid key without an exception being thrown. Best of all, we don't have to declare an awkward mutable variable to store the value. What takes two lines of code in C# consumes just one in F#.

> let res, v = d.TryGetValue(4);;

val res : bool
val v : string

> res;;

val it : bool = false

It is the attention to detail that makes it a joy to code with F#. This is just one example of how F# can consume .NET framework classes in ways more elegant than even C#, the lingua franca of the .NET universe!

I haven't decided what the next article will cover yet. Are there any requests? Feel free to email them to dustin AT diditwith.net.

1The #light directive in the first line of the code sample enables the F# lightweight syntax. We'll look closer at this in a future article.
2This might be frustrating to users of the System.Collections.Hashtable class from .NET Framework 1.0. Unlike Dictionary, Hashtable returns null when a key isn't found rather than throwing an exception. The reason for this behavior difference is detailed here.
3Normally, I would consider the use of output parameters to be a code smell. However, TryGetValue is an example of a scenario where an output parameter is justified.
4We'll be looking more deeply into reference cells in a future article.

posted on Tuesday, January 29, 2008 11:30:29 AM (Pacific Standard Time, UTC-08:00)  #    Comments [3]

kick it on DotNetKicks.com